2023-03-28 18:39:22 +00:00
// Copyright (c) HashiCorp, Inc.
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
2023-03-28 18:39:22 +00:00
2018-06-13 08:40:03 +00:00
package ca
import (
2019-11-01 13:20:26 +00:00
"crypto/x509"
2020-09-09 23:36:37 +00:00
"encoding/json"
2018-06-14 21:20:10 +00:00
"fmt"
2022-11-10 16:26:01 +00:00
"io"
2023-10-20 15:03:27 +00:00
"runtime/pprof"
2023-05-03 20:30:37 +00:00
"strconv"
"strings"
2022-03-28 14:58:16 +00:00
"sync/atomic"
2018-06-13 08:40:03 +00:00
"testing"
2018-06-14 21:20:10 +00:00
"time"
2018-06-13 08:40:03 +00:00
2020-09-09 23:36:37 +00:00
"github.com/hashicorp/go-hclog"
2018-06-14 17:56:17 +00:00
vaultapi "github.com/hashicorp/vault/api"
2023-01-18 19:53:04 +00:00
"github.com/hashicorp/vault/api/auth/gcp"
2021-11-05 16:42:28 +00:00
vaultconst "github.com/hashicorp/vault/sdk/helper/consts"
2023-09-13 19:33:02 +00:00
"github.com/stretchr/testify/assert"
2018-06-13 08:40:03 +00:00
"github.com/stretchr/testify/require"
2021-06-21 21:51:37 +00:00
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/structs"
2022-12-05 21:39:21 +00:00
"github.com/hashicorp/consul/sdk/testutil"
2021-06-21 21:51:37 +00:00
"github.com/hashicorp/consul/sdk/testutil/retry"
2018-06-13 08:40:03 +00:00
)
2022-12-06 16:06:36 +00:00
const pkiTestPolicyBase = `
2022-10-18 17:17:27 +00:00
path "sys/mounts"
2022-09-08 08:11:19 +00:00
{
2022-10-18 17:17:27 +00:00
capabilities = [ "read" ]
}
2022-12-06 16:06:36 +00:00
path "sys/mounts/%[1]s"
2022-10-18 17:17:27 +00:00
{
capabilities = [ "create" , "read" , "update" , "delete" , "list" ]
}
2022-12-06 16:06:36 +00:00
path "sys/mounts/%[2]s"
2022-10-18 17:17:27 +00:00
{
capabilities = [ "create" , "read" , "update" , "delete" , "list" ]
}
2022-12-06 16:06:36 +00:00
path "sys/mounts/%[2]s/tune"
2022-10-18 17:17:27 +00:00
{
capabilities = [ "update" ]
}
2022-12-06 16:06:36 +00:00
path "%[1]s/*"
2022-10-18 17:17:27 +00:00
{
capabilities = [ "create" , "read" , "update" , "delete" , "list" ]
2022-09-08 08:11:19 +00:00
}
2022-12-06 16:06:36 +00:00
path "%[2]s/*"
2022-09-08 08:11:19 +00:00
{
2022-10-18 17:17:27 +00:00
capabilities = [ "create" , "read" , "update" , "delete" , "list" ]
2022-09-08 08:11:19 +00:00
} `
2022-12-06 16:06:36 +00:00
func pkiTestPolicy ( rootPath , intermediatePath string ) string {
return fmt . Sprintf ( pkiTestPolicyBase , rootPath , intermediatePath )
}
2021-11-18 20:15:28 +00:00
func TestVaultCAProvider_ParseVaultCAConfig ( t * testing . T ) {
cases := map [ string ] struct {
rawConfig map [ string ] interface { }
expConfig * structs . VaultCAProviderConfig
2023-06-21 19:34:42 +00:00
isPrimary bool
2021-11-18 20:15:28 +00:00
expError string
} {
"no token and no auth method provided" : {
rawConfig : map [ string ] interface { } { } ,
expError : "must provide a Vault token or configure a Vault auth method" ,
} ,
"both token and auth method provided" : {
rawConfig : map [ string ] interface { } { "Token" : "test" , "AuthMethod" : map [ string ] interface { } { "Type" : "test" } } ,
expError : "only one of Vault token or Vault auth method can be provided, but not both" ,
} ,
2023-06-21 19:34:42 +00:00
"primary no root PKI path" : {
rawConfig : map [ string ] interface { } { "Token" : "test" , "IntermediatePKIPath" : "test" } ,
isPrimary : true ,
2021-11-18 20:15:28 +00:00
expError : "must provide a valid path to a root PKI backend" ,
} ,
2023-06-21 19:34:42 +00:00
"secondary no root PKI path" : {
rawConfig : map [ string ] interface { } { "Token" : "test" , "IntermediatePKIPath" : "test" } ,
isPrimary : false ,
expConfig : & structs . VaultCAProviderConfig {
CommonCAProviderConfig : defaultCommonConfig ( ) ,
Token : "test" ,
IntermediatePKIPath : "test/" ,
} ,
} ,
2021-11-18 20:15:28 +00:00
"no root intermediate path" : {
rawConfig : map [ string ] interface { } { "Token" : "test" , "RootPKIPath" : "test" } ,
expError : "must provide a valid path for the intermediate PKI backend" ,
} ,
"adds a slash to RootPKIPath and IntermediatePKIPath" : {
2023-06-21 19:34:42 +00:00
isPrimary : true ,
2021-11-18 20:15:28 +00:00
rawConfig : map [ string ] interface { } { "Token" : "test" , "RootPKIPath" : "test" , "IntermediatePKIPath" : "test" } ,
expConfig : & structs . VaultCAProviderConfig {
CommonCAProviderConfig : defaultCommonConfig ( ) ,
Token : "test" ,
RootPKIPath : "test/" ,
IntermediatePKIPath : "test/" ,
} ,
} ,
}
for name , c := range cases {
t . Run ( name , func ( t * testing . T ) {
2023-06-21 19:34:42 +00:00
config , err := ParseVaultCAConfig ( c . rawConfig , c . isPrimary )
2021-11-18 20:15:28 +00:00
if c . expError != "" {
require . EqualError ( t , err , c . expError )
} else {
require . NoError ( t , err )
require . Equal ( t , c . expConfig , config )
}
} )
}
}
func TestVaultCAProvider_configureVaultAuthMethod ( t * testing . T ) {
cases := map [ string ] struct {
expLoginPath string
params map [ string ] interface { }
expError string
2023-01-18 19:53:04 +00:00
hasLDG bool
2021-11-18 20:15:28 +00:00
} {
2023-03-07 03:02:05 +00:00
"alicloud" : { expLoginPath : "auth/alicloud/login" , params : map [ string ] any { "role" : "test-role" , "region" : "test-region" } , hasLDG : true } ,
2023-03-03 19:29:53 +00:00
"approle" : { expLoginPath : "auth/approle/login" , params : map [ string ] any { "role_id_file_path" : "test-path" } , hasLDG : true } ,
2023-01-18 19:53:04 +00:00
"aws" : { expLoginPath : "auth/aws/login" , params : map [ string ] interface { } { "type" : "iam" } , hasLDG : true } ,
2023-03-01 00:07:33 +00:00
"azure" : { expLoginPath : "auth/azure/login" , params : map [ string ] interface { } { "role" : "test-role" , "resource" : "test-resource" } , hasLDG : true } ,
2021-11-18 20:15:28 +00:00
"cf" : { expLoginPath : "auth/cf/login" } ,
"github" : { expLoginPath : "auth/github/login" } ,
2023-01-18 19:53:04 +00:00
"gcp" : { expLoginPath : "auth/gcp/login" , params : map [ string ] interface { } { "type" : "iam" , "role" : "test-role" } } ,
2023-03-02 20:33:06 +00:00
"jwt" : { expLoginPath : "auth/jwt/login" , params : map [ string ] any { "role" : "test-role" , "path" : "test-path" } , hasLDG : true } ,
2021-11-18 20:15:28 +00:00
"kerberos" : { expLoginPath : "auth/kerberos/login" } ,
2023-03-02 22:05:40 +00:00
"kubernetes" : { expLoginPath : "auth/kubernetes/login" , params : map [ string ] interface { } { "role" : "test-role" } , hasLDG : true } ,
2021-11-18 20:15:28 +00:00
"ldap" : { expLoginPath : "auth/ldap/login/foo" , params : map [ string ] interface { } { "username" : "foo" } } ,
"oci" : { expLoginPath : "auth/oci/login/foo" , params : map [ string ] interface { } { "role" : "foo" } } ,
"okta" : { expLoginPath : "auth/okta/login/foo" , params : map [ string ] interface { } { "username" : "foo" } } ,
"radius" : { expLoginPath : "auth/radius/login/foo" , params : map [ string ] interface { } { "username" : "foo" } } ,
"cert" : { expLoginPath : "auth/cert/login" } ,
"token" : { expError : "'token' auth method is not supported via auth method configuration; please provide the token with the 'token' parameter in the CA configuration" } ,
"userpass" : { expLoginPath : "auth/userpass/login/foo" , params : map [ string ] interface { } { "username" : "foo" } } ,
"unsupported" : { expError : "auth method \"unsupported\" is not supported" } ,
}
for authMethodType , c := range cases {
t . Run ( authMethodType , func ( t * testing . T ) {
2023-01-18 19:53:04 +00:00
authMethod := & structs . VaultAuthMethod {
2021-11-18 20:15:28 +00:00
Type : authMethodType ,
Params : c . params ,
2023-01-18 19:53:04 +00:00
}
authIF , err := configureVaultAuthMethod ( authMethod )
if c . expError != "" {
2021-11-18 20:15:28 +00:00
require . EqualError ( t , err , c . expError )
2023-01-18 19:53:04 +00:00
return
}
require . NoError ( t , err )
require . NotNil ( t , authIF )
switch authMethodType {
case VaultAuthMethodTypeGCP :
_ = authIF . ( * gcp . GCPAuth )
default :
auth := authIF . ( * VaultAuthClient )
require . Equal ( t , authMethod , auth . AuthMethod )
require . Equal ( t , c . expLoginPath , auth . LoginPath )
require . Equal ( t , c . hasLDG , auth . LoginDataGen != nil )
2021-11-18 20:15:28 +00:00
}
} )
}
}
2019-01-08 16:09:22 +00:00
func TestVaultCAProvider_VaultTLSConfig ( t * testing . T ) {
config := & structs . VaultCAProviderConfig {
CAFile : "/capath/ca.pem" ,
CAPath : "/capath/" ,
CertFile : "/certpath/cert.pem" ,
KeyFile : "/certpath/key.pem" ,
TLSServerName : "server.name" ,
TLSSkipVerify : true ,
}
tlsConfig := vaultTLSConfig ( config )
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
require . Equal ( t , config . CAFile , tlsConfig . CACert )
require . Equal ( t , config . CAPath , tlsConfig . CAPath )
require . Equal ( t , config . CertFile , tlsConfig . ClientCert )
require . Equal ( t , config . KeyFile , tlsConfig . ClientKey )
require . Equal ( t , config . TLSServerName , tlsConfig . TLSServerName )
require . Equal ( t , config . TLSSkipVerify , tlsConfig . Insecure )
2019-01-08 16:09:22 +00:00
}
2021-11-05 16:42:28 +00:00
func TestVaultCAProvider_Configure ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
testcases := [ ] struct {
name string
2022-12-06 16:06:36 +00:00
rawConfig map [ string ] any
2021-11-05 16:42:28 +00:00
expectedValue func ( t * testing . T , v * VaultProvider )
} {
{
2022-12-06 16:06:36 +00:00
name : "DefaultConfig" ,
rawConfig : map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} ,
2021-11-05 16:42:28 +00:00
expectedValue : func ( t * testing . T , v * VaultProvider ) {
headers := v . client . Headers ( )
require . Equal ( t , "" , headers . Get ( vaultconst . NamespaceHeaderName ) )
require . Equal ( t , "pki-root/" , v . config . RootPKIPath )
require . Equal ( t , "pki-intermediate/" , v . config . IntermediatePKIPath )
} ,
} ,
{
2022-12-06 16:06:36 +00:00
name : "TestConfigWithNamespace" ,
rawConfig : map [ string ] any {
"namespace" : "ns1" ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} ,
2021-11-05 16:42:28 +00:00
expectedValue : func ( t * testing . T , v * VaultProvider ) {
h := v . client . Headers ( )
require . Equal ( t , "ns1" , h . Get ( vaultconst . NamespaceHeaderName ) )
} ,
} ,
}
for _ , testcase := range testcases {
t . Run ( testcase . name , func ( t * testing . T ) {
2022-12-06 16:06:36 +00:00
testVault := NewTestVaultServer ( t )
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
2021-11-05 16:42:28 +00:00
2022-12-06 16:06:36 +00:00
provider := createVaultProvider ( t , true , testVault . Addr , token , testcase . rawConfig )
2021-11-05 16:42:28 +00:00
testcase . expectedValue ( t , provider )
} )
}
2023-10-20 15:03:27 +00:00
}
// This test must not run in parallel
func TestVaultCAProvider_ConfigureFailureGoroutineLeakCheck ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
SkipIfVaultNotPresent ( t )
testVault := NewTestVaultServer ( t )
2021-11-05 16:42:28 +00:00
2023-10-20 15:03:27 +00:00
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := NewVaultProvider ( hclog . New ( & hclog . LoggerOptions { Name : "ca.vault" } ) )
t . Run ( "error on Configure does not leak renewal routine" , func ( t * testing . T ) {
config := map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "badbadbad/" ,
}
cfg := vaultProviderConfig ( t , testVault . Addr , token , config )
err := provider . Configure ( cfg )
require . Error ( t , err )
retry . RunWith ( retry . TwoSeconds ( ) , t , func ( r * retry . R ) {
profile := pprof . Lookup ( "goroutine" )
sb := strings . Builder { }
require . NoError ( r , profile . WriteTo ( & sb , 2 ) )
require . NotContains ( r , sb . String ( ) ,
"created by github.com/hashicorp/consul/agent/connect/ca.(*VaultProvider).Configure" ,
"found renewal goroutine leak" )
// If this test is failing because you added a new goroutine to
// (*VaultProvider).Configure AND that goroutine should persist
// even if Configure errored, then you should change the checked
// string to (*VaultProvider).renewToken.
} )
} )
t . Run ( "successful Configure starts renewal routine" , func ( t * testing . T ) {
config := map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
}
cfg := vaultProviderConfig ( t , testVault . Addr , token , config )
require . NoError ( t , provider . Configure ( cfg ) )
retry . RunWith ( retry . TwoSeconds ( ) , t , func ( r * retry . R ) {
profile := pprof . Lookup ( "goroutine" )
sb := strings . Builder { }
require . NoError ( r , profile . WriteTo ( & sb , 2 ) )
t . Log ( sb . String ( ) )
require . Contains ( r , sb . String ( ) ,
"created by github.com/hashicorp/consul/agent/connect/ca.(*VaultProvider).Configure" ,
"expected renewal goroutine, got none" )
} )
} )
2021-11-05 16:42:28 +00:00
}
2020-06-05 19:36:22 +00:00
func TestVaultCAProvider_SecondaryActiveIntermediate ( t * testing . T ) {
2020-10-09 12:02:18 +00:00
SkipIfVaultNotPresent ( t )
2020-06-05 19:36:22 +00:00
2022-12-06 16:06:36 +00:00
t . Parallel ( )
testVault := NewTestVaultServer ( t )
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := createVaultProvider ( t , false , testVault . Addr , token , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2020-06-05 19:36:22 +00:00
2023-04-03 15:40:33 +00:00
cert , err := provider . ActiveLeafSigningCert ( )
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
require . Empty ( t , cert )
require . NoError ( t , err )
2020-06-05 19:36:22 +00:00
}
2020-09-09 23:36:37 +00:00
func TestVaultCAProvider_RenewToken ( t * testing . T ) {
2020-10-09 12:02:18 +00:00
SkipIfVaultNotPresent ( t )
2020-09-09 23:36:37 +00:00
2022-12-06 16:06:36 +00:00
t . Parallel ( )
testVault := NewTestVaultServer ( t )
2020-09-09 23:36:37 +00:00
// Create a token with a short TTL to be renewed by the provider.
ttl := 1 * time . Second
tcr := & vaultapi . TokenCreateRequest {
TTL : ttl . String ( ) ,
}
secret , err := testVault . client . Auth ( ) . Token ( ) . Create ( tcr )
2020-09-11 15:41:05 +00:00
require . NoError ( t , err )
2020-09-09 23:36:37 +00:00
providerToken := secret . Auth . ClientToken
2022-12-06 16:06:36 +00:00
_ = createVaultProvider ( t , true , testVault . Addr , providerToken , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2020-09-09 23:36:37 +00:00
// Check the last renewal time.
secret , err = testVault . client . Auth ( ) . Token ( ) . Lookup ( providerToken )
2020-09-11 15:41:05 +00:00
require . NoError ( t , err )
2020-09-09 23:36:37 +00:00
firstRenewal , err := secret . Data [ "last_renewal_time" ] . ( json . Number ) . Int64 ( )
2020-09-11 15:41:05 +00:00
require . NoError ( t , err )
2020-09-09 23:36:37 +00:00
2023-02-07 20:52:22 +00:00
// Retry past the TTL and make sure the token has been renewed.
2020-09-11 15:41:05 +00:00
retry . Run ( t , func ( r * retry . R ) {
secret , err = testVault . client . Auth ( ) . Token ( ) . Lookup ( providerToken )
require . NoError ( r , err )
lastRenewal , err := secret . Data [ "last_renewal_time" ] . ( json . Number ) . Int64 ( )
require . NoError ( r , err )
require . Greater ( r , lastRenewal , firstRenewal )
} )
2020-09-09 23:36:37 +00:00
}
2022-03-28 14:58:16 +00:00
func TestVaultCAProvider_RenewTokenStopWatcherOnConfigure ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
2022-12-06 16:06:36 +00:00
t . Parallel ( )
testVault := NewTestVaultServer ( t )
2022-03-28 14:58:16 +00:00
// Create a token with a short TTL to be renewed by the provider.
ttl := 1 * time . Second
tcr := & vaultapi . TokenCreateRequest {
TTL : ttl . String ( ) ,
}
secret , err := testVault . client . Auth ( ) . Token ( ) . Create ( tcr )
require . NoError ( t , err )
providerToken := secret . Auth . ClientToken
2022-12-06 16:06:36 +00:00
provider := createVaultProvider ( t , true , testVault . Addr , providerToken , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2022-03-28 14:58:16 +00:00
2023-02-03 22:09:34 +00:00
// overwrite stopWatcher to set flag on stop for testing
// be sure that original stopWatcher gets called to avoid goroutine leak
gotStopped := uint32 ( 0 )
realStop := provider . stopWatcher
2022-03-28 14:58:16 +00:00
provider . stopWatcher = func ( ) {
atomic . StoreUint32 ( & gotStopped , 1 )
2023-02-03 22:09:34 +00:00
realStop ( )
2022-03-28 14:58:16 +00:00
}
// Check the last renewal time.
secret , err = testVault . client . Auth ( ) . Token ( ) . Lookup ( providerToken )
require . NoError ( t , err )
firstRenewal , err := secret . Data [ "last_renewal_time" ] . ( json . Number ) . Int64 ( )
require . NoError ( t , err )
// Wait past the TTL and make sure the token has been renewed.
retry . Run ( t , func ( r * retry . R ) {
secret , err = testVault . client . Auth ( ) . Token ( ) . Lookup ( providerToken )
require . NoError ( r , err )
lastRenewal , err := secret . Data [ "last_renewal_time" ] . ( json . Number ) . Int64 ( )
require . NoError ( r , err )
require . Greater ( r , lastRenewal , firstRenewal )
} )
2022-12-06 16:06:36 +00:00
providerConfig := vaultProviderConfig ( t , testVault . Addr , providerToken , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2022-03-28 14:58:16 +00:00
require . NoError ( t , provider . Configure ( providerConfig ) )
require . Equal ( t , uint32 ( 1 ) , atomic . LoadUint32 ( & gotStopped ) )
}
2018-06-13 08:40:03 +00:00
func TestVaultCAProvider_Bootstrap ( t * testing . T ) {
2020-10-09 12:02:18 +00:00
SkipIfVaultNotPresent ( t )
2019-09-23 17:04:40 +00:00
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2019-09-23 17:04:40 +00:00
2022-12-06 16:06:36 +00:00
type testcase struct {
name string
caConfig map [ string ] any
certFunc func ( * VaultProvider ) ( string , error )
2021-11-02 18:02:10 +00:00
backendPath string
rootCaCreation bool
expectedRootCertTTL string
2018-06-13 08:40:03 +00:00
}
2022-12-06 16:06:36 +00:00
run := func ( t * testing . T , tc testcase ) {
t . Parallel ( )
tc . caConfig [ "RootPKIPath" ] = "pki-root/"
tc . caConfig [ "IntermediatePKIPath" ] = "pki-intermediate/"
testVault := NewTestVaultServer ( t )
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := createVaultProvider ( t , true , testVault . Addr , token , tc . caConfig )
client := testVault . client
cert , err := tc . certFunc ( provider )
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
require . NoError ( t , err )
2023-01-18 19:53:04 +00:00
resp , err := client . Logical ( ) . ReadRaw ( tc . backendPath + "ca/pem" )
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
require . NoError ( t , err )
2022-11-10 16:26:01 +00:00
bytes , err := io . ReadAll ( resp . Body )
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
require . NoError ( t , err )
require . Equal ( t , cert , string ( bytes ) + "\n" )
2018-06-13 08:40:03 +00:00
2018-06-14 17:56:17 +00:00
// Should be a valid CA cert
2018-06-13 08:40:03 +00:00
parsed , err := connect . ParseCert ( cert )
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
require . NoError ( t , err )
require . True ( t , parsed . IsCA )
require . Len ( t , parsed . URIs , 1 )
require . Equal ( t , fmt . Sprintf ( "spiffe://%s.consul" , provider . clusterID ) , parsed . URIs [ 0 ] . String ( ) )
2021-11-02 18:02:10 +00:00
// test that the root cert ttl as applied
if tc . rootCaCreation {
rootCertTTL , err := time . ParseDuration ( tc . expectedRootCertTTL )
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
require . NoError ( t , err )
2021-11-02 18:02:10 +00:00
expectedNotAfter := time . Now ( ) . Add ( rootCertTTL ) . UTC ( )
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
require . WithinDuration ( t , expectedNotAfter , parsed . NotAfter , 10 * time . Minute , "expected parsed cert ttl to be the same as the value configured" )
2021-11-02 18:02:10 +00:00
}
2018-06-14 21:20:10 +00:00
}
2022-12-06 16:06:36 +00:00
cases := [ ] testcase {
{
name : "default-root-cert-ttl" ,
caConfig : map [ string ] any {
"LeafCertTTL" : "1h" ,
} ,
certFunc : func ( provider * VaultProvider ) ( string , error ) {
2023-04-03 15:40:33 +00:00
root , err := provider . GenerateCAChain ( )
2023-07-14 19:58:33 +00:00
return root , err
2022-12-06 16:06:36 +00:00
} ,
backendPath : "pki-root/" ,
rootCaCreation : true ,
expectedRootCertTTL : structs . DefaultRootCertTTL ,
} ,
{
name : "custom-root-cert-ttl" ,
caConfig : map [ string ] any {
"LeafCertTTL" : "1h" ,
"RootCertTTL" : "8761h" ,
} ,
certFunc : func ( provider * VaultProvider ) ( string , error ) {
2023-04-03 15:40:33 +00:00
return provider . ActiveLeafSigningCert ( )
2022-12-06 16:06:36 +00:00
} ,
backendPath : "pki-intermediate/" ,
rootCaCreation : false ,
expectedRootCertTTL : "8761h" ,
} ,
}
// Verify the root and intermediate certs match the ones in the vault backends
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
run ( t , tc )
} )
}
2018-06-14 21:20:10 +00:00
}
2019-11-01 13:20:26 +00:00
func assertCorrectKeyType ( t * testing . T , want , certPEM string ) {
t . Helper ( )
2018-06-14 21:20:10 +00:00
2019-11-01 13:20:26 +00:00
cert , err := connect . ParseCert ( certPEM )
require . NoError ( t , err )
2018-06-14 21:20:10 +00:00
2019-11-01 13:20:26 +00:00
switch want {
case "ec" :
require . Equal ( t , x509 . ECDSA , cert . PublicKeyAlgorithm )
case "rsa" :
require . Equal ( t , x509 . RSA , cert . PublicKeyAlgorithm )
default :
t . Fatal ( "test doesn't support key type" )
2018-06-14 21:20:10 +00:00
}
2019-11-01 13:20:26 +00:00
}
2018-06-14 21:20:10 +00:00
2019-11-01 13:20:26 +00:00
func TestVaultCAProvider_SignLeaf ( t * testing . T ) {
2020-10-09 12:02:18 +00:00
SkipIfVaultNotPresent ( t )
2018-06-14 21:20:10 +00:00
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
run := func ( t * testing . T , tc KeyTestCase ) {
t . Parallel ( )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
testVault := NewTestVaultServer ( t )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
provider := createVaultProvider ( t , true , testVault . Addr , token , map [ string ] any {
"LeafCertTTL" : "1h" ,
"PrivateKeyType" : tc . KeyType ,
"PrivateKeyBits" : tc . KeyBits ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
spiffeService := & connect . SpiffeIDService {
Host : "node1" ,
Namespace : "default" ,
Datacenter : "dc1" ,
Service : "foo" ,
}
2019-11-01 13:20:26 +00:00
2023-07-14 19:58:33 +00:00
rootPEM , err := provider . GenerateCAChain ( )
2022-12-06 16:06:36 +00:00
require . NoError ( t , err )
assertCorrectKeyType ( t , tc . KeyType , rootPEM )
2019-11-01 13:20:26 +00:00
2023-04-03 15:40:33 +00:00
intPEM , err := provider . ActiveLeafSigningCert ( )
2022-12-06 16:06:36 +00:00
require . NoError ( t , err )
assertCorrectKeyType ( t , tc . KeyType , intPEM )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
// Generate a leaf cert for the service.
var firstSerial uint64
{
raw , _ := connect . TestCSR ( t , spiffeService )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
csr , err := connect . ParseCSR ( raw )
require . NoError ( t , err )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
cert , err := provider . Sign ( csr )
require . NoError ( t , err )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
parsed , err := connect . ParseCert ( cert )
require . NoError ( t , err )
require . Equal ( t , parsed . URIs [ 0 ] , spiffeService . URI ( ) )
firstSerial = parsed . SerialNumber . Uint64 ( )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
// Ensure the cert is valid now and expires within the correct limit.
now := time . Now ( )
require . True ( t , parsed . NotAfter . Sub ( now ) < time . Hour )
require . True ( t , parsed . NotBefore . Before ( now ) )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
// Make sure we can validate the cert as expected.
require . NoError ( t , connect . ValidateLeaf ( rootPEM , cert , [ ] string { intPEM } ) )
requireTrailingNewline ( t , cert )
}
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
// Generate a new cert for another service and make sure
// the serial number is unique.
spiffeService . Service = "bar"
{
raw , _ := connect . TestCSR ( t , spiffeService )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
csr , err := connect . ParseCSR ( raw )
require . NoError ( t , err )
cert , err := provider . Sign ( csr )
require . NoError ( t , err )
parsed , err := connect . ParseCert ( cert )
require . NoError ( t , err )
require . Equal ( t , parsed . URIs [ 0 ] , spiffeService . URI ( ) )
require . NotEqual ( t , firstSerial , parsed . SerialNumber . Uint64 ( ) )
// Ensure the cert is valid now and expires within the correct limit.
require . True ( t , time . Until ( parsed . NotAfter ) < time . Hour )
require . True ( t , parsed . NotBefore . Before ( time . Now ( ) ) )
// Make sure we can validate the cert as expected.
require . NoError ( t , connect . ValidateLeaf ( rootPEM , cert , [ ] string { intPEM } ) )
}
}
for _ , tc := range KeyTestCases {
t . Run ( tc . Desc , func ( t * testing . T ) {
run ( t , tc )
2019-11-01 13:20:26 +00:00
} )
2018-06-13 08:40:03 +00:00
}
}
2018-06-15 23:25:53 +00:00
func TestVaultCAProvider_CrossSignCA ( t * testing . T ) {
2023-11-15 20:43:33 +00:00
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2020-10-09 12:02:18 +00:00
SkipIfVaultNotPresent ( t )
2019-09-23 17:04:40 +00:00
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2019-11-01 13:20:26 +00:00
tests := CASigningKeyTypeCases ( )
2022-12-06 16:06:36 +00:00
run := func ( t * testing . T , tc CASigningKeyTypes , withSudo , expectFailure bool ) {
t . Parallel ( )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
testVault1 := NewTestVaultServer ( t )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
attr1 := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
WithSudo : withSudo ,
}
token1 := CreateVaultTokenWithAttrs ( t , testVault1 . client , attr1 )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
provider1 := createVaultProvider ( t , true , testVault1 . Addr , token1 , map [ string ] any {
"LeafCertTTL" : "1h" ,
"PrivateKeyType" : tc . SigningKeyType ,
"PrivateKeyBits" : tc . SigningKeyBits ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
testutil . RunStep ( t , "init" , func ( t * testing . T ) {
2023-07-14 19:58:33 +00:00
rootPEM , err := provider1 . GenerateCAChain ( )
2022-12-06 16:06:36 +00:00
require . NoError ( t , err )
2023-07-14 19:58:33 +00:00
assertCorrectKeyType ( t , tc . SigningKeyType , rootPEM )
2019-11-01 13:20:26 +00:00
2023-04-03 15:40:33 +00:00
intPEM , err := provider1 . ActiveLeafSigningCert ( )
2022-12-06 16:06:36 +00:00
require . NoError ( t , err )
assertCorrectKeyType ( t , tc . SigningKeyType , intPEM )
} )
testVault2 := NewTestVaultServer ( t )
attr2 := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
WithSudo : false , // irrelevant for the new CA provider
}
token2 := CreateVaultTokenWithAttrs ( t , testVault2 . client , attr2 )
provider2 := createVaultProvider ( t , true , testVault2 . Addr , token2 , map [ string ] any {
"LeafCertTTL" : "1h" ,
"PrivateKeyType" : tc . CSRKeyType ,
"PrivateKeyBits" : tc . CSRKeyBits ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
testutil . RunStep ( t , "swap" , func ( t * testing . T ) {
2023-07-14 19:58:33 +00:00
rootPEM , err := provider2 . GenerateCAChain ( )
2022-12-06 16:06:36 +00:00
require . NoError ( t , err )
2023-07-14 19:58:33 +00:00
assertCorrectKeyType ( t , tc . CSRKeyType , rootPEM )
2022-12-06 16:06:36 +00:00
2023-04-03 15:40:33 +00:00
intPEM , err := provider2 . ActiveLeafSigningCert ( )
2022-12-06 16:06:36 +00:00
require . NoError ( t , err )
assertCorrectKeyType ( t , tc . CSRKeyType , intPEM )
if expectFailure {
testCrossSignProvidersShouldFail ( t , provider1 , provider2 )
} else {
testCrossSignProviders ( t , provider1 , provider2 )
2019-11-01 13:20:26 +00:00
}
2022-12-06 16:06:36 +00:00
} )
}
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
for _ , tc := range tests {
t . Run ( tc . Desc , func ( t * testing . T ) {
t . Run ( "without sudo" , func ( t * testing . T ) {
run ( t , tc , false , true )
} )
t . Run ( "with sudo" , func ( t * testing . T ) {
run ( t , tc , true , false )
} )
2019-11-01 13:20:26 +00:00
} )
}
2018-06-15 23:25:53 +00:00
}
2018-09-13 20:09:07 +00:00
func TestVaultProvider_SignIntermediate ( t * testing . T ) {
2020-10-09 12:02:18 +00:00
SkipIfVaultNotPresent ( t )
2019-09-23 17:04:40 +00:00
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2019-11-01 13:20:26 +00:00
tests := CASigningKeyTypeCases ( )
2022-12-06 16:06:36 +00:00
run := func ( t * testing . T , tc CASigningKeyTypes ) {
t . Parallel ( )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
testVault1 := NewTestVaultServer ( t )
2019-11-01 13:20:26 +00:00
2022-12-06 16:06:36 +00:00
attr1 := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token1 := CreateVaultTokenWithAttrs ( t , testVault1 . client , attr1 )
provider1 := createVaultProvider ( t , true , testVault1 . Addr , token1 , map [ string ] any {
"LeafCertTTL" : "1h" ,
"PrivateKeyType" : tc . SigningKeyType ,
"PrivateKeyBits" : tc . SigningKeyBits ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
testVault2 := NewTestVaultServer ( t )
attr2 := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token2 := CreateVaultTokenWithAttrs ( t , testVault2 . client , attr2 )
provider2 := createVaultProvider ( t , false , testVault2 . Addr , token2 , map [ string ] any {
"LeafCertTTL" : "1h" ,
"PrivateKeyType" : tc . CSRKeyType ,
"PrivateKeyBits" : tc . CSRKeyBits ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
testSignIntermediateCrossDC ( t , provider1 , provider2 )
}
for _ , tc := range tests {
t . Run ( tc . Desc , func ( t * testing . T ) {
run ( t , tc )
2019-11-01 13:20:26 +00:00
} )
}
2018-09-13 20:09:07 +00:00
}
func TestVaultProvider_SignIntermediateConsul ( t * testing . T ) {
2020-10-09 12:02:18 +00:00
SkipIfVaultNotPresent ( t )
2018-09-13 20:09:07 +00:00
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2018-09-14 23:08:54 +00:00
// primary = Vault, secondary = Consul
2019-09-23 17:04:40 +00:00
t . Run ( "pri=vault,sec=consul" , func ( t * testing . T ) {
2022-12-06 16:06:36 +00:00
t . Parallel ( )
testVault1 := NewTestVaultServer ( t )
attr1 := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token1 := CreateVaultTokenWithAttrs ( t , testVault1 . client , attr1 )
provider1 := createVaultProvider ( t , true , testVault1 . Addr , token1 , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2018-09-13 20:09:07 +00:00
2018-09-14 23:08:54 +00:00
conf := testConsulCAConfig ( )
delegate := newMockDelegate ( t , conf )
2019-11-11 20:30:01 +00:00
provider2 := TestConsulProvider ( t , delegate )
2019-11-18 14:22:19 +00:00
cfg := testProviderConfig ( conf )
cfg . IsPrimary = false
cfg . Datacenter = "dc2"
require . NoError ( t , provider2 . Configure ( cfg ) )
2018-09-14 23:08:54 +00:00
testSignIntermediateCrossDC ( t , provider1 , provider2 )
2019-09-23 17:04:40 +00:00
} )
2018-09-14 23:08:54 +00:00
// primary = Consul, secondary = Vault
2019-09-23 17:04:40 +00:00
t . Run ( "pri=consul,sec=vault" , func ( t * testing . T ) {
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2018-09-14 23:08:54 +00:00
conf := testConsulCAConfig ( )
delegate := newMockDelegate ( t , conf )
2019-11-11 20:30:01 +00:00
provider1 := TestConsulProvider ( t , delegate )
2019-11-18 14:22:19 +00:00
require . NoError ( t , provider1 . Configure ( testProviderConfig ( conf ) ) )
2023-04-03 15:40:33 +00:00
_ , err := provider1 . GenerateCAChain ( )
2021-12-01 20:39:20 +00:00
require . NoError ( t , err )
2018-09-14 23:08:54 +00:00
2020-01-21 20:55:21 +00:00
// Ensure that we don't configure vault to try and mint leafs that
// outlive their CA during the test (which hard fails in vault).
intermediateCertTTL := getIntermediateCertTTL ( t , conf )
leafCertTTL := intermediateCertTTL - 4 * time . Hour
2022-12-06 16:06:36 +00:00
overrideConf := map [ string ] any {
"LeafCertTTL" : [ ] uint8 ( leafCertTTL . String ( ) ) ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
}
testVault2 := NewTestVaultServer ( t )
attr2 := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
2020-01-21 20:55:21 +00:00
}
2022-12-06 16:06:36 +00:00
token2 := CreateVaultTokenWithAttrs ( t , testVault2 . client , attr2 )
2020-01-21 20:55:21 +00:00
2022-12-06 16:06:36 +00:00
provider2 := createVaultProvider ( t , false , testVault2 . Addr , token2 , overrideConf )
2018-09-14 23:08:54 +00:00
testSignIntermediateCrossDC ( t , provider1 , provider2 )
2019-09-23 17:04:40 +00:00
} )
}
2021-01-15 18:20:27 +00:00
func TestVaultProvider_Cleanup ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2021-01-15 18:20:27 +00:00
2022-12-06 16:06:36 +00:00
testVault := NewTestVaultServer ( t )
2021-01-15 18:20:27 +00:00
t . Run ( "provider-change" , func ( t * testing . T ) {
2022-12-06 16:06:36 +00:00
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := createVaultProvider ( t , true , testVault . Addr , token , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2021-01-15 18:20:27 +00:00
// ensure that the intermediate PKI mount exists
mounts , err := provider . client . Sys ( ) . ListMounts ( )
require . NoError ( t , err )
require . Contains ( t , mounts , provider . config . IntermediatePKIPath )
// call cleanup with a provider change - this should cause removal of the mount
require . NoError ( t , provider . Cleanup ( true , nil ) )
// verify the mount was removed
mounts , err = provider . client . Sys ( ) . ListMounts ( )
require . NoError ( t , err )
require . NotContains ( t , mounts , provider . config . IntermediatePKIPath )
} )
t . Run ( "pki-path-change" , func ( t * testing . T ) {
2022-12-06 16:06:36 +00:00
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := createVaultProvider ( t , true , testVault . Addr , token , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2021-01-15 18:20:27 +00:00
// ensure that the intermediate PKI mount exists
mounts , err := provider . client . Sys ( ) . ListMounts ( )
require . NoError ( t , err )
require . Contains ( t , mounts , provider . config . IntermediatePKIPath )
// call cleanup with an intermediate pki path change - this should cause removal of the mount
2022-12-06 16:06:36 +00:00
require . NoError ( t , provider . Cleanup ( false , map [ string ] any {
2021-01-15 18:20:27 +00:00
"Address" : testVault . Addr ,
2022-12-06 16:06:36 +00:00
"Token" : token ,
2021-01-15 18:20:27 +00:00
"RootPKIPath" : "pki-root/" ,
//
2022-12-06 16:06:36 +00:00
"IntermediatePKIPath" : "pki-intermediate-2/" ,
2021-01-15 18:20:27 +00:00
// Tests duration parsing after msgpack type mangling during raft apply.
"LeafCertTTL" : [ ] uint8 ( "72h" ) ,
} ) )
// verify the mount was removed
mounts , err = provider . client . Sys ( ) . ListMounts ( )
require . NoError ( t , err )
require . NotContains ( t , mounts , provider . config . IntermediatePKIPath )
} )
t . Run ( "pki-path-unchanged" , func ( t * testing . T ) {
2022-12-06 16:06:36 +00:00
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := createVaultProvider ( t , true , testVault . Addr , token , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2021-01-15 18:20:27 +00:00
// ensure that the intermediate PKI mount exists
mounts , err := provider . client . Sys ( ) . ListMounts ( )
require . NoError ( t , err )
require . Contains ( t , mounts , provider . config . IntermediatePKIPath )
// call cleanup with no config changes - this should not cause removal of the intermediate pki path
2022-12-06 16:06:36 +00:00
require . NoError ( t , provider . Cleanup ( false , map [ string ] any {
2021-01-15 18:20:27 +00:00
"Address" : testVault . Addr ,
2022-12-06 16:06:36 +00:00
"Token" : token ,
2021-01-15 18:20:27 +00:00
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
// Tests duration parsing after msgpack type mangling during raft apply.
"LeafCertTTL" : [ ] uint8 ( "72h" ) ,
} ) )
// verify the mount was NOT removed
mounts , err = provider . client . Sys ( ) . ListMounts ( )
require . NoError ( t , err )
require . Contains ( t , mounts , provider . config . IntermediatePKIPath )
} )
}
2021-11-18 20:15:28 +00:00
func TestVaultProvider_ConfigureWithAuthMethod ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
cases := [ ] struct {
authMethodType string
configureAuthMethodFunc func ( t * testing . T , vaultClient * vaultapi . Client ) map [ string ] interface { }
} {
{
authMethodType : "userpass" ,
configureAuthMethodFunc : func ( t * testing . T , vaultClient * vaultapi . Client ) map [ string ] interface { } {
_ , err := vaultClient . Logical ( ) . Write ( "/auth/userpass/users/test" ,
2022-09-08 08:11:19 +00:00
map [ string ] interface { } { "password" : "foo" , "policies" : "pki" } )
2021-11-18 20:15:28 +00:00
require . NoError ( t , err )
return map [ string ] interface { } {
"Type" : "userpass" ,
"Params" : map [ string ] interface { } {
"username" : "test" ,
"password" : "foo" ,
} ,
}
} ,
} ,
{
authMethodType : "approle" ,
configureAuthMethodFunc : func ( t * testing . T , vaultClient * vaultapi . Client ) map [ string ] interface { } {
2022-09-08 08:11:19 +00:00
_ , err := vaultClient . Logical ( ) . Write ( "auth/approle/role/my-role" ,
map [ string ] interface { } { "token_policies" : "pki" } )
2021-11-18 20:15:28 +00:00
require . NoError ( t , err )
resp , err := vaultClient . Logical ( ) . Read ( "auth/approle/role/my-role/role-id" )
require . NoError ( t , err )
roleID := resp . Data [ "role_id" ]
resp , err = vaultClient . Logical ( ) . Write ( "auth/approle/role/my-role/secret-id" , nil )
require . NoError ( t , err )
secretID := resp . Data [ "secret_id" ]
return map [ string ] interface { } {
"Type" : "approle" ,
"Params" : map [ string ] interface { } {
"role_id" : roleID ,
"secret_id" : secretID ,
} ,
}
} ,
} ,
}
for _ , c := range cases {
t . Run ( c . authMethodType , func ( t * testing . T ) {
testVault := NewTestVaultServer ( t )
err := testVault . Client ( ) . Sys ( ) . EnableAuthWithOptions ( c . authMethodType , & vaultapi . EnableAuthOptions { Type : c . authMethodType } )
require . NoError ( t , err )
2022-12-06 16:06:36 +00:00
err = testVault . Client ( ) . Sys ( ) . PutPolicy ( "pki" , pkiTestPolicy ( "pki-root" , "pki-intermediate" ) )
2022-09-08 08:11:19 +00:00
require . NoError ( t , err )
2021-11-18 20:15:28 +00:00
authMethodConf := c . configureAuthMethodFunc ( t , testVault . Client ( ) )
2022-12-06 16:06:36 +00:00
conf := map [ string ] any {
2021-11-18 20:15:28 +00:00
"Address" : testVault . Addr ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
"AuthMethod" : authMethodConf ,
}
provider := NewVaultProvider ( hclog . New ( nil ) )
cfg := ProviderConfig {
ClusterID : connect . TestClusterID ,
Datacenter : "dc1" ,
IsPrimary : true ,
RawConfig : conf ,
}
t . Cleanup ( provider . Stop )
err = provider . Configure ( cfg )
require . NoError ( t , err )
require . NotEmpty ( t , provider . client . Token ( ) )
} )
}
}
func TestVaultProvider_RotateAuthMethodToken ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2021-11-18 20:15:28 +00:00
testVault := NewTestVaultServer ( t )
2022-12-06 16:06:36 +00:00
err := testVault . Client ( ) . Sys ( ) . PutPolicy ( "pki" , pkiTestPolicy ( "pki-root" , "pki-intermediate" ) )
2022-09-08 08:11:19 +00:00
require . NoError ( t , err )
err = testVault . Client ( ) . Sys ( ) . EnableAuthWithOptions ( "approle" , & vaultapi . EnableAuthOptions { Type : "approle" } )
2021-11-18 20:15:28 +00:00
require . NoError ( t , err )
_ , err = testVault . Client ( ) . Logical ( ) . Write ( "auth/approle/role/my-role" ,
2022-09-08 08:11:19 +00:00
map [ string ] interface { } {
"token_ttl" : "2s" ,
"token_explicit_max_ttl" : "2s" ,
"token_policies" : "pki" ,
} )
2021-11-18 20:15:28 +00:00
require . NoError ( t , err )
resp , err := testVault . Client ( ) . Logical ( ) . Read ( "auth/approle/role/my-role/role-id" )
require . NoError ( t , err )
roleID := resp . Data [ "role_id" ]
resp , err = testVault . Client ( ) . Logical ( ) . Write ( "auth/approle/role/my-role/secret-id" , nil )
require . NoError ( t , err )
secretID := resp . Data [ "secret_id" ]
2022-12-06 16:06:36 +00:00
conf := map [ string ] any {
2021-11-18 20:15:28 +00:00
"Address" : testVault . Addr ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
"AuthMethod" : map [ string ] interface { } {
"Type" : "approle" ,
"Params" : map [ string ] interface { } {
"role_id" : roleID ,
"secret_id" : secretID ,
} ,
} ,
}
provider := NewVaultProvider ( hclog . New ( nil ) )
cfg := ProviderConfig {
ClusterID : connect . TestClusterID ,
Datacenter : "dc1" ,
IsPrimary : true ,
RawConfig : conf ,
}
t . Cleanup ( provider . Stop )
err = provider . Configure ( cfg )
require . NoError ( t , err )
token := provider . client . Token ( )
require . NotEmpty ( t , token )
// Check that the token is rotated after max_ttl time has passed.
require . Eventually ( t , func ( ) bool {
return provider . client . Token ( ) != token
} , 10 * time . Second , 100 * time . Millisecond )
}
2022-10-18 17:17:27 +00:00
func TestVaultProvider_ReconfigureIntermediateTTL ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2022-10-18 17:17:27 +00:00
// Set up a standard policy without any sys/mounts/pki-intermediate/tune permissions.
policy := `
path "sys/mounts"
{
capabilities = [ "read" ]
}
path "sys/mounts/pki-root"
{
capabilities = [ "create" , "read" , "update" , "delete" , "list" ]
}
path "sys/mounts/pki-intermediate"
{
capabilities = [ "create" , "read" , "update" , "delete" , "list" ]
}
path "pki-root/*"
{
capabilities = [ "create" , "read" , "update" , "delete" , "list" ]
}
path "pki-intermediate/*"
{
capabilities = [ "create" , "read" , "update" , "delete" , "list" ]
} `
testVault := NewTestVaultServer ( t )
err := testVault . Client ( ) . Sys ( ) . PutPolicy ( "pki" , policy )
require . NoError ( t , err )
tcr := & vaultapi . TokenCreateRequest {
Policies : [ ] string { "pki" } ,
}
secret , err := testVault . client . Auth ( ) . Token ( ) . Create ( tcr )
require . NoError ( t , err )
providerToken := secret . Auth . ClientToken
makeProviderConfWithTTL := func ( ttl string ) ProviderConfig {
2022-12-06 16:06:36 +00:00
conf := map [ string ] any {
2022-10-18 17:17:27 +00:00
"Address" : testVault . Addr ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
"Token" : providerToken ,
"IntermediateCertTTL" : ttl ,
}
cfg := ProviderConfig {
ClusterID : connect . TestClusterID ,
Datacenter : "dc1" ,
IsPrimary : true ,
RawConfig : conf ,
}
return cfg
}
provider := NewVaultProvider ( hclog . New ( nil ) )
// Set up the initial provider config
t . Cleanup ( provider . Stop )
err = provider . Configure ( makeProviderConfWithTTL ( "222h" ) )
require . NoError ( t , err )
2023-04-03 15:40:33 +00:00
_ , err = provider . GenerateCAChain ( )
2022-10-18 17:17:27 +00:00
require . NoError ( t , err )
2023-04-03 15:40:33 +00:00
_ , err = provider . GenerateLeafSigningCert ( )
2022-10-18 17:17:27 +00:00
require . NoError ( t , err )
// Attempt to update the ttl without permissions for the tune endpoint - shouldn't
// return an error.
err = provider . Configure ( makeProviderConfWithTTL ( "333h" ) )
require . NoError ( t , err )
// Intermediate TTL shouldn't have changed
mountConfig , err := testVault . Client ( ) . Sys ( ) . MountConfig ( "pki-intermediate" )
require . NoError ( t , err )
require . Equal ( t , 222 * 3600 , mountConfig . MaxLeaseTTL )
// Update the policy and verify we can reconfigure the TTL properly.
policy += `
path "sys/mounts/pki-intermediate/tune"
{
capabilities = [ "update" ]
} `
err = testVault . Client ( ) . Sys ( ) . PutPolicy ( "pki" , policy )
require . NoError ( t , err )
err = provider . Configure ( makeProviderConfWithTTL ( "333h" ) )
require . NoError ( t , err )
mountConfig , err = testVault . Client ( ) . Sys ( ) . MountConfig ( "pki-intermediate" )
require . NoError ( t , err )
require . Equal ( t , 333 * 3600 , mountConfig . MaxLeaseTTL )
}
2022-11-17 21:29:49 +00:00
func TestVaultCAProvider_GenerateIntermediate ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
2022-12-06 16:06:36 +00:00
t . Parallel ( )
testVault := NewTestVaultServer ( t )
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := createVaultProvider ( t , true , testVault . Addr , token , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2022-11-17 21:29:49 +00:00
2023-04-03 15:40:33 +00:00
orig , err := provider . ActiveLeafSigningCert ( )
2022-11-17 21:29:49 +00:00
require . NoError ( t , err )
// This test was created to ensure that our calls to Vault
// returns a new Intermediate certificate and further calls
2023-04-03 15:40:33 +00:00
// to ActiveLeafSigningCert return the same new cert.
2023-07-14 19:58:33 +00:00
newLeaf , err := provider . GenerateLeafSigningCert ( )
2022-11-17 21:29:49 +00:00
require . NoError ( t , err )
2023-04-03 15:40:33 +00:00
newActive , err := provider . ActiveLeafSigningCert ( )
2022-11-17 21:29:49 +00:00
require . NoError ( t , err )
2023-07-14 19:58:33 +00:00
require . Equal ( t , newLeaf , newActive )
require . NotEqual ( t , orig , newLeaf )
2022-11-17 21:29:49 +00:00
}
2023-05-03 20:30:37 +00:00
func TestVaultCAProvider_AutoTidyExpiredIssuers ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
t . Parallel ( )
testVault := NewTestVaultServer ( t )
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := createVaultProvider ( t , true , testVault . Addr , token ,
map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
version := strings . Split ( vaultTestVersion , "." )
require . Len ( t , version , 3 )
minorVersion , err := strconv . Atoi ( version [ 1 ] )
require . NoError ( t , err )
expIssSet , errStr := provider . autotidyIssuers ( "pki-intermediate/" )
switch {
case minorVersion <= 11 :
require . False ( t , expIssSet )
require . Contains ( t , errStr , "auto-tidy" )
case minorVersion == 12 :
require . False ( t , expIssSet )
require . Contains ( t , errStr , "tidy_expired_issuers" )
default : // Consul 1.13+
require . True ( t , expIssSet )
}
// check permission denied
expIssSet , errStr = provider . autotidyIssuers ( "pki-bad/" )
require . False ( t , expIssSet )
require . Contains ( t , errStr , "permission denied" )
}
2023-09-13 19:33:02 +00:00
func TestVaultCAProvider_DeletePreviousIssuerAndKey ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
t . Parallel ( )
testVault := NewTestVaultServer ( t )
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := createVaultProvider ( t , true , testVault . Addr , token ,
map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
res , err := testVault . Client ( ) . Logical ( ) . List ( "pki-intermediate/issuers" )
require . NoError ( t , err )
// Why 2 issuers? There is always an initial issuer that
// gets created before we manage the lifecycle of issuers.
// Since we're asserting that the number doesn't grow
// this isn't too much of a concern.
//
// Note the key "keys" refers to the IDs of the resource based
// on the endpoint the response is from.
require . Len ( t , res . Data [ "keys" ] , 2 )
res , err = testVault . Client ( ) . Logical ( ) . List ( "pki-intermediate/keys" )
require . NoError ( t , err )
require . Len ( t , res . Data [ "keys" ] , 1 )
for i := 0 ; i < 3 ; i ++ {
_ , err := provider . GenerateLeafSigningCert ( )
require . NoError ( t , err )
res , err := testVault . Client ( ) . Logical ( ) . List ( "pki-intermediate/issuers" )
require . NoError ( t , err )
require . Len ( t , res . Data [ "keys" ] , 2 )
res , err = testVault . Client ( ) . Logical ( ) . List ( "pki-intermediate/keys" )
require . NoError ( t , err )
assert . Len ( t , res . Data [ "keys" ] , 1 )
}
}
2022-12-05 21:39:21 +00:00
func TestVaultCAProvider_GenerateIntermediate_inSecondary ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
2022-12-06 16:06:36 +00:00
t . Parallel ( )
2022-12-05 21:39:21 +00:00
// Primary DC will be a consul provider.
conf := testConsulCAConfig ( )
delegate := newMockDelegate ( t , conf )
primaryProvider := TestConsulProvider ( t , delegate )
require . NoError ( t , primaryProvider . Configure ( testProviderConfig ( conf ) ) )
2023-04-03 15:40:33 +00:00
_ , err := primaryProvider . GenerateCAChain ( )
2022-12-05 21:39:21 +00:00
require . NoError ( t , err )
// Ensure that we don't configure vault to try and mint leafs that
// outlive their CA during the test (which hard fails in vault).
intermediateCertTTL := getIntermediateCertTTL ( t , conf )
leafCertTTL := intermediateCertTTL - 4 * time . Hour
2022-12-06 16:06:36 +00:00
testVault := NewTestVaultServer ( t )
attr := & VaultTokenAttributes {
RootPath : "pki-root" ,
IntermediatePath : "pki-intermediate" ,
ConsulManaged : true ,
}
token := CreateVaultTokenWithAttrs ( t , testVault . client , attr )
provider := createVaultProvider ( t , false , testVault . Addr , token , map [ string ] any {
"LeafCertTTL" : [ ] uint8 ( leafCertTTL . String ( ) ) ,
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
2022-12-05 21:39:21 +00:00
} )
var origIntermediate string
testutil . RunStep ( t , "initialize secondary provider" , func ( t * testing . T ) {
// Get the intermediate CSR from provider.
csrPEM , issuerID , err := provider . GenerateIntermediateCSR ( )
require . NoError ( t , err )
csr , err := connect . ParseCSR ( csrPEM )
require . NoError ( t , err )
// Sign the CSR with primaryProvider.
intermediatePEM , err := primaryProvider . SignIntermediate ( csr )
require . NoError ( t , err )
2023-07-14 19:58:33 +00:00
rootPEM , err := primaryProvider . GenerateCAChain ( )
2022-12-05 21:39:21 +00:00
require . NoError ( t , err )
// Give the new intermediate to provider to use.
require . NoError ( t , provider . SetIntermediate ( intermediatePEM , rootPEM , issuerID ) )
2023-04-03 15:40:33 +00:00
origIntermediate , err = provider . ActiveLeafSigningCert ( )
2022-12-05 21:39:21 +00:00
require . NoError ( t , err )
} )
testutil . RunStep ( t , "renew secondary provider" , func ( t * testing . T ) {
// Get the intermediate CSR from provider.
csrPEM , issuerID , err := provider . GenerateIntermediateCSR ( )
require . NoError ( t , err )
csr , err := connect . ParseCSR ( csrPEM )
require . NoError ( t , err )
// Sign the CSR with primaryProvider.
intermediatePEM , err := primaryProvider . SignIntermediate ( csr )
require . NoError ( t , err )
2023-07-14 19:58:33 +00:00
rootPEM , err := primaryProvider . GenerateCAChain ( )
2022-12-05 21:39:21 +00:00
require . NoError ( t , err )
// Give the new intermediate to provider to use.
require . NoError ( t , provider . SetIntermediate ( intermediatePEM , rootPEM , issuerID ) )
// This test was created to ensure that our calls to Vault
// returns a new Intermediate certificate and further calls
2023-04-03 15:40:33 +00:00
// to ActiveLeafSigningCert return the same new cert.
newActiveIntermediate , err := provider . ActiveLeafSigningCert ( )
2022-12-05 21:39:21 +00:00
require . NoError ( t , err )
require . NotEqual ( t , origIntermediate , newActiveIntermediate )
require . Equal ( t , intermediatePEM , newActiveIntermediate )
} )
}
2022-11-28 21:17:58 +00:00
func TestVaultCAProvider_VaultManaged ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
const vaultManagedPKIPolicy = `
path "/pki-root/" {
capabilities = [ "read" ]
}
path "/pki-root/root/sign-intermediate" {
capabilities = [ "update" ]
}
path "/pki-intermediate/*" {
capabilities = [ "create" , "read" , "update" , "delete" , "list" ]
}
path "auth/token/renew-self" {
capabilities = [ "update" ]
}
path "auth/token/lookup-self" {
capabilities = [ "read" ]
}
`
2022-12-06 16:06:36 +00:00
testVault := NewTestVaultServer ( t )
2022-11-28 21:17:58 +00:00
client := testVault . Client ( )
client . SetToken ( "root" )
// Mount pki root externally
require . NoError ( t , client . Sys ( ) . Mount ( "pki-root" , & vaultapi . MountInput {
Type : "pki" ,
Description : "root CA backend for Consul Connect" ,
Config : vaultapi . MountConfigInput {
MaxLeaseTTL : "12m" ,
} ,
} ) )
2022-12-06 16:06:36 +00:00
_ , err := client . Logical ( ) . Write ( "pki-root/root/generate/internal" , map [ string ] interface { } {
2022-11-28 21:17:58 +00:00
"common_name" : "testconsul" ,
} )
require . NoError ( t , err )
// Mount pki intermediate externally
require . NoError ( t , client . Sys ( ) . Mount ( "pki-intermediate" , & vaultapi . MountInput {
Type : "pki" ,
Description : "intermediate CA backend for Consul Connect" ,
Config : vaultapi . MountConfigInput {
MaxLeaseTTL : "6m" ,
} ,
} ) )
// Generate a policy and token for the VaultProvider to use
require . NoError ( t , client . Sys ( ) . PutPolicy ( "consul-ca" , vaultManagedPKIPolicy ) )
tcr := & vaultapi . TokenCreateRequest {
Policies : [ ] string { "consul-ca" } ,
}
secret , err := testVault . client . Auth ( ) . Token ( ) . Create ( tcr )
require . NoError ( t , err )
providerToken := secret . Auth . ClientToken
// We want to test the provider.Configure() step
2022-12-06 16:06:36 +00:00
_ = createVaultProvider ( t , true , testVault . Addr , providerToken , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2022-11-28 21:17:58 +00:00
}
func TestVaultCAProvider_ConsulManaged ( t * testing . T ) {
SkipIfVaultNotPresent ( t )
2022-12-06 16:06:36 +00:00
testVault := NewTestVaultServer ( t )
2022-11-28 21:17:58 +00:00
client := testVault . Client ( )
client . SetToken ( "root" )
// We do not configure any mounts and instead let Consul
// be responsible for mounting root and intermediate PKI
// Generate a policy and token for the VaultProvider to use
2022-12-06 16:06:36 +00:00
require . NoError ( t , client . Sys ( ) . PutPolicy ( "consul-ca" , pkiTestPolicy ( "pki-root" , "pki-intermediate" ) ) )
2022-11-28 21:17:58 +00:00
tcr := & vaultapi . TokenCreateRequest {
Policies : [ ] string { "consul-ca" } ,
}
secret , err := testVault . client . Auth ( ) . Token ( ) . Create ( tcr )
require . NoError ( t , err )
providerToken := secret . Auth . ClientToken
// We want to test the provider.Configure() step
2022-12-06 16:06:36 +00:00
_ = createVaultProvider ( t , true , testVault . Addr , providerToken , map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
} )
2022-11-28 21:17:58 +00:00
}
2023-10-10 13:53:00 +00:00
func TestVaultCAProvider_EnterpriseNamespace ( t * testing . T ) {
SkipIfVaultNotPresent ( t , vaultRequirements { Enterprise : true } )
t . Parallel ( )
cases := map [ string ] struct {
namespaces map [ string ] string
} {
"no configured namespaces" : { } ,
"only base namespace provided" : { namespaces : map [ string ] string { "Namespace" : "base-ns" } } ,
"only root namespace provided" : { namespaces : map [ string ] string { "RootPKINamespace" : "root-pki-ns" } } ,
"only intermediate namespace provided" : { namespaces : map [ string ] string { "IntermediatePKINamespace" : "int-pki-ns" } } ,
"base and root namespace provided" : {
namespaces : map [ string ] string {
"Namespace" : "base-ns" ,
"RootPKINamespace" : "root-pki-ns" ,
} ,
} ,
"base and intermediate namespace provided" : {
namespaces : map [ string ] string {
"Namespace" : "base-ns" ,
"IntermediatePKINamespace" : "int-pki-ns" ,
} ,
} ,
"root and intermediate namespace provided" : {
namespaces : map [ string ] string {
"RootPKINamespace" : "root-pki-ns" ,
"IntermediatePKINamespace" : "int-pki-ns" ,
} ,
} ,
"all namespaces provided" : {
namespaces : map [ string ] string {
"Namespace" : "base-ns" ,
"RootPKINamespace" : "root-pki-ns" ,
"IntermediatePKINamespace" : "int-pki-ns" ,
} ,
} ,
}
for name , c := range cases {
c := c
t . Run ( name , func ( t * testing . T ) {
t . Parallel ( )
testVault := NewTestVaultServer ( t )
token := "root"
providerConfig := map [ string ] any {
"RootPKIPath" : "pki-root/" ,
"IntermediatePKIPath" : "pki-intermediate/" ,
}
for k , v := range c . namespaces {
providerConfig [ k ] = v
}
if len ( c . namespaces ) > 0 {
// If explicit namespaces are provided, try to create the provider before any of the namespaces
// have been created. Verify that the provider fails to initialize.
provider , err := createVaultProviderE ( t , true , testVault . Addr , token , providerConfig )
require . Error ( t , err )
require . NotNil ( t , provider )
}
// Create the namespaces
client := testVault . Client ( )
client . SetToken ( token )
for _ , ns := range c . namespaces {
_ , err := client . Logical ( ) . Write ( fmt . Sprintf ( "/sys/namespaces/%s" , ns ) , map [ string ] any { } )
require . NoError ( t , err )
}
// Verify that once the namespaces have been created we are able to initialize the provider.
provider , err := createVaultProviderE ( t , true , testVault . Addr , token , providerConfig )
require . NoError ( t , err )
require . NotNil ( t , provider )
} )
}
}
2020-01-21 20:55:21 +00:00
func getIntermediateCertTTL ( t * testing . T , caConf * structs . CAConfiguration ) time . Duration {
t . Helper ( )
require . NotNil ( t , caConf )
require . NotNil ( t , caConf . Config )
iface , ok := caConf . Config [ "IntermediateCertTTL" ]
require . True ( t , ok )
ttlBytes , ok := iface . ( [ ] uint8 )
require . True ( t , ok )
ttlString := string ( ttlBytes )
dur , err := time . ParseDuration ( ttlString )
require . NoError ( t , err )
return dur
}
2022-12-06 16:06:36 +00:00
func createVaultProvider ( t * testing . T , isPrimary bool , addr , token string , rawConf map [ string ] any ) * VaultProvider {
t . Helper ( )
2023-10-10 13:53:00 +00:00
provider , err := createVaultProviderE ( t , isPrimary , addr , token , rawConf )
require . NoError ( t , err )
return provider
}
func createVaultProviderE ( t * testing . T , isPrimary bool , addr , token string , rawConf map [ string ] any ) ( * VaultProvider , error ) {
t . Helper ( )
2022-03-28 14:58:16 +00:00
cfg := vaultProviderConfig ( t , addr , token , rawConf )
provider := NewVaultProvider ( hclog . New ( nil ) )
if ! isPrimary {
cfg . IsPrimary = false
cfg . Datacenter = "dc2"
}
t . Cleanup ( provider . Stop )
2023-10-10 13:53:00 +00:00
if err := provider . Configure ( cfg ) ; err != nil {
return provider , err
}
2022-03-28 14:58:16 +00:00
if isPrimary {
2023-10-10 13:53:00 +00:00
if _ , err := provider . GenerateCAChain ( ) ; err != nil {
return provider , err
}
if _ , err := provider . GenerateLeafSigningCert ( ) ; err != nil {
return provider , err
}
2022-03-28 14:58:16 +00:00
}
2023-10-10 13:53:00 +00:00
return provider , nil
2022-03-28 14:58:16 +00:00
}
2022-12-06 16:06:36 +00:00
func vaultProviderConfig ( t * testing . T , addr , token string , rawConf map [ string ] any ) ProviderConfig {
t . Helper ( )
require . NotEmpty ( t , rawConf , "config map is required with at least %q and %q set" ,
"RootPKIPath" ,
"IntermediatePKIPath" )
conf := map [ string ] any {
"Address" : addr ,
"Token" : token ,
2019-09-23 17:04:40 +00:00
// Tests duration parsing after msgpack type mangling during raft apply.
"LeafCertTTL" : [ ] uint8 ( "72h" ) ,
}
2022-12-06 16:06:36 +00:00
hasRequired := false
if rawConf != nil {
_ , ok1 := rawConf [ "RootPKIPath" ]
_ , ok2 := rawConf [ "IntermediatePKIPath" ]
hasRequired = ok1 && ok2
}
if ! hasRequired {
t . Fatalf ( "The caller must provide both %q and %q config settings to avoid an incidental collision" ,
"RootPKIPath" ,
"IntermediatePKIPath" )
}
2019-09-23 17:04:40 +00:00
for k , v := range rawConf {
conf [ k ] = v
}
2019-11-18 14:22:19 +00:00
cfg := ProviderConfig {
ClusterID : connect . TestClusterID ,
Datacenter : "dc1" ,
IsPrimary : true ,
RawConfig : conf ,
}
2022-03-28 14:58:16 +00:00
return cfg
2019-09-23 17:04:40 +00:00
}