mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
Don't show field names when not needed
This commit is contained in:
@@ -37,8 +37,9 @@ func Backend() *framework.Backend {
|
||||
|
||||
Paths: []*framework.Path{
|
||||
pathRoles(&b),
|
||||
pathGenerateCA(&b),
|
||||
pathSignCA(&b),
|
||||
pathGenerateRootCA(&b),
|
||||
pathGenerateIntermediateCA(&b),
|
||||
pathSignIntermediateCA(&b),
|
||||
pathSetCA(&b),
|
||||
pathConfigCA(&b),
|
||||
pathConfigCRL(&b),
|
||||
|
||||
@@ -170,10 +170,16 @@ func validateCommonNames(req *logical.Request, commonNames []string, role *roleE
|
||||
func generateCert(b *backend,
|
||||
role *roleEntry,
|
||||
signingBundle *certutil.ParsedCertBundle,
|
||||
isCA bool,
|
||||
req *logical.Request,
|
||||
data *framework.FieldData) (*certutil.ParsedCertBundle, error) {
|
||||
|
||||
creationBundle, err := generateCreationBundle(b, role, signingBundle, req, data)
|
||||
_, ok := data.GetOk("common_name")
|
||||
if !ok {
|
||||
return nil, certutil.UserError{Err: "The common_name field is required"}
|
||||
}
|
||||
|
||||
creationBundle, err := generateCreationBundle(b, role, signingBundle, isCA, req, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -189,10 +195,11 @@ func generateCert(b *backend,
|
||||
func generateCSR(b *backend,
|
||||
role *roleEntry,
|
||||
signingBundle *certutil.ParsedCertBundle,
|
||||
isCA bool,
|
||||
req *logical.Request,
|
||||
data *framework.FieldData) (*certutil.ParsedCSRBundle, error) {
|
||||
|
||||
creationBundle, err := generateCreationBundle(b, role, signingBundle, req, data)
|
||||
creationBundle, err := generateCreationBundle(b, role, signingBundle, isCA, req, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -209,10 +216,16 @@ func signCert(b *backend,
|
||||
role *roleEntry,
|
||||
signingBundle *certutil.ParsedCertBundle,
|
||||
csr *x509.CertificateRequest,
|
||||
isCA bool,
|
||||
req *logical.Request,
|
||||
data *framework.FieldData) (*certutil.ParsedCertBundle, error) {
|
||||
|
||||
creationBundle, err := generateCreationBundle(b, role, signingBundle, req, data)
|
||||
_, ok := data.GetOk("common_name")
|
||||
if !ok {
|
||||
return nil, certutil.UserError{Err: "The common_name field is required"}
|
||||
}
|
||||
|
||||
creationBundle, err := generateCreationBundle(b, role, signingBundle, isCA, req, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -228,47 +241,56 @@ func signCert(b *backend,
|
||||
func generateCreationBundle(b *backend,
|
||||
role *roleEntry,
|
||||
signingBundle *certutil.ParsedCertBundle,
|
||||
isCA bool,
|
||||
req *logical.Request,
|
||||
data *framework.FieldData) (*certCreationBundle, error) {
|
||||
var err error
|
||||
|
||||
// Get the common name(s)
|
||||
var commonNames []string
|
||||
cn := data.Get("common_name").(string)
|
||||
if len(cn) == 0 {
|
||||
return nil, certutil.UserError{Err: "The common_name field is required"}
|
||||
var cn string
|
||||
cnInt, ok := data.GetOk("common_name")
|
||||
if ok {
|
||||
cn = cnInt.(string)
|
||||
}
|
||||
commonNames = []string{cn}
|
||||
|
||||
cnAlt := data.Get("alt_names").(string)
|
||||
if len(cnAlt) != 0 {
|
||||
for _, v := range strings.Split(cnAlt, ",") {
|
||||
commonNames = append(commonNames, v)
|
||||
commonNames := []string{cn}
|
||||
cnAltInt, ok := data.GetOk("alt_names")
|
||||
if ok {
|
||||
cnAlt := cnAltInt.(string)
|
||||
if len(cnAlt) != 0 {
|
||||
for _, v := range strings.Split(cnAlt, ",") {
|
||||
commonNames = append(commonNames, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get any IP SANs
|
||||
ipSANs := []net.IP{}
|
||||
|
||||
ipAlt := data.Get("ip_sans").(string)
|
||||
if len(ipAlt) != 0 {
|
||||
if !role.AllowIPSANs {
|
||||
return nil, certutil.UserError{Err: fmt.Sprintf(
|
||||
"IP Subject Alternative Names are not allowed in this role, but was provided %s", ipAlt)}
|
||||
}
|
||||
for _, v := range strings.Split(ipAlt, ",") {
|
||||
parsedIP := net.ParseIP(v)
|
||||
if parsedIP == nil {
|
||||
ipAltInt, ok := data.GetOk("ip_sans")
|
||||
if ok {
|
||||
ipAlt := ipAltInt.(string)
|
||||
if len(ipAlt) != 0 {
|
||||
if !role.AllowIPSANs {
|
||||
return nil, certutil.UserError{Err: fmt.Sprintf(
|
||||
"the value '%s' is not a valid IP address", v)}
|
||||
"IP Subject Alternative Names are not allowed in this role, but was provided %s", ipAlt)}
|
||||
}
|
||||
for _, v := range strings.Split(ipAlt, ",") {
|
||||
parsedIP := net.ParseIP(v)
|
||||
if parsedIP == nil {
|
||||
return nil, certutil.UserError{Err: fmt.Sprintf(
|
||||
"the value '%s' is not a valid IP address", v)}
|
||||
}
|
||||
ipSANs = append(ipSANs, parsedIP)
|
||||
}
|
||||
ipSANs = append(ipSANs, parsedIP)
|
||||
}
|
||||
}
|
||||
|
||||
ttlField := data.Get("ttl").(string)
|
||||
if len(ttlField) == 0 {
|
||||
var ttlField string
|
||||
ttlFieldInt, ok := data.GetOk("ttl")
|
||||
if !ok {
|
||||
ttlField = role.TTL
|
||||
} else {
|
||||
ttlField = ttlFieldInt.(string)
|
||||
}
|
||||
|
||||
var ttl time.Duration
|
||||
@@ -340,11 +362,13 @@ func generateCreationBundle(b *backend,
|
||||
Usage: usage,
|
||||
}
|
||||
|
||||
if _, ok := req.Data["ca_type"]; ok {
|
||||
creationBundle.CAType = req.Data["ca_type"].(string)
|
||||
}
|
||||
if _, ok := req.Data["pki_address"]; ok {
|
||||
creationBundle.PKIAddress = req.Data["pki_address"].(string)
|
||||
if isCA {
|
||||
if _, ok := req.Data["ca_type"]; ok {
|
||||
creationBundle.CAType = req.Data["ca_type"].(string)
|
||||
}
|
||||
if _, ok := req.Data["pki_address"]; ok {
|
||||
creationBundle.PKIAddress = req.Data["pki_address"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
return creationBundle, nil
|
||||
|
||||
@@ -14,43 +14,7 @@ import (
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
var generateAndSignSchema = map[string]*framework.FieldSchema{
|
||||
"type": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `Must be "self-signed" or "intermediate".
|
||||
If set to "self-signed", a self-signed root CA
|
||||
will be generated. If set to "intermediate", a
|
||||
CSR will be returned for signing by the root.`,
|
||||
},
|
||||
|
||||
"exported": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `Must be "internal" or "exported".
|
||||
If set to "exported", the generated private
|
||||
key will be returned. This is your *only*
|
||||
chance to retrieve the private key!`,
|
||||
},
|
||||
|
||||
"pki_address": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The base URL of the PKI mount. For
|
||||
"self-signed"; this is needed when
|
||||
calling '/generate/' and should be
|
||||
the base URL of the mount where you
|
||||
are running this command, e.g.
|
||||
"https://vault.example.com/v1/root_pki".
|
||||
For "intermediate", this is needed
|
||||
when calling '/sign/' and should be
|
||||
the base URL of the destination
|
||||
mount of the certificate, e.g.
|
||||
"https://vault.example.com/v1/intermediate_pki".
|
||||
For HA setups, the given host name
|
||||
should be the address that can always
|
||||
be used to contact the leader. This is
|
||||
is used for generating the CA/CRL URLs in
|
||||
the certificate. Required as specified.`,
|
||||
},
|
||||
|
||||
var rootAndSignSchema = map[string]*framework.FieldSchema{
|
||||
"common_name": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested common name; if you want more than
|
||||
@@ -70,26 +34,16 @@ in a comma-delimited list`,
|
||||
common-delimited list`,
|
||||
},
|
||||
|
||||
"key_bits": &framework.FieldSchema{
|
||||
Type: framework.TypeInt,
|
||||
Default: 2048,
|
||||
Description: `The number of bits to use. You will almost
|
||||
certainly want to change this if you adjust
|
||||
the key_type.`,
|
||||
},
|
||||
|
||||
"ttl": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested Time To Live for the certificate;
|
||||
sets the expiration date. If not specified
|
||||
the role default, backend default, or system
|
||||
default TTL is used, in that order. Cannot
|
||||
be larger than the mount max TTL.`,
|
||||
},
|
||||
|
||||
"csr": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `PEM-format CSR to be signed.`,
|
||||
be larger than the mount max TTL. Note:
|
||||
this only has an effect when generating
|
||||
a CA cert or signing a CA cert, not when
|
||||
creating a CSR for an intermediate CA.`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -134,26 +88,88 @@ endpoint, just the signed certificate.`,
|
||||
}
|
||||
}
|
||||
|
||||
func pathGenerateCA(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "config/ca/generate/" + framework.GenericNameRegex("type") + "/" + framework.GenericNameRegex("exported"),
|
||||
func pathGenerateRootCA(b *backend) *framework.Path {
|
||||
ret := &framework.Path{
|
||||
Pattern: "config/ca/generate/root/" + framework.GenericNameRegex("exported"),
|
||||
|
||||
Fields: generateAndSignSchema,
|
||||
Fields: rootAndSignSchema,
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.WriteOperation: b.pathCAGenerateWrite,
|
||||
logical.WriteOperation: b.pathCAGenerateRoot,
|
||||
},
|
||||
|
||||
HelpSynopsis: pathConfigCAGenerateHelpSyn,
|
||||
HelpDescription: pathConfigCAGenerateHelpDesc,
|
||||
}
|
||||
|
||||
ret.Fields["pki_address"] = &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The base URL of the PKI mount, e.g.
|
||||
"https://vault.example.com/v1/root_pki".
|
||||
For HA setups, the given host name
|
||||
should be the address that can always
|
||||
be used to contact the leader, as this is
|
||||
is used for generating the CA/CRL URLs in
|
||||
the certificate.`,
|
||||
}
|
||||
|
||||
ret.Fields["exported"] = &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `Must be "internal" or "exported".
|
||||
If set to "exported", the generated private
|
||||
key will be returned. This is your *only*
|
||||
chance to retrieve the private key!`,
|
||||
}
|
||||
|
||||
ret.Fields["key_bits"] = &framework.FieldSchema{
|
||||
Type: framework.TypeInt,
|
||||
Default: 2048,
|
||||
Description: `The number of bits to use. You will almost
|
||||
certainly want to change this if you adjust
|
||||
the key_type.`,
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func pathSignCA(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
func pathGenerateIntermediateCA(b *backend) *framework.Path {
|
||||
ret := &framework.Path{
|
||||
Pattern: "config/ca/generate/intermediate/" + framework.GenericNameRegex("exported"),
|
||||
|
||||
Fields: map[string]*framework.FieldSchema{},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.WriteOperation: b.pathCAGenerateIntermediate,
|
||||
},
|
||||
|
||||
HelpSynopsis: pathConfigCAGenerateHelpSyn,
|
||||
HelpDescription: pathConfigCAGenerateHelpDesc,
|
||||
}
|
||||
|
||||
ret.Fields["exported"] = &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `Must be "internal" or "exported".
|
||||
If set to "exported", the generated private
|
||||
key will be returned. This is your *only*
|
||||
chance to retrieve the private key!`,
|
||||
}
|
||||
|
||||
ret.Fields["key_bits"] = &framework.FieldSchema{
|
||||
Type: framework.TypeInt,
|
||||
Default: 2048,
|
||||
Description: `The number of bits to use. You will almost
|
||||
certainly want to change this if you adjust
|
||||
the key_type.`,
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func pathSignIntermediateCA(b *backend) *framework.Path {
|
||||
ret := &framework.Path{
|
||||
Pattern: "config/ca/sign",
|
||||
|
||||
Fields: generateAndSignSchema,
|
||||
Fields: rootAndSignSchema,
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.WriteOperation: b.pathCASignWrite,
|
||||
@@ -162,9 +178,28 @@ func pathSignCA(b *backend) *framework.Path {
|
||||
HelpSynopsis: pathConfigCASignHelpSyn,
|
||||
HelpDescription: pathConfigCASignHelpDesc,
|
||||
}
|
||||
|
||||
ret.Fields["pki_address"] = &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The base URL of the *destination*
|
||||
PKI mount, e.g.
|
||||
"https://vault.example.com/v1/intermediate_pki".
|
||||
For HA setups, the given host name
|
||||
should be the address that can always
|
||||
be used to contact the leader, as this is
|
||||
is used for generating the CA/CRL URLs in
|
||||
the certificate.`,
|
||||
}
|
||||
|
||||
ret.Fields["csr"] = &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `PEM-format CSR to be signed.`,
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *backend) pathCAGenerateWrite(
|
||||
func (b *backend) pathCAGenerateRoot(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
var err error
|
||||
exported := data.Get("exported").(string)
|
||||
@@ -176,39 +211,28 @@ func (b *backend) pathCAGenerateWrite(
|
||||
"The \"exported\" path parameter must be \"internal\" or \"exported\"")), nil
|
||||
}
|
||||
|
||||
genType := data.Get("type").(string)
|
||||
switch genType {
|
||||
case "self-signed":
|
||||
case "intermediate":
|
||||
default:
|
||||
req.Data["ca_type"] = "root"
|
||||
|
||||
pkiAddress := strings.ToLower(data.Get("pki_address").(string))
|
||||
switch {
|
||||
case len(pkiAddress) == 0:
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"The \"type\" path parameter must be \"self-signed\" or \"intermediate\"")), nil
|
||||
"\"pki_address\" cannot be empty")), nil
|
||||
case !strings.HasPrefix(pkiAddress, "http"):
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"\"pki_address\" must be a URL")), nil
|
||||
case !strings.Contains(pkiAddress, "/v1/"):
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"\"pki_address\" needs to be the path to the PKI mount, not the base Vault path")), nil
|
||||
case !strings.Contains(pkiAddress, "/v1/"+req.MountPoint[:len(req.MountPoint)-1]):
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"\"pki_address\" needs to be the path to this mount")), nil
|
||||
}
|
||||
if strings.HasSuffix(pkiAddress, "/") {
|
||||
pkiAddress = pkiAddress[:len(pkiAddress)-1]
|
||||
}
|
||||
|
||||
req.Data["ca_type"] = genType
|
||||
|
||||
if genType == "self-signed" {
|
||||
pkiAddress := strings.ToLower(data.Get("pki_address").(string))
|
||||
switch {
|
||||
case len(pkiAddress) == 0:
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"\"pki_address\" cannot be empty")), nil
|
||||
case !strings.HasPrefix(pkiAddress, "http"):
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"\"pki_address\" must be a URL")), nil
|
||||
case !strings.Contains(pkiAddress, "/v1/"):
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"\"pki_address\" needs to be the path to the PKI mount, not the base Vault path")), nil
|
||||
case !strings.Contains(pkiAddress, "/v1/"+req.MountPoint[:len(req.MountPoint)-1]):
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"\"pki_address\" needs to be the path to this mount")), nil
|
||||
}
|
||||
if strings.HasSuffix(pkiAddress, "/") {
|
||||
pkiAddress = pkiAddress[:len(pkiAddress)-1]
|
||||
}
|
||||
|
||||
req.Data["pki_address"] = pkiAddress
|
||||
}
|
||||
req.Data["pki_address"] = pkiAddress
|
||||
|
||||
role := &roleEntry{
|
||||
TTL: data.Get("ttl").(string),
|
||||
@@ -219,21 +243,6 @@ func (b *backend) pathCAGenerateWrite(
|
||||
EnforceHostnames: false,
|
||||
}
|
||||
|
||||
maxSystemTTL := b.System().MaxLeaseTTL()
|
||||
|
||||
ttl := b.System().DefaultLeaseTTL()
|
||||
if len(role.TTL) != 0 {
|
||||
ttl, err = time.ParseDuration(role.TTL)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"Invalid ttl: %s", err)), nil
|
||||
}
|
||||
}
|
||||
if ttl > maxSystemTTL {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"\"ttl\" value must be less than mount max of %d seconds", maxSystemTTL/time.Second)), nil
|
||||
}
|
||||
|
||||
switch role.KeyBits {
|
||||
case 0:
|
||||
role.KeyBits = 2048
|
||||
@@ -246,105 +255,135 @@ func (b *backend) pathCAGenerateWrite(
|
||||
}
|
||||
|
||||
var resp *logical.Response
|
||||
switch genType {
|
||||
case "self-signed":
|
||||
parsedBundle, err := generateCert(b, role, nil, req, data)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case certutil.UserError:
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
case certutil.InternalError:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
cb, err := parsedBundle.ToCertBundle()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error converting raw cert bundle to cert bundle: %s", err)
|
||||
}
|
||||
|
||||
resp = &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"serial_number": cb.SerialNumber,
|
||||
"certificate": cb.Certificate,
|
||||
"issuing_ca": cb.IssuingCA,
|
||||
"expiration": int(parsedBundle.Certificate.NotAfter.Unix()),
|
||||
},
|
||||
}
|
||||
|
||||
if exported == "exported" {
|
||||
resp.Data["private_key"] = cb.PrivateKey
|
||||
resp.Data["private_key_type"] = cb.PrivateKeyType
|
||||
}
|
||||
|
||||
entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = req.Storage.Put(entry)
|
||||
if err != nil {
|
||||
parsedBundle, err := generateCert(b, role, nil, true, req, data)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case certutil.UserError:
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
case certutil.InternalError:
|
||||
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 = parsedBundle.CertificateBytes
|
||||
err = req.Storage.Put(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cb, err := parsedBundle.ToCertBundle()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error converting raw cert bundle to cert bundle: %s", err)
|
||||
}
|
||||
|
||||
entry.Key = "crl"
|
||||
entry.Value = []byte{}
|
||||
err = req.Storage.Put(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp = &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"serial_number": cb.SerialNumber,
|
||||
"certificate": cb.Certificate,
|
||||
"issuing_ca": cb.IssuingCA,
|
||||
"expiration": int(parsedBundle.Certificate.NotAfter.Unix()),
|
||||
},
|
||||
}
|
||||
|
||||
case "intermediate":
|
||||
parsedBundle, err := generateCSR(b, role, nil, req, data)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case certutil.UserError:
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
case certutil.InternalError:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if exported == "exported" {
|
||||
resp.Data["private_key"] = cb.PrivateKey
|
||||
resp.Data["private_key_type"] = cb.PrivateKeyType
|
||||
}
|
||||
|
||||
csrb, err := parsedBundle.ToCSRBundle()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error converting raw CSR bundle to CSR 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
|
||||
}
|
||||
|
||||
resp = &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"csr": csrb.CSR,
|
||||
},
|
||||
}
|
||||
// For ease of later use, also store just the certificate at a known
|
||||
// location, plus a blank CRL
|
||||
entry.Key = "ca"
|
||||
entry.Value = parsedBundle.CertificateBytes
|
||||
err = req.Storage.Put(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exported == "exported" {
|
||||
resp.Data["private_key"] = csrb.PrivateKey
|
||||
resp.Data["private_key_type"] = csrb.PrivateKeyType
|
||||
}
|
||||
entry.Key = "crl"
|
||||
entry.Value = []byte{}
|
||||
err = req.Storage.Put(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cb := &certutil.CertBundle{
|
||||
PrivateKey: csrb.PrivateKey,
|
||||
PrivateKeyType: csrb.PrivateKeyType,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathCAGenerateIntermediate(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
var err error
|
||||
exported := data.Get("exported").(string)
|
||||
switch exported {
|
||||
case "exported":
|
||||
case "internal":
|
||||
default:
|
||||
return logical.ErrorResponse("Unknown generation type"), nil
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"The \"exported\" path parameter must be \"internal\" or \"exported\"")), nil
|
||||
}
|
||||
|
||||
req.Data["ca_type"] = "intermediate"
|
||||
|
||||
role := &roleEntry{
|
||||
KeyType: "rsa",
|
||||
KeyBits: data.Get("key_bits").(int),
|
||||
AllowLocalhost: true,
|
||||
AllowAnyName: true,
|
||||
EnforceHostnames: false,
|
||||
}
|
||||
|
||||
switch role.KeyBits {
|
||||
case 0:
|
||||
role.KeyBits = 2048
|
||||
case 1024:
|
||||
case 2048:
|
||||
case 4096:
|
||||
default:
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"\"key_bits\" must be 1024, 2048, or 4096")), nil
|
||||
}
|
||||
|
||||
var resp *logical.Response
|
||||
parsedBundle, err := generateCSR(b, role, nil, true, req, data)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case certutil.UserError:
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
case certutil.InternalError:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
csrb, err := parsedBundle.ToCSRBundle()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error converting raw CSR bundle to CSR bundle: %s", err)
|
||||
}
|
||||
|
||||
resp = &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"csr": csrb.CSR,
|
||||
},
|
||||
}
|
||||
|
||||
if exported == "exported" {
|
||||
resp.Data["private_key"] = csrb.PrivateKey
|
||||
resp.Data["private_key_type"] = csrb.PrivateKeyType
|
||||
}
|
||||
|
||||
cb := &certutil.CertBundle{
|
||||
PrivateKey: csrb.PrivateKey,
|
||||
PrivateKeyType: csrb.PrivateKeyType,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
@@ -425,7 +464,7 @@ func (b *backend) pathCASignWrite(
|
||||
"Error fetching CA certificate: %s", caErr)}
|
||||
}
|
||||
|
||||
parsedBundle, err := signCert(b, role, signingBundle, csr, req, data)
|
||||
parsedBundle, err := signCert(b, role, signingBundle, csr, true, req, data)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case certutil.UserError:
|
||||
|
||||
@@ -10,45 +10,65 @@ import (
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
func pathIssue(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "issue/" + framework.GenericNameRegex("role"),
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"role": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The desired role with configuration for this
|
||||
var issueAndSignSchema = map[string]*framework.FieldSchema{
|
||||
"role": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The desired role with configuration for this
|
||||
request`,
|
||||
},
|
||||
},
|
||||
|
||||
"common_name": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested common name; if you want more than
|
||||
"common_name": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested common name; if you want more than
|
||||
one, specify the alternative names in the
|
||||
alt_names map`,
|
||||
},
|
||||
},
|
||||
|
||||
"alt_names": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested Subject Alternative Names, if any,
|
||||
"alt_names": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested Subject Alternative Names, if any,
|
||||
in a comma-delimited list`,
|
||||
},
|
||||
},
|
||||
|
||||
"ip_sans": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested IP SANs, if any, in a
|
||||
"ip_sans": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested IP SANs, if any, in a
|
||||
common-delimited list`,
|
||||
},
|
||||
},
|
||||
|
||||
"ttl": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested Time To Live for the certificate;
|
||||
"ttl": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `The requested Time To Live for the certificate;
|
||||
sets the expiration date. If not specified
|
||||
the role default, backend default, or system
|
||||
default TTL is used, in that order. Cannot
|
||||
be later than the role max TTL.`,
|
||||
},
|
||||
},
|
||||
"csr": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `PEM-format CSR to be signed.`,
|
||||
},
|
||||
}
|
||||
|
||||
func pathIssue(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "issue/" + framework.GenericNameRegex("role"),
|
||||
Fields: issueAndSignSchema,
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.WriteOperation: b.pathIssueCert,
|
||||
},
|
||||
|
||||
HelpSynopsis: pathIssueCertHelpSyn,
|
||||
HelpDescription: pathIssueCertHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
func pathSign(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "sign/" + framework.GenericNameRegex("role"),
|
||||
Fields: issueAndSignSchema,
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.WriteOperation: b.pathIssueCert,
|
||||
},
|
||||
@@ -82,13 +102,7 @@ func (b *backend) pathIssueCert(
|
||||
"Error fetching CA certificate: %s", caErr)}
|
||||
}
|
||||
|
||||
// Don't allow these on the standard path. Ideally we should determine
|
||||
// this internally once we get SudoPrivilege from System() working
|
||||
// for non-TokenStore
|
||||
delete(req.Data, "ca_type")
|
||||
delete(req.Data, "pki_address")
|
||||
|
||||
parsedBundle, err := generateCert(b, role, signingBundle, req, data)
|
||||
parsedBundle, err := generateCert(b, role, signingBundle, false, req, data)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case certutil.UserError:
|
||||
|
||||
Reference in New Issue
Block a user