open-vault/builtin/logical/pki/crl_test.go

213 lines
5.9 KiB
Go

package pki
import (
"context"
"testing"
"time"
"github.com/hashicorp/vault/api"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault"
"github.com/stretchr/testify/require"
)
func TestBackend_CRL_EnableDisable(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
err = client.Sys().Mount("pki", &api.MountInput{
Type: "pki",
Config: api.MountConfigInput{
DefaultLeaseTTL: "16h",
MaxLeaseTTL: "60h",
},
})
resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{
"ttl": "40h",
"common_name": "myvault.com",
})
if err != nil {
t.Fatal(err)
}
caSerial := resp.Data["serial_number"]
_, err = client.Logical().Write("pki/roles/test", map[string]interface{}{
"allow_bare_domains": true,
"allow_subdomains": true,
"allowed_domains": "foobar.com",
"generate_lease": true,
})
if err != nil {
t.Fatal(err)
}
serials := make(map[int]string)
for i := 0; i < 6; i++ {
resp, err := client.Logical().Write("pki/issue/test", map[string]interface{}{
"common_name": "test.foobar.com",
})
if err != nil {
t.Fatal(err)
}
serials[i] = resp.Data["serial_number"].(string)
}
test := func(numRevokedExpected int, expectedSerials ...string) {
certList := getCrlCertificateList(t, client, "pki")
lenList := len(certList.RevokedCertificates)
if lenList != numRevokedExpected {
t.Fatalf("expected %d revoked certificates, found %d", numRevokedExpected, lenList)
}
for _, serialNum := range expectedSerials {
requireSerialNumberInCRL(t, certList, serialNum)
}
}
revoke := func(serialIndex int) {
resp, err = client.Logical().Write("pki/revoke", map[string]interface{}{
"serial_number": serials[serialIndex],
})
if err != nil {
t.Fatal(err)
}
resp, err = client.Logical().Write("pki/revoke", map[string]interface{}{
"serial_number": caSerial,
})
if err == nil {
t.Fatal("expected error")
}
}
toggle := func(disabled bool) {
_, err = client.Logical().Write("pki/config/crl", map[string]interface{}{
"disable": disabled,
})
if err != nil {
t.Fatal(err)
}
}
test(0)
revoke(0)
revoke(1)
test(2, serials[0], serials[1])
toggle(true)
test(0)
revoke(2)
revoke(3)
test(0)
toggle(false)
test(4, serials[0], serials[1], serials[2], serials[3])
revoke(4)
revoke(5)
test(6)
toggle(true)
test(0)
toggle(false)
test(6)
// The rotate command should reset the update time of the CRL.
crlCreationTime1 := getCrlCertificateList(t, client, "pki").ThisUpdate
time.Sleep(1 * time.Second)
_, err = client.Logical().Read("pki/crl/rotate")
require.NoError(t, err)
crlCreationTime2 := getCrlCertificateList(t, client, "pki").ThisUpdate
require.NotEqual(t, crlCreationTime1, crlCreationTime2)
}
func TestBackend_Secondary_CRL_Rebuilding(t *testing.T) {
ctx := context.Background()
b, s := createBackendWithStorage(t)
// Write out the issuer/key to storage without going through the api call as replication would.
bundle := genCertBundle(t, b, s)
issuer, _, err := writeCaBundle(ctx, b, s, bundle, "", "")
require.NoError(t, err)
// Just to validate, before we call the invalidate function, make sure our CRL has not been generated
// and we get a nil response
resp := requestCrlFromBackend(t, s, b)
require.Nil(t, resp.Data["http_raw_body"])
// This should force any calls from now on to rebuild our CRL even a read
b.invalidate(ctx, issuerPrefix+issuer.ID.String())
// Perform the read operation again, we should have a valid CRL now...
resp = requestCrlFromBackend(t, s, b)
crl := parseCrlPemBytes(t, resp.Data["http_raw_body"].([]byte))
require.Equal(t, 0, len(crl.RevokedCertificates))
}
func TestCrlRebuilder(t *testing.T) {
ctx := context.Background()
b, s := createBackendWithStorage(t)
// Write out the issuer/key to storage without going through the api call as replication would.
bundle := genCertBundle(t, b, s)
_, _, err := writeCaBundle(ctx, b, s, bundle, "", "")
require.NoError(t, err)
req := &logical.Request{Storage: s}
cb := crlBuilder{}
// Force an initial build
err = cb.rebuild(ctx, b, req, true)
require.NoError(t, err, "Failed to rebuild CRL")
resp := requestCrlFromBackend(t, s, b)
crl1 := parseCrlPemBytes(t, resp.Data["http_raw_body"].([]byte))
// We shouldn't rebuild within this call.
err = cb.rebuildIfForced(ctx, b, req)
require.NoError(t, err, "Failed to rebuild if forced CRL")
resp = requestCrlFromBackend(t, s, b)
crl2 := parseCrlPemBytes(t, resp.Data["http_raw_body"].([]byte))
require.Equal(t, crl1.ThisUpdate, crl2.ThisUpdate, "According to the update field, we rebuilt the CRL")
// Make sure we have ticked over to the next second
for {
diff := time.Now().Sub(crl1.ThisUpdate)
if diff.Seconds() >= 1 {
break
}
time.Sleep(100 * time.Millisecond)
}
// This should rebuild the CRL
cb.requestRebuildIfActiveNode(b)
err = cb.rebuildIfForced(ctx, b, req)
require.NoError(t, err, "Failed to rebuild if forced CRL")
resp = requestCrlFromBackend(t, s, b)
crl3 := parseCrlPemBytes(t, resp.Data["http_raw_body"].([]byte))
require.True(t, crl1.ThisUpdate.Before(crl3.ThisUpdate),
"initial crl time: %#v not before next crl rebuild time: %#v", crl1.ThisUpdate, crl3.ThisUpdate)
}
func requestCrlFromBackend(t *testing.T, s logical.Storage, b *backend) *logical.Response {
crlReq := &logical.Request{
Operation: logical.ReadOperation,
Path: "crl/pem",
Storage: s,
}
resp, err := b.HandleRequest(context.Background(), crlReq)
require.NoError(t, err, "crl req failed with an error")
require.NotNil(t, resp, "crl response was nil with no error")
require.False(t, resp.IsError(), "crl error response: %v", resp)
return resp
}