mirror of
https://github.com/status-im/consul.git
synced 2025-01-10 13:55:55 +00:00
5fb9df1640
* Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
780 lines
20 KiB
Go
780 lines
20 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package ca
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"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/hashicorp/vault/sdk/helper/jsonutil"
|
|
"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
|
|
authMethod structs.VaultAuthMethod
|
|
}{
|
|
"valid login data": {
|
|
authMethod: structs.VaultAuthMethod{},
|
|
},
|
|
"with role": {
|
|
expErr: nil,
|
|
authMethod: structs.VaultAuthMethod{Type: "aws", MountPath: "", Params: map[string]interface{}{"role": "test-role"}},
|
|
},
|
|
}
|
|
|
|
for name, c := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
ldg := &AWSLoginDataGenerator{credentials: credentials.AnonymousCredentials}
|
|
loginData, err := ldg.GenerateLoginData(&c.authMethod)
|
|
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)
|
|
}
|
|
|
|
if c.authMethod.Params["role"] != nil {
|
|
require.Equal(t, c.authMethod.Params["role"], loginData["role"])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVaultCAProvider_AzureAuthClient(t *testing.T) {
|
|
instance := instanceData{Compute: Compute{
|
|
Name: "a", ResourceGroupName: "b", SubscriptionID: "c", VMScaleSetName: "d",
|
|
}}
|
|
instanceJSON, err := json.Marshal(instance)
|
|
require.NoError(t, err)
|
|
identity := identityData{AccessToken: "a-jwt-token"}
|
|
identityJSON, err := json.Marshal(identity)
|
|
require.NoError(t, err)
|
|
|
|
msi := httptest.NewServer(http.HandlerFunc(
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
url := r.URL.Path
|
|
switch url {
|
|
case "/metadata/instance":
|
|
w.Write(instanceJSON)
|
|
case "/metadata/identity/oauth2/token":
|
|
w.Write(identityJSON)
|
|
default:
|
|
t.Errorf("unexpected testing URL: %s", url)
|
|
}
|
|
}))
|
|
|
|
origIn, origId := instanceEndpoint, identityEndpoint
|
|
instanceEndpoint = msi.URL + "/metadata/instance"
|
|
identityEndpoint = msi.URL + "/metadata/identity/oauth2/token"
|
|
defer func() {
|
|
instanceEndpoint, identityEndpoint = origIn, origId
|
|
}()
|
|
|
|
t.Run("get-metadata-instance-info", func(t *testing.T) {
|
|
md, err := getMetadataInfo(instanceEndpoint, nil)
|
|
require.NoError(t, err)
|
|
var testInstance instanceData
|
|
err = jsonutil.DecodeJSON(md, &testInstance)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testInstance, instance)
|
|
})
|
|
|
|
t.Run("get-metadata-identity-info", func(t *testing.T) {
|
|
md, err := getMetadataInfo(identityEndpoint, nil)
|
|
require.NoError(t, err)
|
|
var testIdentity identityData
|
|
err = jsonutil.DecodeJSON(md, &testIdentity)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testIdentity, identity)
|
|
})
|
|
|
|
cases := map[string]struct {
|
|
authMethod *structs.VaultAuthMethod
|
|
expData map[string]any
|
|
expErr error
|
|
}{
|
|
"legacy-case": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "azure",
|
|
Params: map[string]interface{}{
|
|
"role": "a",
|
|
"vm_name": "b",
|
|
"vmss_name": "c",
|
|
"resource_group_name": "d",
|
|
"subscription_id": "e",
|
|
"jwt": "f",
|
|
},
|
|
},
|
|
expData: map[string]any{
|
|
"role": "a",
|
|
"vm_name": "b",
|
|
"vmss_name": "c",
|
|
"resource_group_name": "d",
|
|
"subscription_id": "e",
|
|
"jwt": "f",
|
|
},
|
|
},
|
|
"base-case": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "azure",
|
|
Params: map[string]interface{}{
|
|
"role": "a-role",
|
|
"resource": "b-resource",
|
|
},
|
|
},
|
|
expData: map[string]any{
|
|
"role": "a-role",
|
|
"jwt": "a-jwt-token",
|
|
},
|
|
},
|
|
"no-role": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "azure",
|
|
Params: map[string]interface{}{
|
|
"resource": "b-resource",
|
|
},
|
|
},
|
|
expErr: fmt.Errorf("missing 'role' value"),
|
|
},
|
|
"no-resource": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "azure",
|
|
Params: map[string]interface{}{
|
|
"role": "a-role",
|
|
},
|
|
},
|
|
expErr: fmt.Errorf("missing 'resource' value"),
|
|
},
|
|
}
|
|
for name, c := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
auth, err := NewAzureAuthClient(c.authMethod)
|
|
if c.expErr != nil {
|
|
require.EqualError(t, err, c.expErr.Error())
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
if auth.LoginDataGen != nil {
|
|
data, err := auth.LoginDataGen(c.authMethod)
|
|
require.NoError(t, err)
|
|
require.Subset(t, data, c.expData)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVaultCAProvider_JwtAuthClient(t *testing.T) {
|
|
tokenF, err := os.CreateTemp("", "token-path")
|
|
require.NoError(t, err)
|
|
defer func() { os.Remove(tokenF.Name()) }()
|
|
_, err = tokenF.WriteString("test-token")
|
|
require.NoError(t, err)
|
|
err = tokenF.Close()
|
|
require.NoError(t, err)
|
|
|
|
cases := map[string]struct {
|
|
authMethod *structs.VaultAuthMethod
|
|
expData map[string]any
|
|
expErr error
|
|
}{
|
|
"base-case": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "jwt",
|
|
Params: map[string]any{
|
|
"role": "test-role",
|
|
"path": tokenF.Name(),
|
|
},
|
|
},
|
|
expData: map[string]any{
|
|
"role": "test-role",
|
|
"jwt": "test-token",
|
|
},
|
|
},
|
|
"no-role": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "jwt",
|
|
Params: map[string]any{},
|
|
},
|
|
expErr: fmt.Errorf("missing 'role' value"),
|
|
},
|
|
"no-path": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "jwt",
|
|
Params: map[string]any{
|
|
"role": "test-role",
|
|
},
|
|
},
|
|
expErr: fmt.Errorf("missing 'path' value"),
|
|
},
|
|
"no-path-but-jwt": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "jwt",
|
|
Params: map[string]any{
|
|
"role": "test-role",
|
|
"jwt": "test-jwt",
|
|
},
|
|
},
|
|
expData: map[string]any{
|
|
"role": "test-role",
|
|
"jwt": "test-jwt",
|
|
},
|
|
},
|
|
}
|
|
for name, c := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
auth, err := NewJwtAuthClient(c.authMethod)
|
|
if c.expErr != nil {
|
|
require.EqualError(t, c.expErr, err.Error())
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
if auth.LoginDataGen != nil {
|
|
data, err := auth.LoginDataGen(c.authMethod)
|
|
require.NoError(t, err)
|
|
require.Equal(t, c.expData, data)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVaultCAProvider_K8sAuthClient(t *testing.T) {
|
|
tokenF, err := os.CreateTemp("", "token-path")
|
|
require.NoError(t, err)
|
|
defer func() { os.Remove(tokenF.Name()) }()
|
|
_, err = tokenF.WriteString("test-token")
|
|
require.NoError(t, err)
|
|
err = tokenF.Close()
|
|
require.NoError(t, err)
|
|
|
|
cases := map[string]struct {
|
|
authMethod *structs.VaultAuthMethod
|
|
expData map[string]any
|
|
expErr error
|
|
}{
|
|
"base-case": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "kubernetes",
|
|
Params: map[string]any{
|
|
"role": "test-role",
|
|
"token_path": tokenF.Name(),
|
|
},
|
|
},
|
|
expData: map[string]any{
|
|
"role": "test-role",
|
|
"jwt": "test-token",
|
|
},
|
|
},
|
|
"legacy-case": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "kubernetes",
|
|
Params: map[string]any{
|
|
"role": "test-role",
|
|
"jwt": "test-token",
|
|
},
|
|
},
|
|
expData: map[string]any{
|
|
"role": "test-role",
|
|
"jwt": "test-token",
|
|
},
|
|
},
|
|
"no-role": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "kubernetes",
|
|
Params: map[string]any{},
|
|
},
|
|
expErr: fmt.Errorf("missing 'role' value"),
|
|
},
|
|
}
|
|
for name, c := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
auth, err := NewK8sAuthClient(c.authMethod)
|
|
if c.expErr != nil {
|
|
require.Error(t, err)
|
|
require.EqualError(t, c.expErr, err.Error())
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
if auth.LoginDataGen != nil {
|
|
data, err := auth.LoginDataGen(c.authMethod)
|
|
require.NoError(t, err)
|
|
require.Equal(t, c.expData, data)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVaultCAProvider_AppRoleAuthClient(t *testing.T) {
|
|
roleID, secretID := "test_role_id", "test_secret_id"
|
|
|
|
roleFd, err := os.CreateTemp("", "role")
|
|
require.NoError(t, err)
|
|
_, err = roleFd.WriteString(roleID)
|
|
require.NoError(t, err)
|
|
err = roleFd.Close()
|
|
require.NoError(t, err)
|
|
|
|
secretFd, err := os.CreateTemp("", "secret")
|
|
require.NoError(t, err)
|
|
_, err = secretFd.WriteString(secretID)
|
|
require.NoError(t, err)
|
|
err = secretFd.Close()
|
|
require.NoError(t, err)
|
|
|
|
roleIdPath := roleFd.Name()
|
|
secretIdPath := secretFd.Name()
|
|
|
|
defer func() {
|
|
os.Remove(secretFd.Name())
|
|
os.Remove(roleFd.Name())
|
|
}()
|
|
|
|
cases := map[string]struct {
|
|
authMethod *structs.VaultAuthMethod
|
|
expData map[string]any
|
|
expErr error
|
|
}{
|
|
"base-case": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "approle",
|
|
Params: map[string]any{
|
|
"role_id_file_path": roleIdPath,
|
|
"secret_id_file_path": secretIdPath,
|
|
},
|
|
},
|
|
expData: map[string]any{
|
|
"role_id": roleID,
|
|
"secret_id": secretID,
|
|
},
|
|
},
|
|
"optional-secret-left-out": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "approle",
|
|
Params: map[string]any{
|
|
"role_id_file_path": roleIdPath,
|
|
},
|
|
},
|
|
expData: map[string]any{
|
|
"role_id": roleID,
|
|
},
|
|
},
|
|
"missing-role-id-file-path": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "approle",
|
|
Params: map[string]any{},
|
|
},
|
|
expErr: fmt.Errorf("missing '%s' value", "role_id_file_path"),
|
|
},
|
|
"legacy-direct-values": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: "approle",
|
|
Params: map[string]any{
|
|
"role_id": "test-role",
|
|
"secret_id": "test-secret",
|
|
},
|
|
},
|
|
expData: map[string]any{
|
|
"role_id": "test-role",
|
|
"secret_id": "test-secret",
|
|
},
|
|
},
|
|
}
|
|
|
|
for k, c := range cases {
|
|
t.Run(k, func(t *testing.T) {
|
|
auth, err := NewAppRoleAuthClient(c.authMethod)
|
|
if c.expErr != nil {
|
|
require.Error(t, err)
|
|
require.EqualError(t, c.expErr, err.Error())
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
if auth.LoginDataGen != nil {
|
|
data, err := auth.LoginDataGen(c.authMethod)
|
|
require.NoError(t, err)
|
|
require.Equal(t, c.expData, data)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVaultCAProvider_AliCloudAuthClient(t *testing.T) {
|
|
// required as login parameters, will hang if not set
|
|
os.Setenv("ALICLOUD_ACCESS_KEY", "test-access-key")
|
|
os.Setenv("ALICLOUD_SECRET_KEY", "test-secret-key")
|
|
os.Setenv("ALICLOUD_ACCESS_KEY_STS_TOKEN", "test-access-token")
|
|
defer func() {
|
|
os.Unsetenv("ALICLOUD_ACCESS_KEY")
|
|
os.Unsetenv("ALICLOUD_SECRET_KEY")
|
|
os.Unsetenv("ALICLOUD_ACCESS_KEY_STS_TOKEN")
|
|
}()
|
|
cases := map[string]struct {
|
|
authMethod *structs.VaultAuthMethod
|
|
expQry map[string][]string
|
|
expErr error
|
|
}{
|
|
"base-case": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: VaultAuthMethodTypeAliCloud,
|
|
Params: map[string]interface{}{
|
|
"role": "test-role",
|
|
"region": "test-region",
|
|
},
|
|
},
|
|
expQry: map[string][]string{
|
|
"Action": {"GetCallerIdentity"},
|
|
"AccessKeyId": {"test-access-key"},
|
|
"RegionId": {"test-region"},
|
|
},
|
|
},
|
|
"no-role": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: VaultAuthMethodTypeAliCloud,
|
|
Params: map[string]interface{}{
|
|
"region": "test-region",
|
|
},
|
|
},
|
|
expErr: fmt.Errorf("role is required for AliCloud login"),
|
|
},
|
|
"no-region": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: VaultAuthMethodTypeAliCloud,
|
|
Params: map[string]interface{}{
|
|
"role": "test-role",
|
|
},
|
|
},
|
|
expErr: fmt.Errorf("region is required for AliCloud login"),
|
|
},
|
|
"legacy-case": {
|
|
authMethod: &structs.VaultAuthMethod{
|
|
Type: VaultAuthMethodTypeAliCloud,
|
|
Params: map[string]interface{}{
|
|
"access_key": "test-key",
|
|
"access_token": "test-token",
|
|
"secret_key": "test-secret-key",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, c := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
auth, err := NewAliCloudAuthClient(c.authMethod)
|
|
if c.expErr != nil {
|
|
require.Error(t, err)
|
|
require.EqualError(t, c.expErr, err.Error())
|
|
return
|
|
}
|
|
require.NotNil(t, auth)
|
|
|
|
if auth.LoginDataGen != nil {
|
|
encodedData, err := auth.LoginDataGen(c.authMethod)
|
|
require.NoError(t, err)
|
|
|
|
// identity_request_headers (json encoded headers)
|
|
rawheaders, err := base64.StdEncoding.DecodeString(
|
|
encodedData["identity_request_headers"].(string))
|
|
require.NoError(t, err)
|
|
headers := string(rawheaders)
|
|
require.Contains(t, headers, "User-Agent")
|
|
require.Contains(t, headers, "AlibabaCloud")
|
|
require.Contains(t, headers, "Content-Type")
|
|
require.Contains(t, headers, "x-acs-action")
|
|
require.Contains(t, headers, "GetCallerIdentity")
|
|
|
|
// identity_request_url (w/ query params)
|
|
rawurl, err := base64.StdEncoding.DecodeString(
|
|
encodedData["identity_request_url"].(string))
|
|
require.NoError(t, err)
|
|
requrl, err := url.Parse(string(rawurl))
|
|
require.NoError(t, err)
|
|
|
|
queries := requrl.Query()
|
|
require.Subset(t, queries, c.expQry, "query missing fields")
|
|
require.Equal(t, requrl.Hostname(), "sts.test-region.aliyuncs.com")
|
|
}
|
|
})
|
|
}
|
|
}
|