Merge branch 'master' into ui-toolbar

This commit is contained in:
Joshua Ogle 2019-05-09 10:13:35 -06:00 committed by GitHub
commit 81c3cbdbfa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 858 additions and 824 deletions

View file

@ -12,7 +12,16 @@ import (
squarejwt "gopkg.in/square/go-jose.v2/jwt"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
)
var (
// PluginMetadataModeEnv is an ENV name used to disable TLS communication
// to bootstrap mounting plugins.
PluginMetadataModeEnv = "VAULT_PLUGIN_METADATA_MODE"
// PluginUnwrapTokenEnv is the ENV name used to pass unwrap tokens to the
// plugin.
PluginUnwrapTokenEnv = "VAULT_UNWRAP_TOKEN"
)
// PluginAPIClientMeta is a helper that plugins can use to configure TLS connections
@ -61,12 +70,12 @@ func (f *PluginAPIClientMeta) GetTLSConfig() *TLSConfig {
// VaultPluginTLSProvider is run inside a plugin and retrieves the response
// wrapped TLS certificate from vault. It returns a configured TLS Config.
func VaultPluginTLSProvider(apiTLSConfig *TLSConfig) func() (*tls.Config, error) {
if os.Getenv(pluginutil.PluginMetadataModeEnv) == "true" {
if os.Getenv(PluginMetadataModeEnv) == "true" {
return nil
}
return func() (*tls.Config, error) {
unwrapToken := os.Getenv(pluginutil.PluginUnwrapTokenEnv)
unwrapToken := os.Getenv(PluginUnwrapTokenEnv)
parsedJWT, err := squarejwt.ParseSigned(unwrapToken)
if err != nil {

View file

@ -432,7 +432,7 @@ func checkCertsAndPrivateKey(keyType string, key crypto.Signer, usage x509.KeyUs
}
func generateURLSteps(t *testing.T, caCert, caKey string, intdata, reqdata map[string]interface{}) []logicaltest.TestStep {
expected := urlEntries{
expected := certutil.URLEntries{
IssuingCertificates: []string{
"http://example.com/ca1",
"http://example.com/ca2",
@ -499,7 +499,7 @@ func generateURLSteps(t *testing.T, caCert, caKey string, intdata, reqdata map[s
if resp.Data == nil {
return fmt.Errorf("no data returned")
}
var entries urlEntries
var entries certutil.URLEntries
err := mapstructure.Decode(resp.Data, &entries)
if err != nil {
return err

View file

@ -4,6 +4,7 @@ import (
"time"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/certutil"
"github.com/hashicorp/vault/sdk/logical"
)
@ -53,7 +54,9 @@ func (b *backend) getGenerationParams(
return
}
errorResp = validateKeyTypeLength(role.KeyType, role.KeyBits)
if err := certutil.ValidateKeyTypeLength(role.KeyType, role.KeyBits); err != nil {
errorResp = logical.ErrorResponse(err.Error())
}
return
}

File diff suppressed because it is too large Load diff

View file

@ -139,20 +139,20 @@ func TestPki_MultipleOUs(t *testing.T) {
"ttl": 3600,
},
}
input := &dataBundle{
input := &inputBundle{
apiData: apiData,
role: &roleEntry{
MaxTTL: 3600,
OU: []string{"Z", "E", "V"},
},
}
err := generateCreationBundle(&b, input)
cb, err := generateCreationBundle(&b, input, nil, nil)
if err != nil {
t.Fatalf("Error: %v", err)
}
expected := []string{"Z", "E", "V"}
actual := input.params.Subject.OrganizationalUnit
actual := cb.Params.Subject.OrganizationalUnit
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("Expected %v, got %v", expected, actual)

View file

@ -7,6 +7,7 @@ import (
"github.com/asaskevich/govalidator"
"github.com/fatih/structs"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/certutil"
"github.com/hashicorp/vault/sdk/logical"
)
@ -53,7 +54,7 @@ func validateURLs(urls []string) string {
return ""
}
func getURLs(ctx context.Context, req *logical.Request) (*urlEntries, error) {
func getURLs(ctx context.Context, req *logical.Request) (*certutil.URLEntries, error) {
entry, err := req.Storage.Get(ctx, "urls")
if err != nil {
return nil, err
@ -62,7 +63,7 @@ func getURLs(ctx context.Context, req *logical.Request) (*urlEntries, error) {
return nil, nil
}
var entries urlEntries
var entries certutil.URLEntries
if err := entry.DecodeJSON(&entries); err != nil {
return nil, err
}
@ -70,7 +71,7 @@ func getURLs(ctx context.Context, req *logical.Request) (*urlEntries, error) {
return &entries, nil
}
func writeURLs(ctx context.Context, req *logical.Request, entries *urlEntries) error {
func writeURLs(ctx context.Context, req *logical.Request, entries *certutil.URLEntries) error {
entry, err := logical.StorageEntryJSON("urls", entries)
if err != nil {
return err
@ -109,7 +110,7 @@ func (b *backend) pathWriteURL(ctx context.Context, req *logical.Request, data *
return nil, err
}
if entries == nil {
entries = &urlEntries{
entries = &certutil.URLEntries{
IssuingCertificates: []string{},
CRLDistributionPoints: []string{},
OCSPServers: []string{},
@ -141,12 +142,6 @@ func (b *backend) pathWriteURL(ctx context.Context, req *logical.Request, data *
return nil, writeURLs(ctx, req, entries)
}
type urlEntries struct {
IssuingCertificates []string `json:"issuing_certificates" structs:"issuing_certificates" mapstructure:"issuing_certificates"`
CRLDistributionPoints []string `json:"crl_distribution_points" structs:"crl_distribution_points" mapstructure:"crl_distribution_points"`
OCSPServers []string `json:"ocsp_servers" structs:"ocsp_servers" mapstructure:"ocsp_servers"`
}
const pathConfigURLsHelpSyn = `
Set the URLs for the issuing CA, CRL distribution points, and OCSP servers.
`

View file

@ -71,7 +71,7 @@ func (b *backend) pathGenerateIntermediate(ctx context.Context, req *logical.Req
}
var resp *logical.Response
input := &dataBundle{
input := &inputBundle{
role: role,
req: req,
apiData: data,

View file

@ -205,18 +205,17 @@ func (b *backend) pathIssueSignCert(ctx context.Context, req *logical.Request, d
"error fetching CA certificate: %s", caErr)}
}
input := &dataBundle{
req: req,
apiData: data,
role: role,
signingBundle: signingBundle,
input := &inputBundle{
req: req,
apiData: data,
role: role,
}
var parsedBundle *certutil.ParsedCertBundle
var err error
if useCSR {
parsedBundle, err = signCert(b, input, false, useCSRValues)
parsedBundle, err = signCert(b, input, signingBundle, false, useCSRValues)
} else {
parsedBundle, err = generateCert(ctx, b, input, false)
parsedBundle, err = generateCert(ctx, b, input, signingBundle, false)
}
if err != nil {
switch err.(type) {

View file

@ -9,6 +9,7 @@ import (
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/certutil"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/parseutil"
"github.com/hashicorp/vault/sdk/logical"
@ -552,13 +553,13 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data
), nil
}
if errResp := validateKeyTypeLength(entry.KeyType, entry.KeyBits); errResp != nil {
return errResp, nil
if err := certutil.ValidateKeyTypeLength(entry.KeyType, entry.KeyBits); err != nil {
return logical.ErrorResponse(err.Error()), nil
}
if len(entry.ExtKeyUsageOIDs) > 0 {
for _, oidstr := range entry.ExtKeyUsageOIDs {
_, err := stringToOid(oidstr)
_, err := certutil.StringToOid(oidstr)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf("%q could not be parsed as a valid oid for an extended key usage", oidstr)), nil
}
@ -567,7 +568,7 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data
if len(entry.PolicyIdentifiers) > 0 {
for _, oidstr := range entry.PolicyIdentifiers {
_, err := stringToOid(oidstr)
_, err := certutil.StringToOid(oidstr)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf("%q could not be parsed as a valid oid for a policy identifier", oidstr)), nil
}
@ -614,51 +615,51 @@ func parseKeyUsages(input []string) int {
return int(parsedKeyUsages)
}
func parseExtKeyUsages(role *roleEntry) certExtKeyUsage {
var parsedKeyUsages certExtKeyUsage
func parseExtKeyUsages(role *roleEntry) certutil.CertExtKeyUsage {
var parsedKeyUsages certutil.CertExtKeyUsage
if role.ServerFlag {
parsedKeyUsages |= serverAuthExtKeyUsage
parsedKeyUsages |= certutil.ServerAuthExtKeyUsage
}
if role.ClientFlag {
parsedKeyUsages |= clientAuthExtKeyUsage
parsedKeyUsages |= certutil.ClientAuthExtKeyUsage
}
if role.CodeSigningFlag {
parsedKeyUsages |= codeSigningExtKeyUsage
parsedKeyUsages |= certutil.CodeSigningExtKeyUsage
}
if role.EmailProtectionFlag {
parsedKeyUsages |= emailProtectionExtKeyUsage
parsedKeyUsages |= certutil.EmailProtectionExtKeyUsage
}
for _, k := range role.ExtKeyUsage {
switch strings.ToLower(strings.TrimSpace(k)) {
case "any":
parsedKeyUsages |= anyExtKeyUsage
parsedKeyUsages |= certutil.AnyExtKeyUsage
case "serverauth":
parsedKeyUsages |= serverAuthExtKeyUsage
parsedKeyUsages |= certutil.ServerAuthExtKeyUsage
case "clientauth":
parsedKeyUsages |= clientAuthExtKeyUsage
parsedKeyUsages |= certutil.ClientAuthExtKeyUsage
case "codesigning":
parsedKeyUsages |= codeSigningExtKeyUsage
parsedKeyUsages |= certutil.CodeSigningExtKeyUsage
case "emailprotection":
parsedKeyUsages |= emailProtectionExtKeyUsage
parsedKeyUsages |= certutil.EmailProtectionExtKeyUsage
case "ipsecendsystem":
parsedKeyUsages |= ipsecEndSystemExtKeyUsage
parsedKeyUsages |= certutil.IpsecEndSystemExtKeyUsage
case "ipsectunnel":
parsedKeyUsages |= ipsecTunnelExtKeyUsage
parsedKeyUsages |= certutil.IpsecTunnelExtKeyUsage
case "ipsecuser":
parsedKeyUsages |= ipsecUserExtKeyUsage
parsedKeyUsages |= certutil.IpsecUserExtKeyUsage
case "timestamping":
parsedKeyUsages |= timeStampingExtKeyUsage
parsedKeyUsages |= certutil.TimeStampingExtKeyUsage
case "ocspsigning":
parsedKeyUsages |= ocspSigningExtKeyUsage
parsedKeyUsages |= certutil.OcspSigningExtKeyUsage
case "microsoftservergatedcrypto":
parsedKeyUsages |= microsoftServerGatedCryptoExtKeyUsage
parsedKeyUsages |= certutil.MicrosoftServerGatedCryptoExtKeyUsage
case "netscapeservergatedcrypto":
parsedKeyUsages |= netscapeServerGatedCryptoExtKeyUsage
parsedKeyUsages |= certutil.NetscapeServerGatedCryptoExtKeyUsage
}
}

View file

@ -7,6 +7,7 @@ import (
"encoding/base64"
"encoding/pem"
"fmt"
"github.com/hashicorp/vault/sdk/helper/certutil"
"reflect"
"strings"
"time"
@ -139,12 +140,12 @@ func (b *backend) pathCAGenerateRoot(ctx context.Context, req *logical.Request,
role.MaxPathLength = &maxPathLength
}
input := &dataBundle{
input := &inputBundle{
req: req,
apiData: data,
role: role,
}
parsedBundle, err := generateCert(ctx, b, input, true)
parsedBundle, err := generateCert(ctx, b, input, nil, true)
if err != nil {
switch err.(type) {
case errutil.UserError:
@ -296,13 +297,12 @@ func (b *backend) pathCASignIntermediate(ctx context.Context, req *logical.Reque
role.MaxPathLength = &maxPathLength
}
input := &dataBundle{
req: req,
apiData: data,
signingBundle: signingBundle,
role: role,
input := &inputBundle{
req: req,
apiData: data,
role: role,
}
parsedBundle, err := signCert(b, input, true, useCSRValues)
parsedBundle, err := signCert(b, input, signingBundle, true, useCSRValues)
if err != nil {
switch err.(type) {
case errutil.UserError:
@ -420,7 +420,7 @@ func (b *backend) pathCASignSelfIssued(ctx context.Context, req *logical.Request
return nil, errwrap.Wrapf("error converting raw signing bundle to cert bundle: {{err}}", err)
}
urls := &urlEntries{}
urls := &certutil.URLEntries{}
if signingBundle.URLs != nil {
urls = signingBundle.URLs
}

View file

@ -168,7 +168,7 @@ func (s *StoragePacker) DeleteItem(_ context.Context, itemID string) error {
bucket.Items = append(bucket.Items[:foundIdx], bucket.Items[foundIdx+1:]...)
// Persist bucket entry only if there is an update
err = s.putBucket(&bucket)
err = s.PutBucket(&bucket)
if err != nil {
return err
}
@ -177,7 +177,7 @@ func (s *StoragePacker) DeleteItem(_ context.Context, itemID string) error {
return nil
}
func (s *StoragePacker) putBucket(bucket *Bucket) error {
func (s *StoragePacker) PutBucket(bucket *Bucket) error {
if bucket == nil {
return fmt.Errorf("nil bucket entry")
}
@ -298,7 +298,7 @@ func (s *StoragePacker) PutItem(_ context.Context, item *Item) error {
}
}
return s.putBucket(bucket)
return s.PutBucket(bucket)
}
// NewStoragePacker creates a new storage packer for a given view

View file

@ -9,16 +9,24 @@ import (
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"math/big"
"net"
"net/url"
"strconv"
"strings"
"time"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/helper/errutil"
"github.com/hashicorp/vault/sdk/helper/jsonutil"
"github.com/mitchellh/mapstructure"
"golang.org/x/crypto/cryptobyte"
cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
)
// GetHexFormatted returns the byte buffer formatted in hex with
@ -275,7 +283,7 @@ func ComparePublicKeys(key1Iface, key2Iface crypto.PublicKey) (bool, error) {
}
}
// PasrsePublicKeyPEM is used to parse RSA and ECDSA public keys from PEMs
// ParsePublicKeyPEM is used to parse RSA and ECDSA public keys from PEMs
func ParsePublicKeyPEM(data []byte) (interface{}, error) {
block, data := pem.Decode(data)
if block != nil {
@ -299,3 +307,500 @@ func ParsePublicKeyPEM(data []byte) (interface{}, error) {
return nil, errors.New("data does not contain any valid RSA or ECDSA public keys")
}
// addPolicyIdentifiers adds certificate policies extension
//
func AddPolicyIdentifiers(data *CreationBundle, certTemplate *x509.Certificate) {
for _, oidstr := range data.Params.PolicyIdentifiers {
oid, err := StringToOid(oidstr)
if err == nil {
certTemplate.PolicyIdentifiers = append(certTemplate.PolicyIdentifiers, oid)
}
}
}
// addExtKeyUsageOids adds custom extended key usage OIDs to certificate
func AddExtKeyUsageOids(data *CreationBundle, certTemplate *x509.Certificate) {
for _, oidstr := range data.Params.ExtKeyUsageOIDs {
oid, err := StringToOid(oidstr)
if err == nil {
certTemplate.UnknownExtKeyUsage = append(certTemplate.UnknownExtKeyUsage, oid)
}
}
}
func HandleOtherCSRSANs(in *x509.CertificateRequest, sans map[string][]string) error {
certTemplate := &x509.Certificate{
DNSNames: in.DNSNames,
IPAddresses: in.IPAddresses,
EmailAddresses: in.EmailAddresses,
URIs: in.URIs,
}
if err := HandleOtherSANs(certTemplate, sans); err != nil {
return err
}
if len(certTemplate.ExtraExtensions) > 0 {
for _, v := range certTemplate.ExtraExtensions {
in.ExtraExtensions = append(in.ExtraExtensions, v)
}
}
return nil
}
func HandleOtherSANs(in *x509.Certificate, sans map[string][]string) error {
// If other SANs is empty we return which causes normal Go stdlib parsing
// of the other SAN types
if len(sans) == 0 {
return nil
}
var rawValues []asn1.RawValue
// We need to generate an IMPLICIT sequence for compatibility with OpenSSL
// -- it's an open question what the default for RFC 5280 actually is, see
// https://github.com/openssl/openssl/issues/5091 -- so we have to use
// cryptobyte because using the asn1 package's marshaling always produces
// an EXPLICIT sequence. Note that asn1 is way too magical according to
// agl, and cryptobyte is modeled after the CBB/CBS bits that agl put into
// boringssl.
for oid, vals := range sans {
for _, val := range vals {
var b cryptobyte.Builder
oidStr, err := StringToOid(oid)
if err != nil {
return err
}
b.AddASN1ObjectIdentifier(oidStr)
b.AddASN1(cbasn1.Tag(0).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
b.AddASN1(cbasn1.UTF8String, func(b *cryptobyte.Builder) {
b.AddBytes([]byte(val))
})
})
m, err := b.Bytes()
if err != nil {
return err
}
rawValues = append(rawValues, asn1.RawValue{Tag: 0, Class: 2, IsCompound: true, Bytes: m})
}
}
// If other SANs is empty we return which causes normal Go stdlib parsing
// of the other SAN types
if len(rawValues) == 0 {
return nil
}
// Append any existing SANs, sans marshalling
rawValues = append(rawValues, marshalSANs(in.DNSNames, in.EmailAddresses, in.IPAddresses, in.URIs)...)
// Marshal and add to ExtraExtensions
ext := pkix.Extension{
// This is the defined OID for subjectAltName
Id: asn1.ObjectIdentifier{2, 5, 29, 17},
}
var err error
ext.Value, err = asn1.Marshal(rawValues)
if err != nil {
return err
}
in.ExtraExtensions = append(in.ExtraExtensions, ext)
return nil
}
// Note: Taken from the Go source code since it's not public, and used in the
// modified function below (which also uses these consts upstream)
const (
nameTypeEmail = 1
nameTypeDNS = 2
nameTypeURI = 6
nameTypeIP = 7
)
// Note: Taken from the Go source code since it's not public, plus changed to not marshal
// marshalSANs marshals a list of addresses into a the contents of an X.509
// SubjectAlternativeName extension.
func marshalSANs(dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL) []asn1.RawValue {
var rawValues []asn1.RawValue
for _, name := range dnsNames {
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeDNS, Class: 2, Bytes: []byte(name)})
}
for _, email := range emailAddresses {
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeEmail, Class: 2, Bytes: []byte(email)})
}
for _, rawIP := range ipAddresses {
// If possible, we always want to encode IPv4 addresses in 4 bytes.
ip := rawIP.To4()
if ip == nil {
ip = rawIP
}
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeIP, Class: 2, Bytes: ip})
}
for _, uri := range uris {
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeURI, Class: 2, Bytes: []byte(uri.String())})
}
return rawValues
}
func StringToOid(in string) (asn1.ObjectIdentifier, error) {
split := strings.Split(in, ".")
ret := make(asn1.ObjectIdentifier, 0, len(split))
for _, v := range split {
i, err := strconv.Atoi(v)
if err != nil {
return nil, err
}
ret = append(ret, i)
}
return asn1.ObjectIdentifier(ret), nil
}
func ValidateKeyTypeLength(keyType string, keyBits int) error {
switch keyType {
case "rsa":
switch keyBits {
case 2048:
case 4096:
case 8192:
default:
return fmt.Errorf("unsupported bit length for RSA key: %d", keyBits)
}
case "ec":
switch keyBits {
case 224:
case 256:
case 384:
case 521:
default:
return fmt.Errorf("unsupported bit length for EC key: %d", keyBits)
}
case "any":
default:
return fmt.Errorf("unknown key type %s", keyType)
}
return nil
}
// Performs the heavy lifting of creating a certificate. Returns
// a fully-filled-in ParsedCertBundle.
func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) {
var err error
result := &ParsedCertBundle{}
serialNumber, err := GenerateSerialNumber()
if err != nil {
return nil, err
}
if err := GeneratePrivateKey(data.Params.KeyType,
data.Params.KeyBits,
result); err != nil {
return nil, err
}
subjKeyID, err := GetSubjKeyID(result.PrivateKey)
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("error getting subject key ID: %s", err)}
}
certTemplate := &x509.Certificate{
SerialNumber: serialNumber,
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: data.Params.NotAfter,
IsCA: false,
SubjectKeyId: subjKeyID,
Subject: data.Params.Subject,
DNSNames: data.Params.DNSNames,
EmailAddresses: data.Params.EmailAddresses,
IPAddresses: data.Params.IPAddresses,
URIs: data.Params.URIs,
}
if data.Params.NotBeforeDuration > 0 {
certTemplate.NotBefore = time.Now().Add(-1 * data.Params.NotBeforeDuration)
}
if err := HandleOtherSANs(certTemplate, data.Params.OtherSANs); err != nil {
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
}
// Add this before calling addKeyUsages
if data.SigningBundle == nil {
certTemplate.IsCA = true
} else if data.Params.BasicConstraintsValidForNonCA {
certTemplate.BasicConstraintsValid = true
certTemplate.IsCA = false
}
// This will only be filled in from the generation paths
if len(data.Params.PermittedDNSDomains) > 0 {
certTemplate.PermittedDNSDomains = data.Params.PermittedDNSDomains
certTemplate.PermittedDNSDomainsCritical = true
}
AddPolicyIdentifiers(data, certTemplate)
AddKeyUsages(data, certTemplate)
AddExtKeyUsageOids(data, certTemplate)
certTemplate.IssuingCertificateURL = data.Params.URLs.IssuingCertificates
certTemplate.CRLDistributionPoints = data.Params.URLs.CRLDistributionPoints
certTemplate.OCSPServer = data.Params.URLs.OCSPServers
var certBytes []byte
if data.SigningBundle != nil {
switch data.SigningBundle.PrivateKeyType {
case RSAPrivateKey:
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
case ECPrivateKey:
certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
}
caCert := data.SigningBundle.Certificate
certTemplate.AuthorityKeyId = caCert.SubjectKeyId
certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, result.PrivateKey.Public(), data.SigningBundle.PrivateKey)
} else {
// Creating a self-signed root
if data.Params.MaxPathLength == 0 {
certTemplate.MaxPathLen = 0
certTemplate.MaxPathLenZero = true
} else {
certTemplate.MaxPathLen = data.Params.MaxPathLength
}
switch data.Params.KeyType {
case "rsa":
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
case "ec":
certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
}
certTemplate.AuthorityKeyId = subjKeyID
certTemplate.BasicConstraintsValid = true
certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, result.PrivateKey.Public(), result.PrivateKey)
}
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
}
result.CertificateBytes = certBytes
result.Certificate, err = x509.ParseCertificate(certBytes)
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)}
}
if data.SigningBundle != nil {
if len(data.SigningBundle.Certificate.AuthorityKeyId) > 0 &&
!bytes.Equal(data.SigningBundle.Certificate.AuthorityKeyId, data.SigningBundle.Certificate.SubjectKeyId) {
result.CAChain = []*CertBlock{
&CertBlock{
Certificate: data.SigningBundle.Certificate,
Bytes: data.SigningBundle.CertificateBytes,
},
}
result.CAChain = append(result.CAChain, data.SigningBundle.CAChain...)
}
}
return result, nil
}
var oidExtensionBasicConstraints = []int{2, 5, 29, 19}
// Creates a CSR. This is currently only meant for use when
// generating an intermediate certificate.
func CreateCSR(data *CreationBundle, addBasicConstraints bool) (*ParsedCSRBundle, error) {
var err error
result := &ParsedCSRBundle{}
if err := GeneratePrivateKey(data.Params.KeyType,
data.Params.KeyBits,
result); err != nil {
return nil, err
}
// Like many root CAs, other information is ignored
csrTemplate := &x509.CertificateRequest{
Subject: data.Params.Subject,
DNSNames: data.Params.DNSNames,
EmailAddresses: data.Params.EmailAddresses,
IPAddresses: data.Params.IPAddresses,
URIs: data.Params.URIs,
}
if err := HandleOtherCSRSANs(csrTemplate, data.Params.OtherSANs); err != nil {
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
}
if addBasicConstraints {
type basicConstraints struct {
IsCA bool `asn1:"optional"`
MaxPathLen int `asn1:"optional,default:-1"`
}
val, err := asn1.Marshal(basicConstraints{IsCA: true, MaxPathLen: -1})
if err != nil {
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling basic constraints: {{err}}", err).Error()}
}
ext := pkix.Extension{
Id: oidExtensionBasicConstraints,
Value: val,
Critical: true,
}
csrTemplate.ExtraExtensions = append(csrTemplate.ExtraExtensions, ext)
}
switch data.Params.KeyType {
case "rsa":
csrTemplate.SignatureAlgorithm = x509.SHA256WithRSA
case "ec":
csrTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
}
csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, result.PrivateKey)
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
}
result.CSRBytes = csr
result.CSR, err = x509.ParseCertificateRequest(csr)
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %v", err)}
}
return result, nil
}
// Performs the heavy lifting of generating a certificate from a CSR.
// Returns a ParsedCertBundle sans private keys.
func SignCertificate(data *CreationBundle) (*ParsedCertBundle, error) {
switch {
case data == nil:
return nil, errutil.UserError{Err: "nil data bundle given to signCertificate"}
case data.Params == nil:
return nil, errutil.UserError{Err: "nil parameters given to signCertificate"}
case data.SigningBundle == nil:
return nil, errutil.UserError{Err: "nil signing bundle given to signCertificate"}
case data.CSR == nil:
return nil, errutil.UserError{Err: "nil csr given to signCertificate"}
}
err := data.CSR.CheckSignature()
if err != nil {
return nil, errutil.UserError{Err: "request signature invalid"}
}
result := &ParsedCertBundle{}
serialNumber, err := GenerateSerialNumber()
if err != nil {
return nil, err
}
marshaledKey, err := x509.MarshalPKIXPublicKey(data.CSR.PublicKey)
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("error marshalling public key: %s", err)}
}
subjKeyID := sha1.Sum(marshaledKey)
caCert := data.SigningBundle.Certificate
certTemplate := &x509.Certificate{
SerialNumber: serialNumber,
Subject: data.Params.Subject,
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: data.Params.NotAfter,
SubjectKeyId: subjKeyID[:],
AuthorityKeyId: caCert.SubjectKeyId,
}
if data.Params.NotBeforeDuration > 0 {
certTemplate.NotBefore = time.Now().Add(-1 * data.Params.NotBeforeDuration)
}
switch data.SigningBundle.PrivateKeyType {
case RSAPrivateKey:
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
case ECPrivateKey:
certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
}
if data.Params.UseCSRValues {
certTemplate.Subject = data.CSR.Subject
certTemplate.Subject.ExtraNames = certTemplate.Subject.Names
certTemplate.DNSNames = data.CSR.DNSNames
certTemplate.EmailAddresses = data.CSR.EmailAddresses
certTemplate.IPAddresses = data.CSR.IPAddresses
certTemplate.URIs = data.CSR.URIs
for _, name := range data.CSR.Extensions {
if !name.Id.Equal(oidExtensionBasicConstraints) {
certTemplate.ExtraExtensions = append(certTemplate.ExtraExtensions, name)
}
}
} else {
certTemplate.DNSNames = data.Params.DNSNames
certTemplate.EmailAddresses = data.Params.EmailAddresses
certTemplate.IPAddresses = data.Params.IPAddresses
certTemplate.URIs = data.Params.URIs
}
if err := HandleOtherSANs(certTemplate, data.Params.OtherSANs); err != nil {
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
}
AddPolicyIdentifiers(data, certTemplate)
AddKeyUsages(data, certTemplate)
AddExtKeyUsageOids(data, certTemplate)
var certBytes []byte
certTemplate.IssuingCertificateURL = data.Params.URLs.IssuingCertificates
certTemplate.CRLDistributionPoints = data.Params.URLs.CRLDistributionPoints
certTemplate.OCSPServer = data.SigningBundle.URLs.OCSPServers
if data.Params.IsCA {
certTemplate.BasicConstraintsValid = true
certTemplate.IsCA = true
if data.SigningBundle.Certificate.MaxPathLen == 0 &&
data.SigningBundle.Certificate.MaxPathLenZero {
return nil, errutil.UserError{Err: "signing certificate has a max path length of zero, and cannot issue further CA certificates"}
}
certTemplate.MaxPathLen = data.Params.MaxPathLength
if certTemplate.MaxPathLen == 0 {
certTemplate.MaxPathLenZero = true
}
} else if data.Params.BasicConstraintsValidForNonCA {
certTemplate.BasicConstraintsValid = true
certTemplate.IsCA = false
}
if len(data.Params.PermittedDNSDomains) > 0 {
certTemplate.PermittedDNSDomains = data.Params.PermittedDNSDomains
certTemplate.PermittedDNSDomainsCritical = true
}
certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, data.CSR.PublicKey, data.SigningBundle.PrivateKey)
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
}
result.CertificateBytes = certBytes
result.Certificate, err = x509.ParseCertificate(certBytes)
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)}
}
result.CAChain = data.SigningBundle.GetCAChain()
return result, nil
}

View file

@ -15,10 +15,14 @@ import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"net"
"net/url"
"strings"
"time"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/helper/errutil"
@ -598,3 +602,158 @@ type IssueData struct {
IPSANs string `json:"ip_sans" structs:"ip_sans" mapstructure:"ip_sans"`
CSR string `json:"csr" structs:"csr" mapstructure:"csr"`
}
type URLEntries struct {
IssuingCertificates []string `json:"issuing_certificates" structs:"issuing_certificates" mapstructure:"issuing_certificates"`
CRLDistributionPoints []string `json:"crl_distribution_points" structs:"crl_distribution_points" mapstructure:"crl_distribution_points"`
OCSPServers []string `json:"ocsp_servers" structs:"ocsp_servers" mapstructure:"ocsp_servers"`
}
type CAInfoBundle struct {
ParsedCertBundle
URLs *URLEntries
}
func (b *CAInfoBundle) GetCAChain() []*CertBlock {
chain := []*CertBlock{}
// Include issuing CA in Chain, not including Root Authority
if (len(b.Certificate.AuthorityKeyId) > 0 &&
!bytes.Equal(b.Certificate.AuthorityKeyId, b.Certificate.SubjectKeyId)) ||
(len(b.Certificate.AuthorityKeyId) == 0 &&
!bytes.Equal(b.Certificate.RawIssuer, b.Certificate.RawSubject)) {
chain = append(chain, &CertBlock{
Certificate: b.Certificate,
Bytes: b.CertificateBytes,
})
if b.CAChain != nil && len(b.CAChain) > 0 {
chain = append(chain, b.CAChain...)
}
}
return chain
}
type CertExtKeyUsage int
const (
AnyExtKeyUsage CertExtKeyUsage = 1 << iota
ServerAuthExtKeyUsage
ClientAuthExtKeyUsage
CodeSigningExtKeyUsage
EmailProtectionExtKeyUsage
IpsecEndSystemExtKeyUsage
IpsecTunnelExtKeyUsage
IpsecUserExtKeyUsage
TimeStampingExtKeyUsage
OcspSigningExtKeyUsage
MicrosoftServerGatedCryptoExtKeyUsage
NetscapeServerGatedCryptoExtKeyUsage
MicrosoftCommercialCodeSigningExtKeyUsage
MicrosoftKernelCodeSigningExtKeyUsage
)
type CreationParameters struct {
Subject pkix.Name
DNSNames []string
EmailAddresses []string
IPAddresses []net.IP
URIs []*url.URL
OtherSANs map[string][]string
IsCA bool
KeyType string
KeyBits int
NotAfter time.Time
KeyUsage x509.KeyUsage
ExtKeyUsage CertExtKeyUsage
ExtKeyUsageOIDs []string
PolicyIdentifiers []string
BasicConstraintsValidForNonCA bool
// Only used when signing a CA cert
UseCSRValues bool
PermittedDNSDomains []string
// URLs to encode into the certificate
URLs *URLEntries
// The maximum path length to encode
MaxPathLength int
// The duration the certificate will use NotBefore
NotBeforeDuration time.Duration
}
type CreationBundle struct {
Params *CreationParameters
SigningBundle *CAInfoBundle
CSR *x509.CertificateRequest
}
// addKeyUsages adds appropriate key usages to the template given the creation
// information
func AddKeyUsages(data *CreationBundle, certTemplate *x509.Certificate) {
if data.Params.IsCA {
certTemplate.KeyUsage = x509.KeyUsage(x509.KeyUsageCertSign | x509.KeyUsageCRLSign)
return
}
certTemplate.KeyUsage = data.Params.KeyUsage
if data.Params.ExtKeyUsage&AnyExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageAny)
}
if data.Params.ExtKeyUsage&ServerAuthExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
}
if data.Params.ExtKeyUsage&ClientAuthExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
}
if data.Params.ExtKeyUsage&CodeSigningExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
}
if data.Params.ExtKeyUsage&EmailProtectionExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
}
if data.Params.ExtKeyUsage&IpsecEndSystemExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageIPSECEndSystem)
}
if data.Params.ExtKeyUsage&IpsecTunnelExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageIPSECTunnel)
}
if data.Params.ExtKeyUsage&IpsecUserExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageIPSECUser)
}
if data.Params.ExtKeyUsage&TimeStampingExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageTimeStamping)
}
if data.Params.ExtKeyUsage&OcspSigningExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageOCSPSigning)
}
if data.Params.ExtKeyUsage&MicrosoftServerGatedCryptoExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageMicrosoftServerGatedCrypto)
}
if data.Params.ExtKeyUsage&NetscapeServerGatedCryptoExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageNetscapeServerGatedCrypto)
}
if data.Params.ExtKeyUsage&MicrosoftCommercialCodeSigningExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageMicrosoftCommercialCodeSigning)
}
if data.Params.ExtKeyUsage&MicrosoftKernelCodeSigningExtKeyUsage != 0 {
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageMicrosoftKernelCodeSigning)
}
}

View file

@ -12,7 +12,16 @@ import (
squarejwt "gopkg.in/square/go-jose.v2/jwt"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
)
var (
// PluginMetadataModeEnv is an ENV name used to disable TLS communication
// to bootstrap mounting plugins.
PluginMetadataModeEnv = "VAULT_PLUGIN_METADATA_MODE"
// PluginUnwrapTokenEnv is the ENV name used to pass unwrap tokens to the
// plugin.
PluginUnwrapTokenEnv = "VAULT_UNWRAP_TOKEN"
)
// PluginAPIClientMeta is a helper that plugins can use to configure TLS connections
@ -61,12 +70,12 @@ func (f *PluginAPIClientMeta) GetTLSConfig() *TLSConfig {
// VaultPluginTLSProvider is run inside a plugin and retrieves the response
// wrapped TLS certificate from vault. It returns a configured TLS Config.
func VaultPluginTLSProvider(apiTLSConfig *TLSConfig) func() (*tls.Config, error) {
if os.Getenv(pluginutil.PluginMetadataModeEnv) == "true" {
if os.Getenv(PluginMetadataModeEnv) == "true" {
return nil
}
return func() (*tls.Config, error) {
unwrapToken := os.Getenv(pluginutil.PluginUnwrapTokenEnv)
unwrapToken := os.Getenv(PluginUnwrapTokenEnv)
parsedJWT, err := squarejwt.ParseSigned(unwrapToken)
if err != nil {

View file

@ -100,7 +100,7 @@ The seal can be migrated from Shamir keys to Auto Unseal and vice versa.
To migrate from Shamir keys to Auto Unseal, take your server cluster offline and update
the [seal configuration](/docs/configuration/seal/index.html) with the appropriate seal
configuration. When you bring up your server back up, run the unseal process with the
configuration. When you bring your server back up, run the unseal process with the
`-migrate` flag. All unseal commands must specify the `-migrate` flag. Once the
required threshold of unseal keys are entered, the unseal keys will be migrated to
recovery keys.
@ -112,7 +112,7 @@ $ vault operator unseal -migrate
To migrate from Auto Unseal to Shamir keys, take your server cluster offline and update
the [seal configuration](/docs/configuration/seal/index.html) and add `disabled = "true"`
to the seal block. This allows the migration to use this information to decrypt the key
but will not unseal Vault. When you bring up your server back up, run the unseal process
but will not unseal Vault. When you bring your server back up, run the unseal process
with the `-migrate` flag and use the Recovery Keys to perform the migration. All unseal
commands must specify the `-migrate` flag. Once the required threshold of recovery keys
are entered, the recovery keys will be migrated to be used as unseal keys.