mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
Add x509 Client Auth to MongoDB Database Plugin (#8329)
* Mark deprecated plugins as deprecated * Add redaction capability to database plugins * Add x509 client auth * Update vendored files * Add integration test for x509 client auth * Remove redaction logic pending further discussion * Update vendored files * Minor updates from code review * Updated docs with x509 client auth * Roles are required * Disable x509 test because it doesn't work in CircleCI * Add timeouts for container lifetime
This commit is contained in:
@@ -2,6 +2,8 @@ package mongodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -23,8 +25,12 @@ import (
|
||||
type mongoDBConnectionProducer struct {
|
||||
ConnectionURL string `json:"connection_url" structs:"connection_url" mapstructure:"connection_url"`
|
||||
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"`
|
||||
|
||||
Username string `json:"username" structs:"username" mapstructure:"username"`
|
||||
Password string `json:"password" structs:"password" mapstructure:"password"`
|
||||
|
||||
TLSCertificateKeyData []byte `json:"tls_certificate_key" structs:"-" mapstructure:"tls_certificate_key"`
|
||||
TLSCAData []byte `json:"tls_ca" structs:"-" mapstructure:"tls_ca"`
|
||||
|
||||
Initialized bool
|
||||
RawConfig map[string]interface{}
|
||||
@@ -64,58 +70,19 @@ func (c *mongoDBConnectionProducer) Init(ctx context.Context, conf map[string]in
|
||||
return nil, fmt.Errorf("connection_url cannot be empty")
|
||||
}
|
||||
|
||||
c.ConnectionURL = dbutil.QueryHelper(c.ConnectionURL, map[string]string{
|
||||
"username": c.Username,
|
||||
"password": c.Password,
|
||||
})
|
||||
|
||||
if c.WriteConcern != "" {
|
||||
input := c.WriteConcern
|
||||
|
||||
// Try to base64 decode the input. If successful, consider the decoded
|
||||
// value as input.
|
||||
inputBytes, err := base64.StdEncoding.DecodeString(input)
|
||||
if err == nil {
|
||||
input = string(inputBytes)
|
||||
}
|
||||
|
||||
concern := &writeConcern{}
|
||||
err = json.Unmarshal([]byte(input), concern)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("error unmarshalling write_concern: {{err}}", err)
|
||||
}
|
||||
|
||||
// Translate write concern to mongo options
|
||||
var w writeconcern.Option
|
||||
switch {
|
||||
case concern.W != 0:
|
||||
w = writeconcern.W(concern.W)
|
||||
case concern.WMode != "":
|
||||
w = writeconcern.WTagSet(concern.WMode)
|
||||
default:
|
||||
w = writeconcern.WMajority()
|
||||
}
|
||||
|
||||
var j writeconcern.Option
|
||||
switch {
|
||||
case concern.FSync:
|
||||
j = writeconcern.J(concern.FSync)
|
||||
case concern.J:
|
||||
j = writeconcern.J(concern.J)
|
||||
default:
|
||||
j = writeconcern.J(false)
|
||||
}
|
||||
|
||||
writeConcern := writeconcern.New(
|
||||
w,
|
||||
j,
|
||||
writeconcern.WTimeout(time.Duration(concern.WTimeout)*time.Millisecond))
|
||||
|
||||
c.clientOptions = &options.ClientOptions{
|
||||
WriteConcern: writeConcern,
|
||||
}
|
||||
writeOpts, err := c.getWriteConcern()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authOpts, err := c.getTLSAuth()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.ConnectionURL = c.getConnectionURL()
|
||||
c.clientOptions = options.MergeClientOptions(writeOpts, authOpts)
|
||||
|
||||
// Set initialized to true at this point since all fields are set,
|
||||
// and the connection can be established at a later time.
|
||||
c.Initialized = true
|
||||
@@ -157,7 +124,8 @@ func (c *mongoDBConnectionProducer) Connection(ctx context.Context) (interface{}
|
||||
c.clientOptions.SetConnectTimeout(1 * time.Minute)
|
||||
|
||||
var err error
|
||||
c.client, err = mongo.Connect(ctx, c.clientOptions.ApplyURI(c.ConnectionURL))
|
||||
opts := c.clientOptions.ApplyURI(c.ConnectionURL)
|
||||
c.client, err = mongo.Connect(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -187,3 +155,98 @@ func (c *mongoDBConnectionProducer) secretValues() map[string]interface{} {
|
||||
c.Password: "[password]",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *mongoDBConnectionProducer) getConnectionURL() (connURL string) {
|
||||
connURL = dbutil.QueryHelper(c.ConnectionURL, map[string]string{
|
||||
"username": c.Username,
|
||||
"password": c.Password,
|
||||
})
|
||||
return connURL
|
||||
}
|
||||
|
||||
func (c *mongoDBConnectionProducer) getWriteConcern() (opts *options.ClientOptions, err error) {
|
||||
if c.WriteConcern == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
input := c.WriteConcern
|
||||
|
||||
// Try to base64 decode the input. If successful, consider the decoded
|
||||
// value as input.
|
||||
inputBytes, err := base64.StdEncoding.DecodeString(input)
|
||||
if err == nil {
|
||||
input = string(inputBytes)
|
||||
}
|
||||
|
||||
concern := &writeConcern{}
|
||||
err = json.Unmarshal([]byte(input), concern)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("error unmarshalling write_concern: {{err}}", err)
|
||||
}
|
||||
|
||||
// Translate write concern to mongo options
|
||||
var w writeconcern.Option
|
||||
switch {
|
||||
case concern.W != 0:
|
||||
w = writeconcern.W(concern.W)
|
||||
case concern.WMode != "":
|
||||
w = writeconcern.WTagSet(concern.WMode)
|
||||
default:
|
||||
w = writeconcern.WMajority()
|
||||
}
|
||||
|
||||
var j writeconcern.Option
|
||||
switch {
|
||||
case concern.FSync:
|
||||
j = writeconcern.J(concern.FSync)
|
||||
case concern.J:
|
||||
j = writeconcern.J(concern.J)
|
||||
default:
|
||||
j = writeconcern.J(false)
|
||||
}
|
||||
|
||||
writeConcern := writeconcern.New(
|
||||
w,
|
||||
j,
|
||||
writeconcern.WTimeout(time.Duration(concern.WTimeout)*time.Millisecond))
|
||||
|
||||
opts = options.Client()
|
||||
opts.SetWriteConcern(writeConcern)
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func (c *mongoDBConnectionProducer) getTLSAuth() (opts *options.ClientOptions, err error) {
|
||||
if len(c.TLSCAData) == 0 && len(c.TLSCertificateKeyData) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
opts = options.Client()
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
|
||||
if len(c.TLSCAData) > 0 {
|
||||
tlsConfig.RootCAs = x509.NewCertPool()
|
||||
|
||||
ok := tlsConfig.RootCAs.AppendCertsFromPEM(c.TLSCAData)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to append CA to client options")
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.TLSCertificateKeyData) > 0 {
|
||||
certificate, err := tls.X509KeyPair(c.TLSCertificateKeyData, c.TLSCertificateKeyData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load tls_certificate_key_data: %w", err)
|
||||
}
|
||||
|
||||
opts.SetAuth(options.Credential{
|
||||
AuthMechanism: "MONGODB-X509",
|
||||
Username: c.Username,
|
||||
})
|
||||
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, certificate)
|
||||
}
|
||||
|
||||
opts.SetTLSConfig(tlsConfig)
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user