Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support root issuer generation #14975

2 changes: 2 additions & 0 deletions builtin/logical/pki/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ func Backend(conf *logical.BackendConfig) *backend {
pathIssuerSignIntermediate(&b),
pathIssuerSignSelfIssued(&b),
pathIssuerSignVerbatim(&b),
pathIssuerGenerateRoot(&b),
pathIssuerGenerateIntermediate(&b),
pathConfigIssuers(&b),

// Fetch APIs have been lowered to favor the newer issuer API endpoints
Expand Down
207 changes: 207 additions & 0 deletions builtin/logical/pki/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4613,6 +4613,213 @@ func TestBackend_Roles_KeySizeRegression(t *testing.T) {
t.Log(fmt.Sprintf("Key size regression expanded matrix test scenarios: %d", tested))
}

func TestRootWithExistingKey(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/root/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/root/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/root/existing", map[string]interface{}{
"common_name": "root myvault.com",
"issuer_name": "my-issuer1",
"key_ref": "my-key1",
})
require.Error(t, err)
require.Contains(t, err.Error(), "unable to find PKI key for reference: my-key1")

// Fail if the specified key name is default.
_, err = client.Logical().WriteWithContext(ctx, "pki-root/issuers/generate/root/internal", map[string]interface{}{
"common_name": "root myvault.com",
"issuer_name": "my-issuer1",
"key_name": "Default",
})
require.Error(t, err)
require.Contains(t, err.Error(), "reserved keyword 'default' can not be used as key name")

// Fail if the specified issuer name is default.
_, err = client.Logical().WriteWithContext(ctx, "pki-root/issuers/generate/root/internal", map[string]interface{}{
"common_name": "root myvault.com",
"issuer_name": "DEFAULT",
})
require.Error(t, err)
require.Contains(t, err.Error(), "reserved keyword 'default' can not be used as issuer name")

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

// Fail if the specified issuer name is re-used.
_, err = client.Logical().WriteWithContext(ctx, "pki-root/issuers/generate/root/internal", map[string]interface{}{
"common_name": "root myvault.com",
"issuer_name": "my-issuer1",
})
require.Error(t, err)
require.Contains(t, err.Error(), "issuer name already used")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if we try generating a root credential with a name which is the Issuer's Key ID? (I'm hoping that this would fail)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that would fail if an end-user were to try to use an existing id for a Name value. Now if they could predict the uuid we would generate in the future though, that would be a bad day...

// Create the second CA
resp, err = client.Logical().WriteWithContext(ctx, "pki-root/issuers/generate/root/internal", map[string]interface{}{
"common_name": "root myvault.com",
"key_type": "rsa",
"issuer_name": "my-issuer2",
"key_name": "root-key2",
})
require.NoError(t, err)
require.NotNil(t, resp.Data["certificate"])
myIssuerId2 := resp.Data["issuer_id"]
myKeyId2 := resp.Data["key_id"]
require.NotEmpty(t, myIssuerId2)
require.NotEmpty(t, myKeyId2)

// Fail if the specified key name is re-used.
_, err = client.Logical().WriteWithContext(ctx, "pki-root/issuers/generate/root/internal", map[string]interface{}{
"common_name": "root myvault.com",
"issuer_name": "my-issuer3",
"key_name": "root-key2",
})
require.Error(t, err)
require.Contains(t, err.Error(), "key name already used")

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

require.NotEqual(t, myIssuerId1, myIssuerId2)
require.NotEqual(t, myIssuerId1, myIssuerId3)
require.NotEqual(t, myKeyId1, myKeyId2)
require.Equal(t, myKeyId1, myKeyId3)

resp, err = client.Logical().ListWithContext(ctx, "pki-root/issuers")
require.NoError(t, err)
require.Equal(t, 3, len(resp.Data["keys"].([]interface{})))
require.Contains(t, resp.Data["keys"], myIssuerId1)
require.Contains(t, resp.Data["keys"], myIssuerId2)
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_ref": "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",
"key_name": "interkey1",
})
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_ref": 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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key name reusue? issuer name reuse (for non roots?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validating that the new CA we generated re-used a key that we had previous generated. Will add a clearer message on this test.


var (
initTest sync.Once
rsaCAKey string
Expand Down
Loading