Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to specify TLS config for mongodb database plugin #5632

Closed
wants to merge 7 commits into from
41 changes: 36 additions & 5 deletions plugins/database/mongodb/connection_producer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package mongodb
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
Expand All @@ -18,7 +19,6 @@ import (
"github.com/hashicorp/vault/plugins/helper/database/connutil"
"github.com/hashicorp/vault/plugins/helper/database/dbutil"
"github.com/mitchellh/mapstructure"

"gopkg.in/mgo.v2"
)

Expand All @@ -29,6 +29,10 @@ type mongoDBConnectionProducer struct {
WriteConcern string `json:"write_concern" structs:"write_concern" mapstructure:"write_concern"`
Username string `json:"username" structs:"username" mapstructure:"username"`
Password string `json:"password" structs:"password" mapstructure:"password"`
TLSCert string `json:"tls_cert" structs:"tls_cert" mapstructure:"tls_cert"`
TLSKey string `json:"tls_key" structs:"tls_key" mapstructure:"tls_key"`
TLSCA string `json:"tls_ca" structs:"tls_ca" mapstructure:"tls_ca"`
TLSVerify string `json:"tls_verify" structs:"tls_verify" mapstructure:"tls_verify"`

Initialized bool
RawConfig map[string]interface{}
Expand Down Expand Up @@ -119,12 +123,11 @@ func (c *mongoDBConnectionProducer) Connection(_ context.Context) (interface{},
c.session.Close()
}

dialInfo, err := parseMongoURL(c.ConnectionURL)
dialInfo, err := parseMongoURL(c.ConnectionURL, c.TLSCert, c.TLSKey, c.TLSCA, c.TLSVerify)
if err != nil {
return nil, err
}

c.session, err = mgo.DialWithInfo(dialInfo)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -153,7 +156,7 @@ func (c *mongoDBConnectionProducer) Close() error {
return nil
}

func parseMongoURL(rawURL string) (*mgo.DialInfo, error) {
func parseMongoURL(rawURL, tlsCert, tlsKey, tlsCA, tlsVerify string) (*mgo.DialInfo, error) {
url, err := url.Parse(rawURL)
if err != nil {
return nil, err
Expand Down Expand Up @@ -201,7 +204,35 @@ func parseMongoURL(rawURL string) (*mgo.DialInfo, error) {
}
if ssl {
info.DialServer = func(addr *mgo.ServerAddr) (net.Conn, error) {
return tls.Dial("tcp", addr.String(), &tls.Config{})
tlsConfig := &tls.Config{}
if tlsCert != "" && tlsKey != "" && tlsCA != "" {
caCerts := x509.NewCertPool()
ok := caCerts.AppendCertsFromPEM([]byte(tlsCA))
if !ok {
return nil, errors.New("failed to parse tls_ca value")
}
clientCert, err := tls.X509KeyPair([]byte(tlsCert), []byte(tlsKey))
if err != nil {
return nil, errors.New("bad value for tls_cert or tls_key")
}
clientCert.Leaf, err = x509.ParseCertificate(clientCert.Certificate[0])
if err != nil {
return nil, errors.New("failed to parse tls_cert or tls_key")
}
insecureSkipVerify := false
if tlsVerify != "" {
insecureSkipVerify, err = strconv.ParseBool(tlsVerify)
if err != nil {
return nil, errors.New("bad value for tls verify: " + tlsVerify)
}
}
tlsConfig = &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: caCerts,
InsecureSkipVerify: insecureSkipVerify,
}
}
return tls.Dial("tcp", addr.String(), tlsConfig)
}
}
case "connect":
Expand Down
4 changes: 2 additions & 2 deletions plugins/database/mongodb/mongodb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func prepareMongoDBTestContainer(t *testing.T) (cleanup func(), retURL string) {
// exponential backoff-retry
if err = pool.Retry(func() error {
var err error
dialInfo, err := parseMongoURL(retURL)
dialInfo, err := parseMongoURL(retURL, "", "", "", "")
if err != nil {
return err
}
Expand Down Expand Up @@ -203,7 +203,7 @@ func TestMongoDB_RevokeUser(t *testing.T) {

func testCredsExist(t testing.TB, connURL, username, password string) error {
connURL = strings.Replace(connURL, "mongodb://", fmt.Sprintf("mongodb://%s:%s@", username, password), 1)
dialInfo, err := parseMongoURL(connURL)
dialInfo, err := parseMongoURL(connURL, "", "", "", "")
if err != nil {
return err
}
Expand Down
16 changes: 11 additions & 5 deletions website/source/api/secret/databases/mongodb.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,19 @@ has a number of parameters to further configure a connection.
### Parameters

- `connection_url` `(string: <required>)` – Specifies the MongoDB standard
connection string (URI). This field can be templated and supports passing the
username and password parameters in the following format {{field_name}}. A
connection string (URI). This field can be templated and supports passing the
username and password parameters in the following format {{field_name}}. A
templated connection URL is required when using root credential rotation.
- `write_concern` `(string: "")` - Specifies the MongoDB [write
concern][mongodb-write-concern]. This is set for the entirety of the session,
maintained for the lifecycle of the plugin process. Must be a serialized JSON
object, or a base64-encoded serialized JSON object. The JSON payload values
map to the values in the [Safe][mgo-safe] struct from the mgo driver.
- `username` `(string: "")` - The root credential username used in the connection URL.
- `password` `(string: "")` - The root credential password used in the connection URL.
- `username` `(string: "")` - The root credential username used in the connection URL.
- `password` `(string: "")` - The root credential password used in the connection URL.
- `tls_cert` `(string: "")` - PEM-encoded client certificate (optional).
- `tls_key` `(string: "")` - PEM-encoded private key (optional).
- `tls_ca` `(string: "")` - PEM-encoded CA certificate to verify the server against (optional).

### Sample Payload

Expand All @@ -46,7 +49,10 @@ has a number of parameters to further configure a connection.
"connection_url": "mongodb://{{username}}:{{password}}@mongodb.acme.com:27017/admin?ssl=true",
"write_concern": "{ \"wmode\": \"majority\", \"wtimeout\": 5000 }",
"username": "admin",
"password": "Password!"
"password": "Password!",
"tls_cert": "<PEM encoded string of certificate>",
"tls_key": "<PEM encoded string of key>",
"tls_ca": "<PEM encoded string of CA>"
}
```

Expand Down
5 changes: 4 additions & 1 deletion website/source/docs/secrets/databases/mongodb.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ more information about setting up the database secrets engine.
allowed_roles="my-role" \
connection_url="mongodb://{{username}}:{{password}}@mongodb.acme.com:27017/admin?ssl=true" \
username="admin" \
password="Password!"
password="Password!" \
tls_cert="PEM-encoded certificate string" \
tls_key="PEM encoded private key" \
tls_ca="PEM encoded CA"
```

1. Configure a role that maps a name in Vault to an SQL statement to execute to
Expand Down