Skip to content

Commit

Permalink
Add support for issuer generate intermediate end-point
Browse files Browse the repository at this point in the history
  • Loading branch information
stevendpclark committed Apr 8, 2022
1 parent a76ec20 commit 240d51c
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 63 deletions.
1 change: 1 addition & 0 deletions builtin/logical/pki/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ func Backend(conf *logical.BackendConfig) *backend {
pathIssuerSignSelfIssued(&b),
pathIssuerSignVerbatim(&b),
pathIssuerGenerateRoot(&b),
pathIssuerGenerateIntermediate(&b),
pathConfigIssuers(&b),

// Fetch APIs have been lowered to favor the newer issuer API endpoints
Expand Down
76 changes: 76 additions & 0 deletions builtin/logical/pki/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4708,6 +4708,82 @@ func TestRootWithExistingKey(t *testing.T) {
require.Contains(t, resp.Data["keys"], myIssuerId3)
}

func TestIntermediateWithExistingKey(t *testing.T) {
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"pki": Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()

client := cluster.Cores[0].Client
var err error

mountPKIEndpoint(t, client, "pki-root")

// Fail requests if type is existing, and we specify the key_type param
ctx := context.Background()
_, err = client.Logical().WriteWithContext(ctx, "pki-root/intermediate/generate/existing", map[string]interface{}{
"common_name": "root myvault.com",
"key_type": "rsa",
})
require.Error(t, err)
require.Contains(t, err.Error(), "key_type nor key_bits arguments can be set in this mode")

// Fail requests if type is existing, and we specify the key_bits param
_, err = client.Logical().WriteWithContext(ctx, "pki-root/intermediate/generate/existing", map[string]interface{}{
"common_name": "root myvault.com",
"key_bits": "2048",
})
require.Error(t, err)
require.Contains(t, err.Error(), "key_type nor key_bits arguments can be set in this mode")

// Fail if the specified key does not exist.
_, err = client.Logical().WriteWithContext(ctx, "pki-root/issuers/generate/intermediate/existing", map[string]interface{}{
"common_name": "root myvault.com",
"key_id": "my-key1",
})
require.Error(t, err)
require.Contains(t, err.Error(), "unable to find PKI key for reference: my-key1")

// Create the first intermediate CA
resp, err := client.Logical().WriteWithContext(ctx, "pki-root/issuers/generate/intermediate/internal", map[string]interface{}{
"common_name": "root myvault.com",
"key_type": "rsa",
})
require.NoError(t, err)
// csr1 := resp.Data["csr"]
myKeyId1 := resp.Data["key_id"]
require.NotEmpty(t, myKeyId1)

// Create the second intermediate CA
resp, err = client.Logical().WriteWithContext(ctx, "pki-root/issuers/generate/intermediate/internal", map[string]interface{}{
"common_name": "root myvault.com",
"key_type": "rsa",
})
require.NoError(t, err)
// csr2 := resp.Data["csr"]
myKeyId2 := resp.Data["key_id"]
require.NotEmpty(t, myKeyId2)

// Create a third intermediate CA re-using key from intermediate CA 1
resp, err = client.Logical().WriteWithContext(ctx, "pki-root/issuers/generate/intermediate/existing", map[string]interface{}{
"common_name": "root myvault.com",
"key_id": myKeyId1,
})
require.NoError(t, err)
// csr3 := resp.Data["csr"]
myKeyId3 := resp.Data["key_id"]
require.NotEmpty(t, myKeyId3)

require.NotEqual(t, myKeyId1, myKeyId2)
require.Equal(t, myKeyId1, myKeyId3)
}

var (
initTest sync.Once
rsaCAKey string
Expand Down
5 changes: 0 additions & 5 deletions builtin/logical/pki/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,6 @@ SHA-2-512. Defaults to 0 to automatically detect based on key length
for the configured default key, an identifier or the name assigned
to the key. Note this is only used for the existing generation type.`,
}

fields["id"] = &framework.FieldSchema{
Type: framework.TypeString,
Description: `Assign a name to the generated issuer.`,
}
return fields
}

Expand Down
9 changes: 8 additions & 1 deletion builtin/logical/pki/managed_key_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,17 @@ func generateCABundle(ctx context.Context, _ *backend, input *inputBundle, data
return certutil.CreateCertificateWithRandomSource(data, randomSource)
}

func generateCSRBundle(_ context.Context, _ *backend, input *inputBundle, data *certutil.CreationBundle, addBasicConstraints bool, randomSource io.Reader) (*certutil.ParsedCSRBundle, error) {
func generateCSRBundle(ctx context.Context, _ *backend, input *inputBundle, data *certutil.CreationBundle, addBasicConstraints bool, randomSource io.Reader) (*certutil.ParsedCSRBundle, error) {
if kmsRequested(input) {
return nil, errEntOnly
}
if existingKeyRequested(input) {
keyRef, err := getExistingKeyRef(input.apiData)
if err != nil {
return nil, err
}
return certutil.CreateCSRWithKeyGenerator(data, addBasicConstraints, randomSource, existingGeneratePrivateKey(ctx, input.req.Storage, keyRef))
}

return certutil.CreateCSRWithRandomSource(data, addBasicConstraints, randomSource)
}
Expand Down
38 changes: 3 additions & 35 deletions builtin/logical/pki/path_intermediate.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,7 @@ import (
)

func pathGenerateIntermediate(b *backend) *framework.Path {
ret := &framework.Path{
Pattern: "intermediate/generate/" + framework.GenericNameRegex("exported"),
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathGenerateIntermediate,
// Read more about why these flags are set in backend.go
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
},

HelpSynopsis: pathGenerateIntermediateHelpSyn,
HelpDescription: pathGenerateIntermediateHelpDesc,
}

ret.Fields = addCACommonFields(map[string]*framework.FieldSchema{})
ret.Fields = addCAKeyGenerationFields(ret.Fields)
ret.Fields["add_basic_constraints"] = &framework.FieldSchema{
Type: framework.TypeBool,
Description: `Whether to add a Basic Constraints
extension with CA: true. Only needed as a
workaround in some compatibility scenarios
with Active Directory Certificate Services.`,
}

return ret
return commonGenerateIntermediate(b, "intermediate/generate/"+framework.GenericNameRegex("exported"))
}

func pathSetSignedIntermediate(b *backend) *framework.Path {
Expand Down Expand Up @@ -135,18 +110,11 @@ func (b *backend) pathGenerateIntermediate(ctx context.Context, req *logical.Req
}
}

cb := &certutil.CertBundle{}
cb.PrivateKey = csrb.PrivateKey
cb.PrivateKeyType = csrb.PrivateKeyType

entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
if err != nil {
return nil, err
}
err = req.Storage.Put(ctx, entry)
myKey, _, err := importKey(ctx, req.Storage, csrb.PrivateKey)
if err != nil {
return nil, err
}
resp.Data["key_id"] = myKey.ID

return resp, nil
}
Expand Down
45 changes: 44 additions & 1 deletion builtin/logical/pki/path_manage_issuers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import (
)

func pathIssuerGenerateRoot(b *backend) *framework.Path {
return commonGenerateRoot(b, "issuers/generate/root/"+framework.GenericNameRegex("exported"))
}

func commonGenerateRoot(b *backend, pattern string) *framework.Path {
ret := &framework.Path{
Pattern: "issuers/generate/root/" + framework.GenericNameRegex("exported"),
Pattern: pattern,

Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Expand All @@ -32,6 +36,45 @@ func pathIssuerGenerateRoot(b *backend) *framework.Path {
ret.Fields = addCAKeyGenerationFields(ret.Fields)
ret.Fields = addCAIssueFields(ret.Fields)

ret.Fields["id"] = &framework.FieldSchema{
Type: framework.TypeString,
Description: `Assign a name to the generated issuer.`,
}

return ret
}

func pathIssuerGenerateIntermediate(b *backend) *framework.Path {
return commonGenerateIntermediate(b,
"issuers/generate/intermediate/"+framework.GenericNameRegex("exported"))
}

func commonGenerateIntermediate(b *backend, pattern string) *framework.Path {
ret := &framework.Path{
Pattern: pattern,
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathGenerateIntermediate,
// Read more about why these flags are set in backend.go
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
},

HelpSynopsis: pathGenerateIntermediateHelpSyn,
HelpDescription: pathGenerateIntermediateHelpDesc,
}

ret.Fields = addCACommonFields(map[string]*framework.FieldSchema{})
ret.Fields = addCAKeyGenerationFields(ret.Fields)
ret.Fields["add_basic_constraints"] = &framework.FieldSchema{
Type: framework.TypeBool,
Description: `Whether to add a Basic Constraints
extension with CA: true. Only needed as a
workaround in some compatibility scenarios
with Active Directory Certificate Services.`,
}

return ret
}

Expand Down
22 changes: 1 addition & 21 deletions builtin/logical/pki/path_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,7 @@ import (
)

func pathGenerateRoot(b *backend) *framework.Path {
ret := &framework.Path{
Pattern: "root/generate/" + framework.GenericNameRegex("exported"),

Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathCAGenerateRoot,
// Read more about why these flags are set in backend.go
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
},

HelpSynopsis: pathGenerateRootHelpSyn,
HelpDescription: pathGenerateRootHelpDesc,
}

ret.Fields = addCACommonFields(map[string]*framework.FieldSchema{})
ret.Fields = addCAKeyGenerationFields(ret.Fields)
ret.Fields = addCAIssueFields(ret.Fields)

return ret
return commonGenerateRoot(b, "root/generate/"+framework.GenericNameRegex("exported"))
}

func pathDeleteRoot(b *backend) *framework.Path {
Expand Down

0 comments on commit 240d51c

Please sign in to comment.