diff --git a/builtin/logical/pki/acme_wrappers.go b/builtin/logical/pki/acme_wrappers.go index 6a45f578e..ba07f4419 100644 --- a/builtin/logical/pki/acme_wrappers.go +++ b/builtin/logical/pki/acme_wrappers.go @@ -19,6 +19,8 @@ type acmeContext struct { // baseUrl is the combination of the configured cluster local URL and the acmePath up to /acme/ baseUrl *url.URL sc *storageContext + role *roleEntry + issuer *issuerEntry } type ( @@ -47,20 +49,99 @@ func (b *backend) acmeWrapper(op acmeOperation) framework.OperationFunc { return nil, fmt.Errorf("ACME is disabled in configuration: %w", ErrServerInternal) } + if b.useLegacyBundleCaStorage() { + return nil, fmt.Errorf("%w: Can not perform ACME operations until migration has completed", ErrServerInternal) + } + acmeBaseUrl, err := getAcmeBaseUrl(sc, r.Path) if err != nil { return nil, err } + role, issuer, err := getAcmeRoleAndIssuer(sc, data) + if err != nil { + return nil, err + } + acmeCtx := &acmeContext{ baseUrl: acmeBaseUrl, sc: sc, + role: role, + issuer: issuer, } return op(acmeCtx, r, data) }) } +func getAcmeIssuer(sc *storageContext, issuerName string) (*issuerEntry, error) { + issuerId, err := sc.resolveIssuerReference(issuerName) + if err != nil { + return nil, fmt.Errorf("%w: issuer does not exist", ErrMalformed) + } + + issuer, err := sc.fetchIssuerById(issuerId) + if err != nil { + return nil, fmt.Errorf("issuer failed to load: %w", err) + } + + if issuer.Usage.HasUsage(IssuanceUsage) && len(issuer.KeyID) > 0 { + return issuer, nil + } + + return nil, fmt.Errorf("%w: issuer missing proper issuance usage or key", ErrServerInternal) +} + +func getAcmeRoleAndIssuer(sc *storageContext, data *framework.FieldData) (*roleEntry, *issuerEntry, error) { + requestedIssuer := defaultRef + requestedIssuerRaw, present := data.GetOk("issuer") + if present { + requestedIssuer = requestedIssuerRaw.(string) + } + + var role *roleEntry + roleNameRaw, present := data.GetOk("role") + if present { + roleName := roleNameRaw.(string) + if len(roleName) > 0 { + var err error + role, err = sc.Backend.getRole(sc.Context, sc.Storage, roleName) + if err != nil { + return nil, nil, fmt.Errorf("%w: err loading role", ErrServerInternal) + } + + if role == nil { + return nil, nil, fmt.Errorf("%w: role does not exist", ErrMalformed) + } + + if role.NoStore { + return nil, nil, fmt.Errorf("%w: role can not be used as NoStore is set to true", ErrServerInternal) + } + + if len(role.Issuer) > 0 { + requestedIssuer = role.Issuer + } + } + } else { + role = buildSignVerbatimRoleWithNoData(&roleEntry{ + Issuer: requestedIssuer, + NoStore: false, + Name: "", + }) + } + + issuer, err := getAcmeIssuer(sc, requestedIssuer) + if err != nil { + return nil, nil, err + } + + role.Issuer = requestedIssuer + + // TODO: Need additional configuration validation here, for allowed roles/issuers. + + return role, issuer, nil +} + func (b *backend) acmeParsedWrapper(op acmeParsedOperation) framework.OperationFunc { return b.acmeWrapper(func(acmeCtx *acmeContext, r *logical.Request, fields *framework.FieldData) (*logical.Response, error) { user, data, err := b.acmeState.ParseRequestParams(acmeCtx, fields) diff --git a/builtin/logical/pki/acme_wrappers_test.go b/builtin/logical/pki/acme_wrappers_test.go new file mode 100644 index 000000000..ee5517680 --- /dev/null +++ b/builtin/logical/pki/acme_wrappers_test.go @@ -0,0 +1,114 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package pki + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/logical" + "github.com/stretchr/testify/require" +) + +// TestACMEIssuerRoleLoading validates the role and issuer loading logic within the base +// ACME wrapper is correct. +func TestACMEIssuerRoleLoading(t *testing.T) { + b, s := CreateBackendWithStorage(t) + + _, err := CBWrite(b, s, "config/cluster", map[string]interface{}{ + "path": "http://localhost:8200/v1/pki", + "aia_path": "http://localhost:8200/cdn/pki", + }) + require.NoError(t, err) + + _, err = CBWrite(b, s, "root/generate/internal", map[string]interface{}{ + "common_name": "myvault1.com", + "issuer_name": "issuer-1", + "key_type": "ec", + }) + require.NoError(t, err, "failed creating issuer issuer-1") + + _, err = CBWrite(b, s, "root/generate/internal", map[string]interface{}{ + "common_name": "myvault2.com", + "issuer_name": "issuer-2", + "key_type": "ec", + }) + require.NoError(t, err, "failed creating issuer issuer-2") + + _, err = CBWrite(b, s, "roles/role-bad-issuer", map[string]interface{}{ + issuerRefParam: "non-existant", + "no_store": "false", + }) + require.NoError(t, err, "failed creating role role-bad-issuer") + + _, err = CBWrite(b, s, "roles/role-no-store-enabled", map[string]interface{}{ + issuerRefParam: "issuer-2", + "no_store": "true", + }) + require.NoError(t, err, "failed creating role role-no-store-enabled") + + _, err = CBWrite(b, s, "roles/role-issuer-2", map[string]interface{}{ + issuerRefParam: "issuer-2", + "no_store": "false", + }) + require.NoError(t, err, "failed creating role role-issuer-2") + + tc := []struct { + name string + roleName string + issuerName string + expectedIssuerName string + expectErr bool + }{ + {name: "pass-default-use-default", roleName: "", issuerName: "", expectedIssuerName: "issuer-1", expectErr: false}, + {name: "pass-role-issuer-2", roleName: "role-issuer-2", issuerName: "", expectedIssuerName: "issuer-2", expectErr: false}, + {name: "pass-issuer-1-no-role", roleName: "", issuerName: "issuer-1", expectedIssuerName: "issuer-1", expectErr: false}, + {name: "fail-role-has-bad-issuer", roleName: "role-bad-issuer", issuerName: "", expectedIssuerName: "", expectErr: true}, + {name: "fail-role-no-store-enabled", roleName: "role-no-store-enabled", issuerName: "", expectedIssuerName: "", expectErr: true}, + {name: "fail-role-no-store-enabled", roleName: "role-no-store-enabled", issuerName: "", expectedIssuerName: "", expectErr: true}, + {name: "fail-role-does-not-exist", roleName: "non-existant", issuerName: "", expectedIssuerName: "", expectErr: true}, + {name: "fail-issuer-does-not-exist", roleName: "", issuerName: "non-existant", expectedIssuerName: "", expectErr: true}, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + f := b.acmeWrapper(func(acmeCtx *acmeContext, r *logical.Request, _ *framework.FieldData) (*logical.Response, error) { + if tt.roleName != acmeCtx.role.Name { + return nil, fmt.Errorf("expected role %s but got %s", tt.roleName, acmeCtx.role.Name) + } + + if tt.expectedIssuerName != acmeCtx.issuer.Name { + return nil, fmt.Errorf("expected issuer %s but got %s", tt.expectedIssuerName, acmeCtx.issuer.Name) + } + + return nil, nil + }) + + fieldRaw := map[string]interface{}{} + if tt.roleName != "" { + fieldRaw["role"] = tt.roleName + } + if tt.issuerName != "" { + fieldRaw[issuerRefParam] = tt.issuerName + } + + resp, err := f(context.Background(), &logical.Request{Storage: s}, &framework.FieldData{ + Raw: fieldRaw, + Schema: getCsrSignVerbatimSchemaFields(), + }) + require.NoError(t, err, "all errors should be re-encoded") + + if tt.expectErr { + require.NotEqual(t, 200, resp.Data[logical.HTTPStatusCode]) + require.Equal(t, ErrorContentType, resp.Data[logical.HTTPContentType]) + } else { + if resp != nil { + t.Fatalf("expected no error got %s", string(resp.Data[logical.HTTPRawBody].([]uint8))) + } + } + }) + } +} diff --git a/builtin/logical/pki/ca_util.go b/builtin/logical/pki/ca_util.go index 129c6a923..85dc243e5 100644 --- a/builtin/logical/pki/ca_util.go +++ b/builtin/logical/pki/ca_util.go @@ -264,3 +264,61 @@ func existingKeyGeneratorFromBytes(key *keyEntry) certutil.KeyGenerator { return nil } } + +func buildSignVerbatimRoleWithNoData(role *roleEntry) *roleEntry { + data := &framework.FieldData{ + Raw: map[string]interface{}{}, + Schema: addSignVerbatimRoleFields(map[string]*framework.FieldSchema{}), + } + return buildSignVerbatimRole(data, role) +} + +func buildSignVerbatimRole(data *framework.FieldData, role *roleEntry) *roleEntry { + entry := &roleEntry{ + AllowLocalhost: true, + AllowAnyName: true, + AllowIPSANs: true, + AllowWildcardCertificates: new(bool), + EnforceHostnames: false, + KeyType: "any", + UseCSRCommonName: true, + UseCSRSANs: true, + AllowedOtherSANs: []string{"*"}, + AllowedSerialNumbers: []string{"*"}, + AllowedURISANs: []string{"*"}, + AllowedUserIDs: []string{"*"}, + CNValidations: []string{"disabled"}, + GenerateLease: new(bool), + // If adding new fields to be read, update the field list within addSignVerbatimRoleFields + KeyUsage: data.Get("key_usage").([]string), + ExtKeyUsage: data.Get("ext_key_usage").([]string), + ExtKeyUsageOIDs: data.Get("ext_key_usage_oids").([]string), + SignatureBits: data.Get("signature_bits").(int), + UsePSS: data.Get("use_pss").(bool), + } + *entry.AllowWildcardCertificates = true + *entry.GenerateLease = false + + if role != nil { + if role.TTL > 0 { + entry.TTL = role.TTL + } + if role.MaxTTL > 0 { + entry.MaxTTL = role.MaxTTL + } + if role.GenerateLease != nil { + *entry.GenerateLease = *role.GenerateLease + } + if role.NotBeforeDuration > 0 { + entry.NotBeforeDuration = role.NotBeforeDuration + } + entry.NoStore = role.NoStore + entry.Issuer = role.Issuer + } + + if len(entry.Issuer) == 0 { + entry.Issuer = defaultRef + } + + return entry +} diff --git a/builtin/logical/pki/fields.go b/builtin/logical/pki/fields.go index 9e6201d2c..4a2c486b9 100644 --- a/builtin/logical/pki/fields.go +++ b/builtin/logical/pki/fields.go @@ -565,3 +565,72 @@ primary node.`, return fields } + +// generate the entire list of schema fields we need for CSR sign verbatim, this is also +// leveraged by ACME internally. +func getCsrSignVerbatimSchemaFields() map[string]*framework.FieldSchema { + fields := map[string]*framework.FieldSchema{} + fields = addNonCACommonFields(fields) + fields = addSignVerbatimRoleFields(fields) + + fields["csr"] = &framework.FieldSchema{ + Type: framework.TypeString, + Default: "", + Description: `PEM-format CSR to be signed. Values will be +taken verbatim from the CSR, except for +basic constraints.`, + } + + return fields +} + +// addSignVerbatimRoleFields provides the fields and defaults to be used by anything that is building up the fields +// and their corresponding default values when generating/using a sign-verbatim type role such as buildSignVerbatimRole. +func addSignVerbatimRoleFields(fields map[string]*framework.FieldSchema) map[string]*framework.FieldSchema { + fields["key_usage"] = &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Default: []string{"DigitalSignature", "KeyAgreement", "KeyEncipherment"}, + Description: `A comma-separated string or list of key usages (not extended +key usages). Valid values can be found at +https://golang.org/pkg/crypto/x509/#KeyUsage +-- simply drop the "KeyUsage" part of the name. +To remove all key usages from being set, set +this value to an empty list.`, + } + + fields["ext_key_usage"] = &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Default: []string{}, + Description: `A comma-separated string or list of extended key usages. Valid values can be found at +https://golang.org/pkg/crypto/x509/#ExtKeyUsage +-- simply drop the "ExtKeyUsage" part of the name. +To remove all key usages from being set, set +this value to an empty list.`, + } + + fields["ext_key_usage_oids"] = &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: `A comma-separated string or list of extended key usage oids.`, + } + + fields["signature_bits"] = &framework.FieldSchema{ + Type: framework.TypeInt, + Default: 0, + Description: `The number of bits to use in the signature +algorithm; accepts 256 for SHA-2-256, 384 for SHA-2-384, and 512 for +SHA-2-512. Defaults to 0 to automatically detect based on key length +(SHA-2-256 for RSA keys, and matching the curve size for NIST P-Curves).`, + DisplayAttrs: &framework.DisplayAttributes{ + Value: 0, + }, + } + + fields["use_pss"] = &framework.FieldSchema{ + Type: framework.TypeBool, + Default: false, + Description: `Whether or not to use PSS signatures when using a +RSA key-type issuer. Defaults to false.`, + } + + return fields +} diff --git a/builtin/logical/pki/path_acme_order.go b/builtin/logical/pki/path_acme_order.go index b187700c9..b1f2ef8d5 100644 --- a/builtin/logical/pki/path_acme_order.go +++ b/builtin/logical/pki/path_acme_order.go @@ -404,31 +404,6 @@ func storeCertificate(sc *storageContext, signedCertBundle *certutil.ParsedCertB } func issueCertFromCsr(ac *acmeContext, csr *x509.CertificateRequest) (*certutil.ParsedCertBundle, issuerID, error) { - entry := &roleEntry{ - AllowLocalhost: true, - AllowAnyName: true, - AllowIPSANs: true, - AllowWildcardCertificates: new(bool), - EnforceHostnames: false, - KeyType: "any", - UseCSRCommonName: true, - UseCSRSANs: true, - AllowedOtherSANs: []string{"*"}, - AllowedSerialNumbers: []string{"*"}, - AllowedURISANs: []string{"*"}, - AllowedUserIDs: []string{"*"}, - CNValidations: []string{"disabled"}, - GenerateLease: new(bool), - KeyUsage: []string{}, - ExtKeyUsage: []string{}, - ExtKeyUsageOIDs: []string{}, - SignatureBits: 0, - UsePSS: false, - Issuer: defaultRef, - } - *entry.AllowWildcardCertificates = true - *entry.GenerateLease = false - pemBlock := &pem.Block{ Type: "CERTIFICATE REQUEST", Headers: nil, @@ -436,27 +411,22 @@ func issueCertFromCsr(ac *acmeContext, csr *x509.CertificateRequest) (*certutil. } pemCsr := string(pem.EncodeToMemory(pemBlock)) - signingBundle, issuerId, err := ac.sc.fetchCAInfoWithIssuer(entry.Issuer, IssuanceUsage) + data := &framework.FieldData{ + Raw: map[string]interface{}{ + "csr": pemCsr, + }, + Schema: getCsrSignVerbatimSchemaFields(), + } + + signingBundle, issuerId, err := ac.sc.fetchCAInfoWithIssuer(ac.issuer.ID.String(), IssuanceUsage) if err != nil { - return nil, "", fmt.Errorf("failed loading CA %s: %w", entry.Issuer, err) + return nil, "", fmt.Errorf("failed loading CA %s: %w", ac.issuer.ID.String(), err) } input := &inputBundle{ - req: &logical.Request{}, - apiData: &framework.FieldData{ - Raw: map[string]interface{}{ - "csr": pemCsr, - "ttl": "1h", - }, - Schema: map[string]*framework.FieldSchema{ - "csr": {Type: framework.TypeString}, - "serial_number": {Type: framework.TypeString}, - "exclude_cn_from_sans": {Type: framework.TypeBool}, - "other_sans": {Type: framework.TypeCommaStringSlice}, - "ttl": {Type: framework.TypeDurationSecond}, - }, - }, - role: entry, + req: &logical.Request{}, + apiData: data, + role: ac.role, } if csr.PublicKeyAlgorithm == x509.UnknownPublicKeyAlgorithm || csr.PublicKey == nil { diff --git a/builtin/logical/pki/path_acme_test.go b/builtin/logical/pki/path_acme_test.go index 8826dad02..67c4057d0 100644 --- a/builtin/logical/pki/path_acme_test.go +++ b/builtin/logical/pki/path_acme_test.go @@ -384,19 +384,22 @@ func setupAcmeBackend(t *testing.T) (*vault.TestCluster, *api.Client, string) { // Allow certain headers to pass through for ACME support _, err = client.Logical().WriteWithContext(context.Background(), "sys/mounts/pki/tune", map[string]interface{}{ "allowed_response_headers": []string{"Last-Modified", "Replay-Nonce", "Link", "Location"}, + "max_lease_ttl": "920000h", }) require.NoError(t, err, "failed tuning mount response headers") - _, err = client.Logical().WriteWithContext(context.Background(), "/pki/issuers/generate/root/internal", map[string]interface{}{ - "issuer_name": "root-ca", - "key_name": "root-key", - "key_type": "ec", - "common_name": "root.com", - "ttl": "10h", - }) + resp, err := client.Logical().WriteWithContext(context.Background(), "/pki/issuers/generate/root/internal", + map[string]interface{}{ + "issuer_name": "root-ca", + "key_name": "root-key", + "key_type": "ec", + "common_name": "root.com", + "ttl": "7200h", + "max_ttl": "920000h", + }) require.NoError(t, err, "failed creating root CA") - resp, err := client.Logical().WriteWithContext(context.Background(), "/pki/issuers/generate/intermediate/internal", + resp, err = client.Logical().WriteWithContext(context.Background(), "/pki/issuers/generate/intermediate/internal", map[string]interface{}{ "key_name": "int-key", "key_type": "ec", @@ -407,8 +410,9 @@ func setupAcmeBackend(t *testing.T) (*vault.TestCluster, *api.Client, string) { // Sign the intermediate CSR using /pki resp, err = client.Logical().Write("pki/issuer/root-ca/sign-intermediate", map[string]interface{}{ - "csr": intermediateCSR, - "ttl": "5h", + "csr": intermediateCSR, + "ttl": "720h", + "max_ttl": "7200h", }) require.NoError(t, err, "failed signing intermediary CSR") intermediateCertPEM := resp.Data["certificate"].(string) @@ -425,10 +429,26 @@ func setupAcmeBackend(t *testing.T) (*vault.TestCluster, *api.Client, string) { _, err = client.Logical().Write("/pki/issuer/"+intCaUuid, map[string]interface{}{ "issuer_name": "int-ca", }) + require.NoError(t, err, "failed updating issuer name") _, err = client.Logical().Write("/pki/config/issuers", map[string]interface{}{ "default": "int-ca", }) + require.NoError(t, err, "failed updating default issuer") + + _, err = client.Logical().Write("/pki/roles/test-role", map[string]interface{}{ + "ttl_duration": "365h", + "max_ttl_duration": "720h", + "key_type": "any", + }) + require.NoError(t, err, "failed creating role test-role") + + _, err = client.Logical().Write("/pki/roles/acme", map[string]interface{}{ + "ttl_duration": "365h", + "max_ttl_duration": "720h", + "key_type": "any", + }) + require.NoError(t, err, "failed creating role acme") return cluster, client, pathConfig } diff --git a/builtin/logical/pki/path_issue_sign.go b/builtin/logical/pki/path_issue_sign.go index ed494a0ab..388e80cd2 100644 --- a/builtin/logical/pki/path_issue_sign.go +++ b/builtin/logical/pki/path_issue_sign.go @@ -226,7 +226,7 @@ func buildPathIssuerSignVerbatim(b *backend, pattern string, displayAttrs *frame ret := &framework.Path{ Pattern: pattern, DisplayAttrs: displayAttrs, - Fields: map[string]*framework.FieldSchema{}, + Fields: getCsrSignVerbatimSchemaFields(), Operations: map[logical.Operation]framework.OperationHandler{ logical.UpdateOperation: &framework.PathOperation{ @@ -280,61 +280,6 @@ func buildPathIssuerSignVerbatim(b *backend, pattern string, displayAttrs *frame HelpDescription: pathIssuerSignVerbatimHelpDesc, } - ret.Fields = addNonCACommonFields(ret.Fields) - - ret.Fields["csr"] = &framework.FieldSchema{ - Type: framework.TypeString, - Default: "", - Description: `PEM-format CSR to be signed. Values will be -taken verbatim from the CSR, except for -basic constraints.`, - } - - ret.Fields["key_usage"] = &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Default: []string{"DigitalSignature", "KeyAgreement", "KeyEncipherment"}, - Description: `A comma-separated string or list of key usages (not extended -key usages). Valid values can be found at -https://golang.org/pkg/crypto/x509/#KeyUsage --- simply drop the "KeyUsage" part of the name. -To remove all key usages from being set, set -this value to an empty list.`, - } - - ret.Fields["ext_key_usage"] = &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Default: []string{}, - Description: `A comma-separated string or list of extended key usages. Valid values can be found at -https://golang.org/pkg/crypto/x509/#ExtKeyUsage --- simply drop the "ExtKeyUsage" part of the name. -To remove all key usages from being set, set -this value to an empty list.`, - } - - ret.Fields["ext_key_usage_oids"] = &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: `A comma-separated string or list of extended key usage oids.`, - } - - ret.Fields["signature_bits"] = &framework.FieldSchema{ - Type: framework.TypeInt, - Default: 0, - Description: `The number of bits to use in the signature -algorithm; accepts 256 for SHA-2-256, 384 for SHA-2-384, and 512 for -SHA-2-512. Defaults to 0 to automatically detect based on key length -(SHA-2-256 for RSA keys, and matching the curve size for NIST P-Curves).`, - DisplayAttrs: &framework.DisplayAttributes{ - Value: 0, - }, - } - - ret.Fields["use_pss"] = &framework.FieldSchema{ - Type: framework.TypeBool, - Default: false, - Description: `Whether or not to use PSS signatures when using a -RSA key-type issuer. Defaults to false.`, - } - return ret } @@ -377,51 +322,7 @@ func (b *backend) pathSign(ctx context.Context, req *logical.Request, data *fram // pathSignVerbatim issues a certificate from a submitted CSR, *not* subject to // role restrictions func (b *backend) pathSignVerbatim(ctx context.Context, req *logical.Request, data *framework.FieldData, role *roleEntry) (*logical.Response, error) { - entry := &roleEntry{ - AllowLocalhost: true, - AllowAnyName: true, - AllowIPSANs: true, - AllowWildcardCertificates: new(bool), - EnforceHostnames: false, - KeyType: "any", - UseCSRCommonName: true, - UseCSRSANs: true, - AllowedOtherSANs: []string{"*"}, - AllowedSerialNumbers: []string{"*"}, - AllowedURISANs: []string{"*"}, - AllowedUserIDs: []string{"*"}, - CNValidations: []string{"disabled"}, - GenerateLease: new(bool), - KeyUsage: data.Get("key_usage").([]string), - ExtKeyUsage: data.Get("ext_key_usage").([]string), - ExtKeyUsageOIDs: data.Get("ext_key_usage_oids").([]string), - SignatureBits: data.Get("signature_bits").(int), - UsePSS: data.Get("use_pss").(bool), - } - *entry.AllowWildcardCertificates = true - - *entry.GenerateLease = false - - if role != nil { - if role.TTL > 0 { - entry.TTL = role.TTL - } - if role.MaxTTL > 0 { - entry.MaxTTL = role.MaxTTL - } - if role.GenerateLease != nil { - *entry.GenerateLease = *role.GenerateLease - } - if role.NotBeforeDuration > 0 { - entry.NotBeforeDuration = role.NotBeforeDuration - } - entry.NoStore = role.NoStore - entry.Issuer = role.Issuer - } - - if len(entry.Issuer) == 0 { - entry.Issuer = defaultRef - } + entry := buildSignVerbatimRole(data, role) return b.pathIssueSignCert(ctx, req, data, entry, true, true) } diff --git a/builtin/logical/pki/path_roles.go b/builtin/logical/pki/path_roles.go index bcfe66c97..4cb15d95d 100644 --- a/builtin/logical/pki/path_roles.go +++ b/builtin/logical/pki/path_roles.go @@ -1014,6 +1014,8 @@ func (b *backend) getRole(ctx context.Context, s logical.Storage, n string) (*ro } } + result.Name = n + return &result, nil } @@ -1105,6 +1107,7 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data NotBeforeDuration: time.Duration(data.Get("not_before_duration").(int)) * time.Second, NotAfter: data.Get("not_after").(string), Issuer: data.Get("issuer_ref").(string), + Name: name, } allowedOtherSANs := data.Get("allowed_other_sans").([]string) @@ -1510,6 +1513,8 @@ type roleEntry struct { NotBeforeDuration time.Duration `json:"not_before_duration"` NotAfter string `json:"not_after"` Issuer string `json:"issuer"` + // Name is only set when the role has been stored, on the fly roles have a blank name + Name string `json:"-"` } func (r *roleEntry) ToResponseData() map[string]interface{} {