140 lines
4.3 KiB
Go
140 lines
4.3 KiB
Go
package pki
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/vault/logical"
|
|
"github.com/hashicorp/vault/logical/certutil"
|
|
"github.com/hashicorp/vault/logical/framework"
|
|
)
|
|
|
|
func pathConfigCA(b *backend) *framework.Path {
|
|
return &framework.Path{
|
|
Pattern: "config/ca",
|
|
Fields: map[string]*framework.FieldSchema{
|
|
"pem_bundle": &framework.FieldSchema{
|
|
Type: framework.TypeString,
|
|
Description: "PEM-format, concatenated unencrypted secret key and certificate",
|
|
},
|
|
},
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
logical.WriteOperation: b.pathCAWrite,
|
|
},
|
|
|
|
HelpSynopsis: pathConfigCAHelpSyn,
|
|
HelpDescription: pathConfigCAHelpDesc,
|
|
}
|
|
}
|
|
|
|
func (b *backend) pathCAWrite(
|
|
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
|
pemBundle := d.Get("pem_bundle").(string)
|
|
|
|
if len(pemBundle) == 0 {
|
|
return logical.ErrorResponse("Empty PEM bundle"), nil
|
|
}
|
|
|
|
pemBytes := []byte(pemBundle)
|
|
var pemBlock *pem.Block
|
|
rawBundle := &certutil.RawCertBundle{}
|
|
|
|
for {
|
|
pemBlock, pemBytes = pem.Decode(pemBytes)
|
|
if pemBlock == nil {
|
|
return logical.ErrorResponse("No PEM data found"), nil
|
|
}
|
|
|
|
if _, err := x509.ParseECPrivateKey(pemBlock.Bytes); err == nil {
|
|
if rawBundle.PrivateKeyType != certutil.UnknownPrivateKeyType {
|
|
return logical.ErrorResponse("More than one private key given; provide only one private key in the bundle"), nil
|
|
}
|
|
rawBundle.PrivateKeyType = certutil.ECPrivateKeyType
|
|
rawBundle.PrivateKeyBytes = pemBlock.Bytes
|
|
// TODO?: CRLs can only be generated with RSA keys right now, in the
|
|
// Go standard library. The plubming is here to support non-RSA keys
|
|
// if the library gets support
|
|
return logical.ErrorResponse("Only RSA keys are supported at this time due to limitations in the Go standard library"), nil
|
|
} else if _, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes); err == nil {
|
|
|
|
if rawBundle.PrivateKeyType != certutil.UnknownPrivateKeyType {
|
|
return logical.ErrorResponse("More than one private key given; provide only one private key in the bundle"), nil
|
|
}
|
|
rawBundle.PrivateKeyType = certutil.RSAPrivateKeyType
|
|
rawBundle.PrivateKeyBytes = pemBlock.Bytes
|
|
} else if certificates, err := x509.ParseCertificates(pemBlock.Bytes); err == nil {
|
|
switch len(certificates) {
|
|
case 0:
|
|
return logical.ErrorResponse("No certificates found in the bundle"), nil
|
|
case 1:
|
|
cert := certificates[0]
|
|
if !cert.IsCA {
|
|
return logical.ErrorResponse("The given certificate is not marked for CA use and cannot be used with this backend"), nil
|
|
}
|
|
rawBundle.CertificateBytes = pemBlock.Bytes
|
|
rawBundle.SerialNumber = cert.SerialNumber
|
|
default:
|
|
return logical.ErrorResponse("More than one certificate given; provide only one certificate in the bundle"), nil
|
|
}
|
|
}
|
|
|
|
if len(pemBytes) == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case rawBundle.PrivateKeyType == certutil.UnknownPrivateKeyType:
|
|
return logical.ErrorResponse("Unable to figure out the private key type; must be RSA or EC"), nil
|
|
case len(rawBundle.PrivateKeyBytes) == 0:
|
|
return logical.ErrorResponse("Unable to decode the private key from the bundle"), nil
|
|
case len(rawBundle.CertificateBytes) == 0:
|
|
return logical.ErrorResponse("Unable to decode the certificate from the bundle"), nil
|
|
}
|
|
|
|
cb, err := rawBundle.ToCertBundle()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error converting raw values into cert bundle: %s", err)
|
|
}
|
|
entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = req.Storage.Put(entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// For ease of later use, also store just the certificate at a known
|
|
// location, plus a blank CRL
|
|
entry.Key = "ca"
|
|
entry.Value = rawBundle.CertificateBytes
|
|
err = req.Storage.Put(entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entry.Key = "crl"
|
|
entry.Value = []byte{}
|
|
err = req.Storage.Put(entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
const pathConfigCAHelpSyn = `
|
|
Configure the CA certificate and private key used for generated credentials.
|
|
`
|
|
|
|
const pathConfigCAHelpDesc = `
|
|
This configures the CA information used for credentials
|
|
generated by this backend. This must be a PEM-format, concatenated
|
|
unencrypted secret key and certificate.
|
|
|
|
For security reasons, you can only view the certificate when reading this endpoint.
|
|
`
|