304 lines
7.7 KiB
Go
304 lines
7.7 KiB
Go
|
package ca
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"strconv"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||
|
"github.com/hashicorp/consul/agent/structs"
|
||
|
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
||
|
"github.com/hashicorp/vault/api/auth/gcp"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
func TestVaultCAProvider_GCPAuthClient(t *testing.T) {
|
||
|
cases := map[string]struct {
|
||
|
authMethod *structs.VaultAuthMethod
|
||
|
isExplicit bool
|
||
|
expErr error
|
||
|
}{
|
||
|
"explicit config": {
|
||
|
authMethod: &structs.VaultAuthMethod{
|
||
|
Type: "gcp",
|
||
|
Params: map[string]interface{}{
|
||
|
"role": "test-role",
|
||
|
"jwt": "test-jwt",
|
||
|
},
|
||
|
},
|
||
|
isExplicit: true,
|
||
|
},
|
||
|
"derived iam auth": {
|
||
|
authMethod: &structs.VaultAuthMethod{
|
||
|
Type: "gcp",
|
||
|
Params: map[string]interface{}{
|
||
|
"type": "iam",
|
||
|
"role": "test-role",
|
||
|
"service_account_email": "test@google.cloud",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
"derived gce auth": {
|
||
|
authMethod: &structs.VaultAuthMethod{
|
||
|
Type: "gcp",
|
||
|
Params: map[string]interface{}{
|
||
|
"type": "gce",
|
||
|
"role": "test-role",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
"derived without role": {
|
||
|
authMethod: &structs.VaultAuthMethod{
|
||
|
Type: "gcp",
|
||
|
Params: map[string]interface{}{
|
||
|
"type": "gce",
|
||
|
},
|
||
|
},
|
||
|
expErr: fmt.Errorf("failed to create a new Vault GCP auth client"),
|
||
|
},
|
||
|
"invalid config": {
|
||
|
authMethod: &structs.VaultAuthMethod{
|
||
|
Type: "gcp",
|
||
|
Params: map[string]interface{}{
|
||
|
"invalid": true,
|
||
|
},
|
||
|
},
|
||
|
expErr: fmt.Errorf("misconfiguration of GCP auth parameters: invalid type for field"),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for name, c := range cases {
|
||
|
t.Run(name, func(t *testing.T) {
|
||
|
auth, err := NewGCPAuthClient(c.authMethod)
|
||
|
if c.expErr != nil {
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), c.expErr.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
require.NoError(t, err)
|
||
|
require.NotNil(t, auth)
|
||
|
|
||
|
if c.isExplicit {
|
||
|
// in this case a JWT is provided so we'll call the login API directly using a VaultAuthClient.
|
||
|
_ = auth.(*VaultAuthClient)
|
||
|
} else {
|
||
|
// in this case we delegate to gcp.GCPAuth to perform the login.
|
||
|
_ = auth.(*gcp.GCPAuth)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestVaultCAProvider_AWSAuthClient(t *testing.T) {
|
||
|
cases := map[string]struct {
|
||
|
authMethod *structs.VaultAuthMethod
|
||
|
expLoginPath string
|
||
|
hasLDG bool
|
||
|
}{
|
||
|
"explicit aws ec2 identity": {
|
||
|
authMethod: &structs.VaultAuthMethod{
|
||
|
Type: "aws",
|
||
|
Params: map[string]interface{}{
|
||
|
"role": "test-role",
|
||
|
"identity": "test-identity",
|
||
|
"signature": "test-signature",
|
||
|
},
|
||
|
},
|
||
|
expLoginPath: "auth/aws/login",
|
||
|
},
|
||
|
"explicit aws ec2 pkcs7": {
|
||
|
authMethod: &structs.VaultAuthMethod{
|
||
|
Type: "aws",
|
||
|
MountPath: "custom-aws",
|
||
|
Params: map[string]interface{}{
|
||
|
"role": "test-role",
|
||
|
"pkcs7": "test-pkcs7",
|
||
|
},
|
||
|
},
|
||
|
expLoginPath: "auth/custom-aws/login",
|
||
|
},
|
||
|
"derived aws login data": {
|
||
|
authMethod: &structs.VaultAuthMethod{
|
||
|
Type: "aws",
|
||
|
Params: map[string]interface{}{
|
||
|
"role": "test-role",
|
||
|
"type": "ec2",
|
||
|
"region": "test-region",
|
||
|
},
|
||
|
},
|
||
|
expLoginPath: "auth/aws/login",
|
||
|
hasLDG: true,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for name, c := range cases {
|
||
|
t.Run(name, func(t *testing.T) {
|
||
|
if c.authMethod.MountPath == "" {
|
||
|
c.authMethod.MountPath = c.authMethod.Type
|
||
|
}
|
||
|
auth := NewAWSAuthClient(c.authMethod)
|
||
|
require.Equal(t, c.authMethod, auth.AuthMethod)
|
||
|
require.Equal(t, c.expLoginPath, auth.LoginPath)
|
||
|
if c.hasLDG {
|
||
|
require.NotNil(t, auth.LoginDataGen)
|
||
|
} else {
|
||
|
require.Nil(t, auth.LoginDataGen)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestVaultCAProvider_AWSCredentialsConfig(t *testing.T) {
|
||
|
cases := map[string]struct {
|
||
|
params map[string]interface{}
|
||
|
envVars map[string]string
|
||
|
expCreds *awsutil.CredentialsConfig
|
||
|
expErr error
|
||
|
expRegion string
|
||
|
}{
|
||
|
"valid config": {
|
||
|
params: map[string]interface{}{
|
||
|
"access_key": "access key",
|
||
|
"secret_key": "secret key",
|
||
|
"session_token": "session token",
|
||
|
"iam_endpoint": "iam endpoint",
|
||
|
"sts_endpoint": "sts endpoint",
|
||
|
"region": "region",
|
||
|
"filename": "filename",
|
||
|
"profile": "profile",
|
||
|
"role_arn": "role arn",
|
||
|
"role_session_name": "role session name",
|
||
|
"web_identity_token_file": "web identity token file",
|
||
|
"header_value": "header value",
|
||
|
"max_retries": "13",
|
||
|
},
|
||
|
expCreds: &awsutil.CredentialsConfig{
|
||
|
AccessKey: "access key",
|
||
|
SecretKey: "secret key",
|
||
|
SessionToken: "session token",
|
||
|
IAMEndpoint: "iam endpoint",
|
||
|
STSEndpoint: "sts endpoint",
|
||
|
Region: "region",
|
||
|
Filename: "filename",
|
||
|
Profile: "profile",
|
||
|
RoleARN: "role arn",
|
||
|
RoleSessionName: "role session name",
|
||
|
WebIdentityTokenFile: "web identity token file",
|
||
|
},
|
||
|
},
|
||
|
"default region": {
|
||
|
params: map[string]interface{}{},
|
||
|
expCreds: &awsutil.CredentialsConfig{},
|
||
|
expRegion: "us-east-1",
|
||
|
},
|
||
|
"env AWS_REGION": {
|
||
|
params: map[string]interface{}{},
|
||
|
envVars: map[string]string{"AWS_REGION": "us-west-1"},
|
||
|
expCreds: &awsutil.CredentialsConfig{},
|
||
|
expRegion: "us-west-1",
|
||
|
},
|
||
|
"env AWS_DEFAULT_REGION": {
|
||
|
params: map[string]interface{}{},
|
||
|
envVars: map[string]string{"AWS_DEFAULT_REGION": "us-west-2"},
|
||
|
expCreds: &awsutil.CredentialsConfig{},
|
||
|
expRegion: "us-west-2",
|
||
|
},
|
||
|
"both AWS_REGION and AWS_DEFAULT_REGION": {
|
||
|
params: map[string]interface{}{},
|
||
|
envVars: map[string]string{
|
||
|
"AWS_REGION": "us-west-1",
|
||
|
"AWS_DEFAULT_REGION": "us-west-2",
|
||
|
},
|
||
|
expCreds: &awsutil.CredentialsConfig{},
|
||
|
expRegion: "us-west-1",
|
||
|
},
|
||
|
"invalid config": {
|
||
|
params: map[string]interface{}{
|
||
|
"invalid": true,
|
||
|
},
|
||
|
expErr: fmt.Errorf("misconfiguration of AWS auth parameters: invalid type for field"),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for name, c := range cases {
|
||
|
t.Run(name, func(t *testing.T) {
|
||
|
if c.envVars != nil {
|
||
|
for k, v := range c.envVars {
|
||
|
require.NoError(t, os.Setenv(k, v))
|
||
|
}
|
||
|
t.Cleanup(func() {
|
||
|
for k := range c.envVars {
|
||
|
os.Unsetenv(k)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
creds, headerValue, err := newAWSCredentialsConfig(c.params)
|
||
|
if c.expErr != nil {
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), c.expErr.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// If a header value was provided in the params then make sure it was returned.
|
||
|
if val, ok := c.params["header_value"]; ok {
|
||
|
require.Equal(t, val, headerValue)
|
||
|
} else {
|
||
|
require.Empty(t, headerValue)
|
||
|
}
|
||
|
|
||
|
if val, ok := c.params["max_retries"]; ok {
|
||
|
mr, err := strconv.Atoi(val.(string))
|
||
|
require.NoError(t, err)
|
||
|
c.expCreds.MaxRetries = &mr
|
||
|
} else {
|
||
|
creds.MaxRetries = nil
|
||
|
}
|
||
|
|
||
|
require.NotNil(t, creds.HTTPClient)
|
||
|
creds.HTTPClient = nil
|
||
|
|
||
|
if c.expRegion != "" {
|
||
|
c.expCreds.Region = c.expRegion
|
||
|
}
|
||
|
require.Equal(t, *c.expCreds, *creds)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestVaultCAProvider_AWSLoginDataGenerator(t *testing.T) {
|
||
|
cases := map[string]struct {
|
||
|
expErr error
|
||
|
}{
|
||
|
"valid login data": {},
|
||
|
}
|
||
|
|
||
|
for name, c := range cases {
|
||
|
t.Run(name, func(t *testing.T) {
|
||
|
ldg := &AWSLoginDataGenerator{credentials: credentials.AnonymousCredentials}
|
||
|
loginData, err := ldg.GenerateLoginData(&structs.VaultAuthMethod{})
|
||
|
if c.expErr != nil {
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), c.expErr.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
keys := []string{
|
||
|
"iam_http_request_method",
|
||
|
"iam_request_url",
|
||
|
"iam_request_headers",
|
||
|
"iam_request_body",
|
||
|
}
|
||
|
|
||
|
for _, key := range keys {
|
||
|
val, exists := loginData[key]
|
||
|
require.True(t, exists, "missing expected key: %s", key)
|
||
|
require.NotEmpty(t, val, "expected non-empty value for key: %s", key)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|