open-vault/builtin/logical/pki/storage_test.go
Kit Haines 71d9c33802
Add "plumbing" for surfacing warnings, and warning overwriting ttl (#17073)
* Add "plumbing" for surfacing warnings, and add warning about TTL > maxTTL when issuing a cert.
2022-09-15 12:38:33 -07:00

261 lines
8 KiB
Go

package pki
import (
"context"
"strings"
"testing"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/certutil"
"github.com/hashicorp/vault/sdk/logical"
"github.com/stretchr/testify/require"
)
var ctx = context.Background()
func Test_ConfigsRoundTrip(t *testing.T) {
t.Parallel()
b, s := createBackendWithStorage(t)
sc := b.makeStorageContext(ctx, s)
// Verify we handle nothing stored properly
keyConfigEmpty, err := sc.getKeysConfig()
require.NoError(t, err)
require.Equal(t, &keyConfigEntry{}, keyConfigEmpty)
issuerConfigEmpty, err := sc.getIssuersConfig()
require.NoError(t, err)
require.Equal(t, &issuerConfigEntry{}, issuerConfigEmpty)
// Now attempt to store and reload properly
origKeyConfig := &keyConfigEntry{
DefaultKeyId: genKeyId(),
}
origIssuerConfig := &issuerConfigEntry{
DefaultIssuerId: genIssuerId(),
}
err = sc.setKeysConfig(origKeyConfig)
require.NoError(t, err)
err = sc.setIssuersConfig(origIssuerConfig)
require.NoError(t, err)
keyConfig, err := sc.getKeysConfig()
require.NoError(t, err)
require.Equal(t, origKeyConfig, keyConfig)
issuerConfig, err := sc.getIssuersConfig()
require.NoError(t, err)
require.Equal(t, origIssuerConfig, issuerConfig)
}
func Test_IssuerRoundTrip(t *testing.T) {
t.Parallel()
b, s := createBackendWithStorage(t)
sc := b.makeStorageContext(ctx, s)
issuer1, key1 := genIssuerAndKey(t, b, s)
issuer2, key2 := genIssuerAndKey(t, b, s)
// We get an error when issuer id not found
_, err := sc.fetchIssuerById(issuer1.ID)
require.Error(t, err)
// We get an error when key id not found
_, err = sc.fetchKeyById(key1.ID)
require.Error(t, err)
// Now write out our issuers and keys
err = sc.writeKey(key1)
require.NoError(t, err)
err = sc.writeIssuer(&issuer1)
require.NoError(t, err)
err = sc.writeKey(key2)
require.NoError(t, err)
err = sc.writeIssuer(&issuer2)
require.NoError(t, err)
fetchedKey1, err := sc.fetchKeyById(key1.ID)
require.NoError(t, err)
fetchedIssuer1, err := sc.fetchIssuerById(issuer1.ID)
require.NoError(t, err)
require.Equal(t, &key1, fetchedKey1)
require.Equal(t, &issuer1, fetchedIssuer1)
keys, err := sc.listKeys()
require.NoError(t, err)
require.ElementsMatch(t, []keyID{key1.ID, key2.ID}, keys)
issuers, err := sc.listIssuers()
require.NoError(t, err)
require.ElementsMatch(t, []issuerID{issuer1.ID, issuer2.ID}, issuers)
}
func Test_KeysIssuerImport(t *testing.T) {
t.Parallel()
b, s := createBackendWithStorage(t)
sc := b.makeStorageContext(ctx, s)
issuer1, key1 := genIssuerAndKey(t, b, s)
issuer2, key2 := genIssuerAndKey(t, b, s)
// Key 1 before Issuer 1; Issuer 2 before Key 2.
// Remove KeyIDs from non-written entities before beginning.
key1.ID = ""
issuer1.ID = ""
issuer1.KeyID = ""
key1Ref1, existing, err := sc.importKey(key1.PrivateKey, "key1", key1.PrivateKeyType)
require.NoError(t, err)
require.False(t, existing)
require.Equal(t, strings.TrimSpace(key1.PrivateKey), strings.TrimSpace(key1Ref1.PrivateKey))
// Make sure if we attempt to re-import the same private key, no import/updates occur.
// So the existing flag should be set to true, and we do not update the existing Name field.
key1Ref2, existing, err := sc.importKey(key1.PrivateKey, "ignore-me", key1.PrivateKeyType)
require.NoError(t, err)
require.True(t, existing)
require.Equal(t, key1.PrivateKey, key1Ref1.PrivateKey)
require.Equal(t, key1Ref1.ID, key1Ref2.ID)
require.Equal(t, key1Ref1.Name, key1Ref2.Name)
issuer1Ref1, existing, err := sc.importIssuer(issuer1.Certificate, "issuer1")
require.NoError(t, err)
require.False(t, existing)
require.Equal(t, strings.TrimSpace(issuer1.Certificate), strings.TrimSpace(issuer1Ref1.Certificate))
require.Equal(t, key1Ref1.ID, issuer1Ref1.KeyID)
require.Equal(t, "issuer1", issuer1Ref1.Name)
// Make sure if we attempt to re-import the same issuer, no import/updates occur.
// So the existing flag should be set to true, and we do not update the existing Name field.
issuer1Ref2, existing, err := sc.importIssuer(issuer1.Certificate, "ignore-me")
require.NoError(t, err)
require.True(t, existing)
require.Equal(t, strings.TrimSpace(issuer1.Certificate), strings.TrimSpace(issuer1Ref1.Certificate))
require.Equal(t, issuer1Ref1.ID, issuer1Ref2.ID)
require.Equal(t, key1Ref1.ID, issuer1Ref2.KeyID)
require.Equal(t, issuer1Ref1.Name, issuer1Ref2.Name)
err = sc.writeIssuer(&issuer2)
require.NoError(t, err)
err = sc.writeKey(key2)
require.NoError(t, err)
// Same double import tests as above, but make sure if the previous was created through writeIssuer not importIssuer.
issuer2Ref, existing, err := sc.importIssuer(issuer2.Certificate, "ignore-me")
require.NoError(t, err)
require.True(t, existing)
require.Equal(t, strings.TrimSpace(issuer2.Certificate), strings.TrimSpace(issuer2Ref.Certificate))
require.Equal(t, issuer2.ID, issuer2Ref.ID)
require.Equal(t, "", issuer2Ref.Name)
require.Equal(t, issuer2.KeyID, issuer2Ref.KeyID)
// Same double import tests as above, but make sure if the previous was created through writeKey not importKey.
key2Ref, existing, err := sc.importKey(key2.PrivateKey, "ignore-me", key2.PrivateKeyType)
require.NoError(t, err)
require.True(t, existing)
require.Equal(t, key2.PrivateKey, key2Ref.PrivateKey)
require.Equal(t, key2.ID, key2Ref.ID)
require.Equal(t, "", key2Ref.Name)
}
func Test_IssuerUpgrade(t *testing.T) {
t.Parallel()
b, s := createBackendWithStorage(t)
sc := b.makeStorageContext(ctx, s)
// Make sure that we add OCSP signing to v0 issuers if CRLSigning is enabled
issuer, _ := genIssuerAndKey(t, b, s)
issuer.Version = 0
issuer.Usage.ToggleUsage(OCSPSigningUsage)
err := sc.writeIssuer(&issuer)
require.NoError(t, err, "failed writing out issuer")
newIssuer, err := sc.fetchIssuerById(issuer.ID)
require.NoError(t, err, "failed fetching issuer")
require.Equal(t, uint(1), newIssuer.Version)
require.True(t, newIssuer.Usage.HasUsage(OCSPSigningUsage))
// If CRLSigning is not present on a v0, we should not have OCSP signing after upgrade.
issuer, _ = genIssuerAndKey(t, b, s)
issuer.Version = 0
issuer.Usage.ToggleUsage(OCSPSigningUsage)
issuer.Usage.ToggleUsage(CRLSigningUsage)
err = sc.writeIssuer(&issuer)
require.NoError(t, err, "failed writing out issuer")
newIssuer, err = sc.fetchIssuerById(issuer.ID)
require.NoError(t, err, "failed fetching issuer")
require.Equal(t, uint(1), newIssuer.Version)
require.False(t, newIssuer.Usage.HasUsage(OCSPSigningUsage))
}
func genIssuerAndKey(t *testing.T, b *backend, s logical.Storage) (issuerEntry, keyEntry) {
certBundle := genCertBundle(t, b, s)
keyId := genKeyId()
pkiKey := keyEntry{
ID: keyId,
PrivateKeyType: certBundle.PrivateKeyType,
PrivateKey: strings.TrimSpace(certBundle.PrivateKey) + "\n",
}
issuerId := genIssuerId()
pkiIssuer := issuerEntry{
ID: issuerId,
KeyID: keyId,
Certificate: strings.TrimSpace(certBundle.Certificate) + "\n",
CAChain: certBundle.CAChain,
SerialNumber: certBundle.SerialNumber,
Usage: AllIssuerUsages,
Version: latestIssuerVersion,
}
return pkiIssuer, pkiKey
}
func genCertBundle(t *testing.T, b *backend, s logical.Storage) *certutil.CertBundle {
// Pretty gross just to generate a cert bundle, but
fields := addCACommonFields(map[string]*framework.FieldSchema{})
fields = addCAKeyGenerationFields(fields)
fields = addCAIssueFields(fields)
apiData := &framework.FieldData{
Schema: fields,
Raw: map[string]interface{}{
"exported": "internal",
"cn": "example.com",
"ttl": 3600,
},
}
sc := b.makeStorageContext(ctx, s)
_, _, role, respErr := getGenerationParams(sc, apiData)
require.Nil(t, respErr)
input := &inputBundle{
req: &logical.Request{
Operation: logical.UpdateOperation,
Path: "issue/testrole",
Storage: s,
},
apiData: apiData,
role: role,
}
parsedCertBundle, _, err := generateCert(sc, input, nil, true, b.GetRandomReader())
require.NoError(t, err)
certBundle, err := parsedCertBundle.ToCertBundle()
require.NoError(t, err)
return certBundle
}