246 lines
7.9 KiB
Go
246 lines
7.9 KiB
Go
package cassandra
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/vault/sdk/framework"
|
|
"github.com/hashicorp/vault/sdk/helper/certutil"
|
|
"github.com/hashicorp/vault/sdk/helper/tlsutil"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
func pathConfigConnection(b *backend) *framework.Path {
|
|
return &framework.Path{
|
|
Pattern: "config/connection",
|
|
Fields: map[string]*framework.FieldSchema{
|
|
"hosts": {
|
|
Type: framework.TypeString,
|
|
Description: "Comma-separated list of hosts",
|
|
},
|
|
|
|
"username": {
|
|
Type: framework.TypeString,
|
|
Description: "The username to use for connecting to the cluster",
|
|
},
|
|
|
|
"password": {
|
|
Type: framework.TypeString,
|
|
Description: "The password to use for connecting to the cluster",
|
|
},
|
|
|
|
"tls": {
|
|
Type: framework.TypeBool,
|
|
Description: `Whether to use TLS. If pem_bundle or pem_json are
|
|
set, this is automatically set to true`,
|
|
},
|
|
|
|
"insecure_tls": {
|
|
Type: framework.TypeBool,
|
|
Description: `Whether to use TLS but skip verification; has no
|
|
effect if a CA certificate is provided`,
|
|
},
|
|
|
|
// TLS 1.3 is not supported as this engine is deprecated. Please switch to the Cassandra database secrets engine
|
|
"tls_min_version": {
|
|
Type: framework.TypeString,
|
|
Default: "tls12",
|
|
Description: "Minimum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'",
|
|
},
|
|
|
|
"pem_bundle": {
|
|
Type: framework.TypeString,
|
|
Description: `PEM-format, concatenated unencrypted secret key
|
|
and certificate, with optional CA certificate`,
|
|
},
|
|
|
|
"pem_json": {
|
|
Type: framework.TypeString,
|
|
Description: `JSON containing a PEM-format, unencrypted secret
|
|
key and certificate, with optional CA certificate.
|
|
The JSON output of a certificate issued with the PKI
|
|
backend can be directly passed into this parameter.
|
|
If both this and "pem_bundle" are specified, this will
|
|
take precedence.`,
|
|
},
|
|
|
|
"protocol_version": {
|
|
Type: framework.TypeInt,
|
|
Description: `The protocol version to use. Defaults to 2.`,
|
|
},
|
|
|
|
"connect_timeout": {
|
|
Type: framework.TypeDurationSecond,
|
|
Default: 5,
|
|
Description: `The connection timeout to use. Defaults to 5.`,
|
|
},
|
|
},
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
logical.ReadOperation: b.pathConnectionRead,
|
|
logical.UpdateOperation: b.pathConnectionWrite,
|
|
},
|
|
|
|
HelpSynopsis: pathConfigConnectionHelpSyn,
|
|
HelpDescription: pathConfigConnectionHelpDesc,
|
|
}
|
|
}
|
|
|
|
func (b *backend) pathConnectionRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
entry, err := req.Storage.Get(ctx, "config/connection")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if entry == nil {
|
|
return logical.ErrorResponse(fmt.Sprintf("Configure the DB connection with config/connection first")), nil
|
|
}
|
|
|
|
config := &sessionConfig{}
|
|
if err := entry.DecodeJSON(config); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp := &logical.Response{
|
|
Data: map[string]interface{}{
|
|
"hosts": config.Hosts,
|
|
"username": config.Username,
|
|
"tls": config.TLS,
|
|
"insecure_tls": config.InsecureTLS,
|
|
"certificate": config.Certificate,
|
|
"issuing_ca": config.IssuingCA,
|
|
"protocol_version": config.ProtocolVersion,
|
|
"connect_timeout": config.ConnectTimeout,
|
|
"tls_min_version": config.TLSMinVersion,
|
|
},
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func (b *backend) pathConnectionWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
hosts := data.Get("hosts").(string)
|
|
username := data.Get("username").(string)
|
|
password := data.Get("password").(string)
|
|
|
|
switch {
|
|
case len(hosts) == 0:
|
|
return logical.ErrorResponse("Hosts cannot be empty"), nil
|
|
case len(username) == 0:
|
|
return logical.ErrorResponse("Username cannot be empty"), nil
|
|
case len(password) == 0:
|
|
return logical.ErrorResponse("Password cannot be empty"), nil
|
|
}
|
|
|
|
config := &sessionConfig{
|
|
Hosts: hosts,
|
|
Username: username,
|
|
Password: password,
|
|
TLS: data.Get("tls").(bool),
|
|
InsecureTLS: data.Get("insecure_tls").(bool),
|
|
ProtocolVersion: data.Get("protocol_version").(int),
|
|
ConnectTimeout: data.Get("connect_timeout").(int),
|
|
}
|
|
|
|
config.TLSMinVersion = data.Get("tls_min_version").(string)
|
|
if config.TLSMinVersion == "" {
|
|
return logical.ErrorResponse("failed to get 'tls_min_version' value"), nil
|
|
}
|
|
|
|
var ok bool
|
|
_, ok = tlsutil.TLSLookup[config.TLSMinVersion]
|
|
if !ok {
|
|
return logical.ErrorResponse("invalid 'tls_min_version'"), nil
|
|
}
|
|
|
|
if config.InsecureTLS {
|
|
config.TLS = true
|
|
}
|
|
|
|
pemBundle := data.Get("pem_bundle").(string)
|
|
pemJSON := data.Get("pem_json").(string)
|
|
|
|
var certBundle *certutil.CertBundle
|
|
var parsedCertBundle *certutil.ParsedCertBundle
|
|
var err error
|
|
|
|
switch {
|
|
case len(pemJSON) != 0:
|
|
parsedCertBundle, err = certutil.ParsePKIJSON([]byte(pemJSON))
|
|
if err != nil {
|
|
return logical.ErrorResponse(fmt.Sprintf("Could not parse given JSON; it must be in the format of the output of the PKI backend certificate issuing command: %s", err)), nil
|
|
}
|
|
certBundle, err = parsedCertBundle.ToCertBundle()
|
|
if err != nil {
|
|
return logical.ErrorResponse(fmt.Sprintf("Error marshaling PEM information: %s", err)), nil
|
|
}
|
|
config.Certificate = certBundle.Certificate
|
|
config.PrivateKey = certBundle.PrivateKey
|
|
config.IssuingCA = certBundle.IssuingCA
|
|
config.TLS = true
|
|
|
|
case len(pemBundle) != 0:
|
|
parsedCertBundle, err = certutil.ParsePEMBundle(pemBundle)
|
|
if err != nil {
|
|
return logical.ErrorResponse(fmt.Sprintf("Error parsing the given PEM information: %s", err)), nil
|
|
}
|
|
certBundle, err = parsedCertBundle.ToCertBundle()
|
|
if err != nil {
|
|
return logical.ErrorResponse(fmt.Sprintf("Error marshaling PEM information: %s", err)), nil
|
|
}
|
|
config.Certificate = certBundle.Certificate
|
|
config.PrivateKey = certBundle.PrivateKey
|
|
config.IssuingCA = certBundle.IssuingCA
|
|
config.TLS = true
|
|
}
|
|
|
|
session, err := createSession(config, req.Storage)
|
|
if err != nil {
|
|
return logical.ErrorResponse(err.Error()), nil
|
|
}
|
|
|
|
// Store it
|
|
entry, err := logical.StorageEntryJSON("config/connection", config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := req.Storage.Put(ctx, entry); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Reset the DB connection
|
|
b.ResetDB(session)
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
const pathConfigConnectionHelpSyn = `
|
|
Configure the connection information to talk to Cassandra.
|
|
`
|
|
|
|
const pathConfigConnectionHelpDesc = `
|
|
This path configures the connection information used to connect to Cassandra.
|
|
|
|
"hosts" is a comma-delimited list of hostnames in the Cassandra cluster.
|
|
|
|
"username" and "password" are self-explanatory, although the given user
|
|
must have superuser access within Cassandra. Note that since this backend
|
|
issues username/password credentials, Cassandra must be configured to use
|
|
PasswordAuthenticator or a similar backend for its authentication. If you wish
|
|
to have no authorization in Cassandra and want to use TLS client certificates,
|
|
see the PKI backend.
|
|
|
|
TLS works as follows:
|
|
|
|
* If "tls" is set to true, the connection will use TLS; this happens automatically if "pem_bundle", "pem_json", or "insecure_tls" is set
|
|
|
|
* If "insecure_tls" is set to true, the connection will not perform verification of the server certificate; this also sets "tls" to true
|
|
|
|
* If only "issuing_ca" is set in "pem_json", or the only certificate in "pem_bundle" is a CA certificate, the given CA certificate will be used for server certificate verification; otherwise the system CA certificates will be used
|
|
|
|
* If "certificate" and "private_key" are set in "pem_bundle" or "pem_json", client auth will be turned on for the connection
|
|
|
|
"pem_bundle" should be a PEM-concatenated bundle of a private key + client certificate, an issuing CA certificate, or both. "pem_json" should contain the same information; for convenience, the JSON format is the same as that output by the issue command from the PKI backend.
|
|
|
|
When configuring the connection information, the backend will verify its
|
|
validity.
|
|
`
|