2016-04-06 00:42:26 +00:00
|
|
|
package aws
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/base64"
|
|
|
|
"encoding/pem"
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
|
2016-04-28 02:25:15 +00:00
|
|
|
"github.com/fatih/structs"
|
2016-04-06 00:42:26 +00:00
|
|
|
"github.com/hashicorp/vault/logical"
|
|
|
|
"github.com/hashicorp/vault/logical/framework"
|
|
|
|
)
|
|
|
|
|
|
|
|
// dsaSignature represents the contents of the signature of a signed
|
|
|
|
// content using digital signature algorithm.
|
|
|
|
type dsaSignature struct {
|
|
|
|
R, S *big.Int
|
|
|
|
}
|
|
|
|
|
|
|
|
// As per AWS documentation, this public key is valid for US East (N. Virginia),
|
|
|
|
// US West (Oregon), US West (N. California), EU (Ireland), EU (Frankfurt),
|
|
|
|
// Asia Pacific (Tokyo), Asia Pacific (Seoul), Asia Pacific (Singapore),
|
2016-05-05 15:22:36 +00:00
|
|
|
// Asia Pacific (Sydney), and South America (Sao Paulo).
|
|
|
|
//
|
|
|
|
// It's also the same certificate, but for some reason listed separately, for
|
|
|
|
// GovCloud (US)
|
2016-04-13 23:01:06 +00:00
|
|
|
const genericAWSPublicCertificate = `-----BEGIN CERTIFICATE-----
|
2016-04-06 00:42:26 +00:00
|
|
|
MIIC7TCCAq0CCQCWukjZ5V4aZzAJBgcqhkjOOAQDMFwxCzAJBgNVBAYTAlVTMRkw
|
|
|
|
FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYD
|
|
|
|
VQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0xMjAxMDUxMjU2MTJaFw0z
|
|
|
|
ODAxMDUxMjU2MTJaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9u
|
|
|
|
IFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNl
|
|
|
|
cnZpY2VzIExMQzCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQCjkvcS2bb1VQ4yt/5e
|
|
|
|
ih5OO6kK/n1Lzllr7D8ZwtQP8fOEpp5E2ng+D6Ud1Z1gYipr58Kj3nssSNpI6bX3
|
|
|
|
VyIQzK7wLclnd/YozqNNmgIyZecN7EglK9ITHJLP+x8FtUpt3QbyYXJdmVMegN6P
|
|
|
|
hviYt5JH/nYl4hh3Pa1HJdskgQIVALVJ3ER11+Ko4tP6nwvHwh6+ERYRAoGBAI1j
|
|
|
|
k+tkqMVHuAFcvAGKocTgsjJem6/5qomzJuKDmbJNu9Qxw3rAotXau8Qe+MBcJl/U
|
|
|
|
hhy1KHVpCGl9fueQ2s6IL0CaO/buycU1CiYQk40KNHCcHfNiZbdlx1E9rpUp7bnF
|
|
|
|
lRa2v1ntMX3caRVDdbtPEWmdxSCYsYFDk4mZrOLBA4GEAAKBgEbmeve5f8LIE/Gf
|
|
|
|
MNmP9CM5eovQOGx5ho8WqD+aTebs+k2tn92BBPqeZqpWRa5P/+jrdKml1qx4llHW
|
|
|
|
MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw
|
|
|
|
vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw
|
|
|
|
7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K
|
|
|
|
-----END CERTIFICATE-----
|
|
|
|
`
|
|
|
|
|
2016-04-13 23:01:06 +00:00
|
|
|
// pathListCertificates creates a path that enables listing of all
|
|
|
|
// the AWS public certificates registered with Vault.
|
|
|
|
func pathListCertificates(b *backend) *framework.Path {
|
|
|
|
return &framework.Path{
|
|
|
|
Pattern: "config/certificates/?",
|
|
|
|
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
|
|
logical.ListOperation: b.pathCertificatesList,
|
|
|
|
},
|
|
|
|
|
|
|
|
HelpSynopsis: pathListCertificatesHelpSyn,
|
|
|
|
HelpDescription: pathListCertificatesHelpDesc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-06 00:42:26 +00:00
|
|
|
func pathConfigCertificate(b *backend) *framework.Path {
|
|
|
|
return &framework.Path{
|
2016-04-13 23:01:06 +00:00
|
|
|
Pattern: "config/certificate/" + framework.GenericNameRegex("cert_name"),
|
2016-04-06 00:42:26 +00:00
|
|
|
Fields: map[string]*framework.FieldSchema{
|
2016-04-13 23:01:06 +00:00
|
|
|
"cert_name": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: "Name of the certificate.",
|
|
|
|
},
|
2016-04-06 00:42:26 +00:00
|
|
|
"aws_public_cert": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
2016-04-13 23:01:06 +00:00
|
|
|
Description: "AWS Public cert required to verify PKCS7 signature of the EC2 instance metadata.",
|
2016-04-06 00:42:26 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
ExistenceCheck: b.pathConfigCertificateExistenceCheck,
|
|
|
|
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
|
|
logical.CreateOperation: b.pathConfigCertificateCreateUpdate,
|
|
|
|
logical.UpdateOperation: b.pathConfigCertificateCreateUpdate,
|
|
|
|
logical.ReadOperation: b.pathConfigCertificateRead,
|
2016-04-13 23:01:06 +00:00
|
|
|
logical.DeleteOperation: b.pathConfigCertificateDelete,
|
2016-04-06 00:42:26 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
HelpSynopsis: pathConfigCertificateSyn,
|
|
|
|
HelpDescription: pathConfigCertificateDesc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Establishes dichotomy of request operation between CreateOperation and UpdateOperation.
|
|
|
|
// Returning 'true' forces an UpdateOperation, CreateOperation otherwise.
|
|
|
|
func (b *backend) pathConfigCertificateExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
|
2016-04-13 23:01:06 +00:00
|
|
|
certName := data.Get("cert_name").(string)
|
|
|
|
if certName == "" {
|
|
|
|
return false, fmt.Errorf("missing cert_name")
|
|
|
|
}
|
2016-05-05 18:12:22 +00:00
|
|
|
|
2016-05-18 00:39:24 +00:00
|
|
|
entry, err := b.lockedAWSPublicCertificateEntry(req.Storage, certName)
|
2016-04-06 00:42:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return entry != nil, nil
|
|
|
|
}
|
|
|
|
|
2016-04-13 23:01:06 +00:00
|
|
|
// pathCertificatesList is used to list all the AWS public certificates registered with Vault.
|
|
|
|
func (b *backend) pathCertificatesList(
|
|
|
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2016-04-19 19:32:15 +00:00
|
|
|
b.configMutex.RLock()
|
|
|
|
defer b.configMutex.RUnlock()
|
2016-05-03 16:14:07 +00:00
|
|
|
|
2016-04-13 23:01:06 +00:00
|
|
|
certs, err := req.Storage.List("config/certificate/")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return logical.ListResponse(certs), nil
|
|
|
|
}
|
|
|
|
|
2016-04-06 00:42:26 +00:00
|
|
|
// Decodes the PEM encoded certiticate and parses it into a x509 cert.
|
|
|
|
func decodePEMAndParseCertificate(certificate string) (*x509.Certificate, error) {
|
|
|
|
// Decode the PEM block and error out if a block is not detected in the first attempt.
|
|
|
|
decodedPublicCert, rest := pem.Decode([]byte(certificate))
|
|
|
|
if len(rest) != 0 {
|
2016-05-05 15:22:36 +00:00
|
|
|
return nil, fmt.Errorf("invalid certificate; should be one PEM block only")
|
2016-04-06 00:42:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the certificate can be parsed.
|
|
|
|
publicCert, err := x509.ParseCertificate(decodedPublicCert.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if publicCert == nil {
|
|
|
|
return nil, fmt.Errorf("invalid certificate; failed to parse certificate")
|
|
|
|
}
|
|
|
|
return publicCert, nil
|
|
|
|
}
|
|
|
|
|
2016-04-13 23:01:06 +00:00
|
|
|
// awsPublicCertificates returns a slice of all the parsed AWS public
|
|
|
|
// certificates, that were registered using `config/certificate/<cert_name>` endpoint.
|
2016-05-03 16:14:07 +00:00
|
|
|
// This method will also append default certificate in the backend, to the slice.
|
2016-04-19 21:07:06 +00:00
|
|
|
func (b *backend) awsPublicCertificates(s logical.Storage) ([]*x509.Certificate, error) {
|
2016-05-05 18:12:22 +00:00
|
|
|
// Lock at beginning and use internal method so that we are consistent as
|
|
|
|
// we iterate through
|
|
|
|
b.configMutex.RLock()
|
|
|
|
defer b.configMutex.RUnlock()
|
|
|
|
|
2016-04-28 02:25:15 +00:00
|
|
|
var certs []*x509.Certificate
|
2016-05-03 16:14:07 +00:00
|
|
|
|
2016-04-28 02:25:15 +00:00
|
|
|
// Append the generic certificate provided in the AWS EC2 instance metadata documentation.
|
|
|
|
decodedCert, err := decodePEMAndParseCertificate(genericAWSPublicCertificate)
|
2016-04-06 00:42:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-04-28 02:25:15 +00:00
|
|
|
certs = append(certs, decodedCert)
|
2016-04-13 23:01:06 +00:00
|
|
|
|
2016-04-28 02:25:15 +00:00
|
|
|
// Get the list of all the registered certificates.
|
|
|
|
registeredCerts, err := s.List("config/certificate/")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-04-13 23:01:06 +00:00
|
|
|
|
|
|
|
// Iterate through each certificate, parse and append it to a slice.
|
|
|
|
for _, cert := range registeredCerts {
|
2016-05-18 00:39:24 +00:00
|
|
|
certEntry, err := b.nonLockedAWSPublicCertificateEntry(s, cert)
|
2016-04-13 23:01:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if certEntry == nil {
|
|
|
|
return nil, fmt.Errorf("certificate storage has a nil entry under the name:%s\n", cert)
|
|
|
|
}
|
|
|
|
decodedCert, err := decodePEMAndParseCertificate(certEntry.AWSPublicCert)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
certs = append(certs, decodedCert)
|
|
|
|
}
|
|
|
|
|
|
|
|
return certs, nil
|
2016-04-06 00:42:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// awsPublicCertificate is used to get the configured AWS Public Key that is used
|
|
|
|
// to verify the PKCS#7 signature of the instance identity document.
|
2016-05-18 00:39:24 +00:00
|
|
|
func (b *backend) lockedAWSPublicCertificateEntry(s logical.Storage, certName string) (*awsPublicCert, error) {
|
2016-04-19 19:32:15 +00:00
|
|
|
b.configMutex.RLock()
|
|
|
|
defer b.configMutex.RUnlock()
|
2016-05-03 16:14:07 +00:00
|
|
|
|
2016-05-18 00:39:24 +00:00
|
|
|
return b.nonLockedAWSPublicCertificateEntry(s, certName)
|
2016-05-05 18:12:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Internal version of the above that does no locking
|
2016-05-18 00:39:24 +00:00
|
|
|
func (b *backend) nonLockedAWSPublicCertificateEntry(s logical.Storage, certName string) (*awsPublicCert, error) {
|
2016-04-13 23:01:06 +00:00
|
|
|
entry, err := s.Get("config/certificate/" + certName)
|
2016-04-06 00:42:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if entry == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var result awsPublicCert
|
|
|
|
if err := entry.DecodeJSON(&result); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &result, nil
|
|
|
|
}
|
|
|
|
|
2016-04-13 23:01:06 +00:00
|
|
|
// pathConfigCertificateDelete is used to delete the previously configured AWS Public Key
|
|
|
|
// that is used to verify the PKCS#7 signature of the instance identity document.
|
|
|
|
func (b *backend) pathConfigCertificateDelete(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2016-04-27 03:40:11 +00:00
|
|
|
b.configMutex.Lock()
|
|
|
|
defer b.configMutex.Unlock()
|
2016-04-28 04:35:49 +00:00
|
|
|
|
2016-04-13 23:01:06 +00:00
|
|
|
certName := data.Get("cert_name").(string)
|
|
|
|
if certName == "" {
|
|
|
|
return logical.ErrorResponse("missing cert_name"), nil
|
|
|
|
}
|
2016-04-28 04:35:49 +00:00
|
|
|
|
|
|
|
return nil, req.Storage.Delete("config/certificate/" + certName)
|
2016-04-13 23:01:06 +00:00
|
|
|
}
|
|
|
|
|
2016-04-06 00:42:26 +00:00
|
|
|
// pathConfigCertificateRead is used to view the configured AWS Public Key that is
|
|
|
|
// used to verify the PKCS#7 signature of the instance identity document.
|
|
|
|
func (b *backend) pathConfigCertificateRead(
|
|
|
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2016-04-13 23:01:06 +00:00
|
|
|
certName := data.Get("cert_name").(string)
|
|
|
|
if certName == "" {
|
|
|
|
return logical.ErrorResponse("missing cert_name"), nil
|
|
|
|
}
|
|
|
|
|
2016-05-18 00:39:24 +00:00
|
|
|
certificateEntry, err := b.lockedAWSPublicCertificateEntry(req.Storage, certName)
|
2016-04-06 00:42:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if certificateEntry == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &logical.Response{
|
2016-04-28 02:25:15 +00:00
|
|
|
Data: structs.New(certificateEntry).Map(),
|
2016-04-06 00:42:26 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// pathConfigCertificateCreateUpdate is used to register an AWS Public Key that is
|
|
|
|
// used to verify the PKCS#7 signature of the instance identity document.
|
|
|
|
func (b *backend) pathConfigCertificateCreateUpdate(
|
|
|
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2016-04-13 23:01:06 +00:00
|
|
|
certName := data.Get("cert_name").(string)
|
|
|
|
if certName == "" {
|
|
|
|
return logical.ErrorResponse("missing cert_name"), nil
|
|
|
|
}
|
|
|
|
|
2016-05-05 18:12:22 +00:00
|
|
|
b.configMutex.Lock()
|
|
|
|
defer b.configMutex.Unlock()
|
|
|
|
|
2016-04-06 00:42:26 +00:00
|
|
|
// Check if there is already a certificate entry registered.
|
2016-05-18 00:39:24 +00:00
|
|
|
certEntry, err := b.nonLockedAWSPublicCertificateEntry(req.Storage, certName)
|
2016-04-06 00:42:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if certEntry == nil {
|
|
|
|
certEntry = &awsPublicCert{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the value is provided by the client.
|
2016-05-05 15:36:28 +00:00
|
|
|
certStrData, ok := data.GetOk("aws_public_cert")
|
2016-04-06 00:42:26 +00:00
|
|
|
if ok {
|
2016-05-05 15:36:28 +00:00
|
|
|
if certBytes, err := base64.StdEncoding.DecodeString(certStrData.(string)); err == nil {
|
|
|
|
certEntry.AWSPublicCert = string(certBytes)
|
|
|
|
} else {
|
2016-05-05 16:44:40 +00:00
|
|
|
certEntry.AWSPublicCert = certStrData.(string)
|
2016-04-06 00:42:26 +00:00
|
|
|
}
|
2016-04-13 23:01:06 +00:00
|
|
|
} else {
|
|
|
|
// aws_public_cert should be supplied for both create and update operations.
|
|
|
|
// If it is not provided, throw an error.
|
|
|
|
return logical.ErrorResponse("missing aws_public_cert"), nil
|
2016-04-06 00:42:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If explicitly set to empty string, error out.
|
|
|
|
if certEntry.AWSPublicCert == "" {
|
2016-04-13 23:01:06 +00:00
|
|
|
return logical.ErrorResponse("invalid aws_public_cert"), nil
|
2016-04-06 00:42:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the certificate by decoding it and parsing it.
|
|
|
|
publicCert, err := decodePEMAndParseCertificate(certEntry.AWSPublicCert)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if publicCert == nil {
|
|
|
|
return logical.ErrorResponse("invalid certificate; failed to decode and parse certificate"), nil
|
|
|
|
}
|
|
|
|
|
2016-05-05 18:12:22 +00:00
|
|
|
// Ensure that we have not
|
2016-04-06 00:42:26 +00:00
|
|
|
// If none of the checks fail, save the provided certificate.
|
2016-04-13 23:01:06 +00:00
|
|
|
entry, err := logical.StorageEntryJSON("config/certificate/"+certName, certEntry)
|
2016-04-06 00:42:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := req.Storage.Put(entry); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Struct awsPublicCert holds the AWS Public Key that is used to verify the PKCS#7 signature
|
|
|
|
// of the instnace identity document.
|
|
|
|
type awsPublicCert struct {
|
|
|
|
AWSPublicCert string `json:"aws_public_cert" structs:"aws_public_cert" mapstructure:"aws_public_cert"`
|
|
|
|
}
|
|
|
|
|
|
|
|
const pathConfigCertificateSyn = `
|
2016-05-12 11:19:29 +00:00
|
|
|
Adds the AWS Public Key that is used to verify the PKCS#7 signature of the identidy document.
|
2016-04-06 00:42:26 +00:00
|
|
|
`
|
|
|
|
|
|
|
|
const pathConfigCertificateDesc = `
|
2016-04-28 02:25:15 +00:00
|
|
|
AWS Public Key which is used to verify the PKCS#7 signature of the identity document,
|
2016-05-12 11:19:29 +00:00
|
|
|
varies by region. The public key(s) can be found in AWS EC2 instance metadata documentation.
|
2016-04-28 02:25:15 +00:00
|
|
|
The default key that is used to verify the signature is the one that is applicable for
|
|
|
|
following regions: US East (N. Virginia), US West (Oregon), US West (N. California),
|
|
|
|
EU (Ireland), EU (Frankfurt), Asia Pacific (Tokyo), Asia Pacific (Seoul), Asia Pacific (Singapore),
|
2016-04-06 00:42:26 +00:00
|
|
|
Asia Pacific (Sydney), and South America (Sao Paulo).
|
2016-04-28 02:25:15 +00:00
|
|
|
|
2016-05-12 11:19:29 +00:00
|
|
|
If the instances belongs to region other than the above, the public key(s) for the
|
|
|
|
corresponding regions should be registered using this endpoint. PKCS#7 is verified
|
|
|
|
using a collection of certificates containing the default certificate and all the
|
|
|
|
certificates that are registered using this endpoint.
|
2016-04-06 00:42:26 +00:00
|
|
|
`
|
2016-04-13 23:01:06 +00:00
|
|
|
const pathListCertificatesHelpSyn = `
|
2016-05-12 11:19:29 +00:00
|
|
|
Lists all the AWS public certificates that are registered with the backend.
|
2016-04-13 23:01:06 +00:00
|
|
|
`
|
|
|
|
const pathListCertificatesHelpDesc = `
|
|
|
|
Certificates will be listed by their respective names that were used during registration.
|
|
|
|
`
|