test(e2e): Implement Vault LDAP API and CLI tests

This commit is contained in:
Hugo authored and Danielle Miu committed 2025-11-05 16:20:44 -05:00
1 parent 9652ebe032
commit 9575d5f239
13 files changed
+567 -13

No files matched your search

@@ -113,6 +113,17 @@ scenario "e2e_docker_base_with_vault" {
}
}
step "create_ldap_server" {
module = module.docker_ldap
depends_on = [
step.create_docker_network
]
variables {
image_name = "${var.docker_mirror}/osixia/openldap:latest"
network_name = [local.network_cluster]
}
}
step "run_e2e_test" {
module = module.test_e2e_docker
depends_on = [
@@ -141,6 +152,13 @@ scenario "e2e_docker_base_with_vault" {
vault_root_token = step.create_vault.token
vault_port = step.create_vault.port
max_page_size = step.create_boundary.max_page_size
ldap_address = step.create_ldap_server.address
ldap_domain_dn = step.create_ldap_server.domain_dn
ldap_admin_dn = step.create_ldap_server.admin_dn
ldap_admin_password = step.create_ldap_server.admin_password
ldap_user_name = step.create_ldap_server.user_name
ldap_user_password = step.create_ldap_server.user_password
ldap_group_name = step.create_ldap_server.group_name
}
}
}
+4 -2
View File
@@ -6,12 +6,14 @@ package boundary
import (
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/api/targets"
)
// ConnectCliOutput parses the json response from running `boundary connect`
type ConnectCliOutput struct {
Port int `json:"port"`
Address string `json:"address"`
Port int `json:"port"`
Address string `json:"address"`
Credentials []*targets.SessionCredential `json:"credentials"`
}
// AuthenticateCliOutput parses the json response from running `boundary authenticate`
@@ -333,6 +333,54 @@ func CreateStaticCredentialPasswordApi(t testing.TB, ctx context.Context, client
return credentialId, nil
}
func CreateVaultLdapCredentialLibraryApi(t testing.TB, ctx context.Context, client *api.Client, credentialStoreId string, vaultPath string) (string, error) {
name, err := base62.Random(16)
if err != nil {
return "", err
}
res, err := credentiallibraries.NewClient(client).Create(ctx, "vault-ldap", credentialStoreId,
credentiallibraries.WithName(fmt.Sprintf("e2e vault credential library %s", name)),
credentiallibraries.WithVaultLdapCredentialLibraryPath(vaultPath),
)
if err != nil {
return "", err
}
return res.GetItem().Id, nil
}
func CreateVaultLdapCredentialLibraryCli(t testing.TB, ctx context.Context, credentialStoreId string, vaultPath string) (string, error) {
name, err := base62.Random(16)
if err != nil {
return "", err
}
output := e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"credential-libraries", "create", "vault-ldap",
"-credential-store-id", credentialStoreId,
"-vault-path", vaultPath,
"-name", fmt.Sprintf("e2e vault credential library %s", name),
"-description", "e2e",
"-format", "json",
),
)
if output.Err != nil {
return "", fmt.Errorf("%w: %s", output.Err, string(output.Stderr))
}
var createCredentialLibraryResult credentiallibraries.CredentialLibraryCreateResult
err = json.Unmarshal(output.Stdout, &createCredentialLibraryResult)
if err != nil {
return "", err
}
credentialLibraryId := createCredentialLibraryResult.Item.Id
t.Logf("Created Vault LDAP Credential Library: %s", credentialLibraryId)
return credentialLibraryId, nil
}
// CreateStaticCredentialPasswordDomainApi uses the API to create a new username-password-domain credential in the
// provided static credential store.
// Returns the id of the new credential
@@ -0,0 +1,368 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package base_with_vault_test
import (
"context"
"encoding/json"
"fmt"
"slices"
"strconv"
"strings"
"testing"
"github.com/hashicorp/boundary/api/scopes"
"github.com/hashicorp/boundary/api/targets"
"github.com/hashicorp/boundary/testing/internal/e2e"
"github.com/hashicorp/boundary/testing/internal/e2e/boundary"
"github.com/hashicorp/boundary/testing/internal/e2e/vault"
"github.com/stretchr/testify/require"
)
// TestApiVaultLdapCredentialLibrary uses the Boundary API to test CRUDL on a
// Vault LDAP credential library. Additionally, it tests assigning it as a
// brokered credential source on a TCP target and connecting.
func TestApiVaultLdapCredentialLibrary(t *testing.T) {
e2e.MaybeSkipTest(t)
c, err := loadTestConfig()
require.NoError(t, err)
require.NotNil(t, c)
client, err := boundary.NewApiClient()
require.NoError(t, err)
require.NotNil(t, client)
orgId, err := boundary.CreateOrgApi(t, t.Context(), client)
require.NoError(t, err)
t.Cleanup(func() {
_, err := scopes.NewClient(client).Delete(context.Background(), orgId)
require.NoError(t, err)
})
projectId, err := boundary.CreateProjectApi(t, t.Context(), client, orgId)
require.NoError(t, err)
// Create TCP target.
targetPort, err := strconv.ParseUint(c.TargetPort, 10, 32)
require.NoError(t, err)
targetId, err := boundary.CreateTargetApi(t, t.Context(), client, projectId, "tcp",
targets.WithTcpTargetDefaultPort(uint32(targetPort)),
targets.WithTcpTargetDefaultClientPort(uint32(targetPort)),
)
require.NoError(t, err)
// Create host catalog, host set and host; Add host set to the target as a
// host source.
hostCatalogId, err := boundary.CreateHostCatalogApi(t, t.Context(), client, projectId)
require.NoError(t, err)
hostSetId, err := boundary.CreateHostSetApi(t, t.Context(), client, hostCatalogId)
require.NoError(t, err)
hostId, err := boundary.CreateHostApi(t, t.Context(), client, hostCatalogId, c.TargetAddress)
require.NoError(t, err)
err = boundary.AddHostToHostSetApi(t, t.Context(), client, hostSetId, hostId)
require.NoError(t, err)
err = boundary.AddHostSourceToTargetApi(t, t.Context(), client, targetId, hostSetId)
require.NoError(t, err)
// Configure Vault for LDAP.
boundaryPolicyName, _, ldapPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
t.Cleanup(func() {
output := e2e.RunCommand(context.Background(), "vault",
e2e.WithArgs("policy", "delete", boundaryPolicyName),
)
require.NoError(t, output.Err, string(output.Stderr))
})
ldapPolicyName := vault.SetupLdap(t, c.VaultLdapPath, ldapPolicyFilePath,
c.LdapAddress, c.LdapAdminDn, c.LdapAdminPassword,
c.LdapDomainDn, c.LdapUserName, c.LdapGroupName,
)
t.Cleanup(func() {
// Destroy LDAP secrets engine and LDAP policy.
output := e2e.RunCommand(context.Background(), "vault",
e2e.WithArgs("secrets", "disable", c.VaultLdapPath),
)
require.NoError(t, output.Err, string(output.Stderr))
output = e2e.RunCommand(context.Background(), "vault",
e2e.WithArgs("policy", "delete", ldapPolicyName),
)
require.NoError(t, output.Err, string(output.Stderr))
})
// Create Vault token for Boundary.
output := e2e.RunCommand(t.Context(), "vault",
e2e.WithArgs(
"token", "create",
"-no-default-policy=true",
fmt.Sprintf("-policy=%s", boundaryPolicyName),
fmt.Sprintf("-policy=%s", ldapPolicyName),
"-orphan=true",
"-period=20m",
"-renewable=true",
"-format=json",
),
)
require.NoError(t, output.Err, string(output.Stderr))
var tokenCreateResult vault.CreateTokenResponse
err = json.Unmarshal(output.Stdout, &tokenCreateResult)
require.NoError(t, err)
vaultToken := tokenCreateResult.Auth.Client_Token
// Create two Vault LDAP credential libraries: One with static, one with
// dynamic creds.
storeId, err := boundary.CreateCredentialStoreVaultApi(t, t.Context(), client, projectId, c.VaultAddr, vaultToken)
require.NoError(t, err)
staticCredVclId, err := boundary.CreateVaultLdapCredentialLibraryApi(t, t.Context(), client, storeId, fmt.Sprintf("%s/static-cred/%s", c.VaultLdapPath, c.LdapUserName))
require.NoError(t, err)
dynamicCredVclId, err := boundary.CreateVaultLdapCredentialLibraryApi(t, t.Context(), client, storeId, fmt.Sprintf("%s/creds/%s", c.VaultLdapPath, c.LdapGroupName))
require.NoError(t, err)
err = boundary.AddBrokeredCredentialSourceToTargetApi(t, t.Context(), client, targetId, staticCredVclId)
require.NoError(t, err)
err = boundary.AddBrokeredCredentialSourceToTargetApi(t, t.Context(), client, targetId, dynamicCredVclId)
require.NoError(t, err)
// Authorize session and assert that session credentials exist.
sar, err := targets.NewClient(client).AuthorizeSession(t.Context(), targetId)
require.NoError(t, err)
require.NotNil(t, sar)
sa, err := sar.GetSessionAuthorization()
require.NoError(t, err)
require.NotNil(t, sa)
require.Len(t, sa.Credentials, 2) // One for static, one for dynamic role.
slices.SortFunc(sa.Credentials, func(a *targets.SessionCredential, b *targets.SessionCredential) int {
// Sort by static role credential library first.
if strings.EqualFold(staticCredVclId, a.CredentialSource.Id) {
return -1
}
return 1
})
// Determine the LDAP server's domain using LDAP dn. LdapDomainDn is a
// string like "dc=subdomain,dc=domain,dc=tld".
expDomain := strings.ReplaceAll(
strings.ReplaceAll(c.LdapDomainDn, "dc=", ""), // Transforms into "subdomain,domain,tld".
",", ".", // Then replace commas with periods to get "subdomain.domain.tld".
)
// Static credential.
require.NotEmpty(t, sa.Credentials[0].Credential)
require.Contains(t, sa.Credentials[0].Credential, "username")
require.Contains(t, sa.Credentials[0].Credential, "password")
require.Contains(t, sa.Credentials[0].Credential, "domain")
require.EqualValues(t, c.LdapUserName, sa.Credentials[0].Credential["username"])
require.NotEmpty(t, sa.Credentials[0].Credential["password"])
require.EqualValues(t, expDomain, sa.Credentials[0].Credential["domain"])
require.NotNil(t, sa.Credentials[0].CredentialSource)
require.EqualValues(t, staticCredVclId, sa.Credentials[0].CredentialSource.Id)
require.Contains(t, sa.Credentials[0].CredentialSource.Name, "e2e vault credential library")
require.EqualValues(t, "vault-ldap", sa.Credentials[0].CredentialSource.Type)
require.EqualValues(t, storeId, sa.Credentials[0].CredentialSource.CredentialStoreId)
require.EqualValues(t, "username_password_domain", sa.Credentials[0].CredentialSource.CredentialType)
require.NotNil(t, sa.Credentials[0].Secret)
require.NotEmpty(t, sa.Credentials[0].Secret.Decoded)
require.Contains(t, sa.Credentials[0].Secret.Decoded, "username")
require.Contains(t, sa.Credentials[0].Secret.Decoded, "password")
require.Contains(t, sa.Credentials[0].Secret.Decoded, "dn")
require.EqualValues(t, c.LdapUserName, sa.Credentials[0].Secret.Decoded["username"])
require.EqualValues(t, sa.Credentials[0].Credential["password"], sa.Credentials[0].Secret.Decoded["password"])
require.EqualValues(t, fmt.Sprintf("cn=%s,%s", c.LdapUserName, c.LdapDomainDn), sa.Credentials[0].Secret.Decoded["dn"])
// Dynamic credential.
require.NotEmpty(t, sa.Credentials[1].Credential)
require.Contains(t, sa.Credentials[1].Credential, "username")
require.Contains(t, sa.Credentials[1].Credential, "password")
require.Contains(t, sa.Credentials[1].Credential, "domain")
require.Contains(t, sa.Credentials[1].Credential["username"], fmt.Sprintf("b_token_%s_", c.LdapGroupName))
require.NotEmpty(t, sa.Credentials[1].Credential["password"])
require.EqualValues(t, expDomain, sa.Credentials[1].Credential["domain"])
require.NotNil(t, sa.Credentials[1].CredentialSource)
require.EqualValues(t, dynamicCredVclId, sa.Credentials[1].CredentialSource.Id)
require.Contains(t, sa.Credentials[1].CredentialSource.Name, "e2e vault credential library")
require.EqualValues(t, "vault-ldap", sa.Credentials[1].CredentialSource.Type)
require.EqualValues(t, storeId, sa.Credentials[1].CredentialSource.CredentialStoreId)
require.EqualValues(t, "username_password_domain", sa.Credentials[1].CredentialSource.CredentialType)
require.NotNil(t, sa.Credentials[1].Secret)
require.NotEmpty(t, sa.Credentials[1].Secret.Decoded)
require.Contains(t, sa.Credentials[1].Secret.Decoded, "username")
require.Contains(t, sa.Credentials[1].Secret.Decoded, "password")
require.Contains(t, sa.Credentials[1].Secret.Decoded, "distinguished_names")
require.EqualValues(t, sa.Credentials[1].Credential["username"], sa.Credentials[1].Secret.Decoded["username"])
require.EqualValues(t, sa.Credentials[1].Credential["password"], sa.Credentials[1].Secret.Decoded["password"])
distinguishedNames, ok := sa.Credentials[1].Secret.Decoded["distinguished_names"].([]any)
require.True(t, ok)
require.NotEmpty(t, distinguishedNames)
require.Contains(t, distinguishedNames, fmt.Sprintf("cn=%s,%s", sa.Credentials[1].Secret.Decoded["username"], c.LdapDomainDn))
require.Contains(t, distinguishedNames, fmt.Sprintf("cn=%s,%s", c.LdapGroupName, c.LdapDomainDn))
}
// TestCliVaultLdapCredentialLibrary uses the Boundary CLI to test CRUDL on a
// Vault LDAP credential library. Additionally, it tests assigning it as a
// brokered credential source on a TCP target and connecting.
func TestCliVaultLdapCredentialLibrary(t *testing.T) {
e2e.MaybeSkipTest(t)
c, err := loadTestConfig()
require.NoError(t, err)
boundary.AuthenticateAdminCli(t, t.Context())
orgId, err := boundary.CreateOrgCli(t, t.Context())
require.NoError(t, err)
t.Cleanup(func() {
boundary.AuthenticateAdminCli(t, context.Background())
output := e2e.RunCommand(context.Background(), "boundary", e2e.WithArgs("scopes", "delete", "-id", orgId))
require.NoError(t, output.Err, string(output.Stderr))
})
projectId, err := boundary.CreateProjectCli(t, t.Context(), orgId)
require.NoError(t, err)
hostCatalogId, err := boundary.CreateHostCatalogCli(t, t.Context(), projectId)
require.NoError(t, err)
hostSetId, err := boundary.CreateHostSetCli(t, t.Context(), hostCatalogId)
require.NoError(t, err)
hostId, err := boundary.CreateHostCli(t, t.Context(), hostCatalogId, c.TargetAddress)
require.NoError(t, err)
err = boundary.AddHostToHostSetCli(t, t.Context(), hostSetId, hostId)
require.NoError(t, err)
targetId, err := boundary.CreateTargetCli(t, t.Context(), projectId, c.TargetPort)
require.NoError(t, err)
err = boundary.AddHostSourceToTargetCli(t, t.Context(), targetId, hostSetId)
require.NoError(t, err)
// Configure Vault for LDAP.
boundaryPolicyName, _, ldapPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
t.Cleanup(func() {
output := e2e.RunCommand(context.Background(), "vault",
e2e.WithArgs("policy", "delete", boundaryPolicyName),
)
require.NoError(t, output.Err, string(output.Stderr))
})
ldapPolicyName := vault.SetupLdap(t, c.VaultLdapPath, ldapPolicyFilePath,
c.LdapAddress, c.LdapAdminDn, c.LdapAdminPassword,
c.LdapDomainDn, c.LdapUserName, c.LdapGroupName,
)
t.Cleanup(func() {
// Destroy LDAP secrets engine and LDAP policy.
output := e2e.RunCommand(context.Background(), "vault",
e2e.WithArgs("secrets", "disable", c.VaultLdapPath),
)
require.NoError(t, output.Err, string(output.Stderr))
output = e2e.RunCommand(context.Background(), "vault",
e2e.WithArgs("policy", "delete", ldapPolicyName),
)
require.NoError(t, output.Err, string(output.Stderr))
})
// Create Vault token for Boundary.
output := e2e.RunCommand(t.Context(), "vault",
e2e.WithArgs(
"token", "create",
"-no-default-policy=true",
fmt.Sprintf("-policy=%s", boundaryPolicyName),
fmt.Sprintf("-policy=%s", ldapPolicyName),
"-orphan=true",
"-period=20m",
"-renewable=true",
"-format=json",
),
)
require.NoError(t, output.Err, string(output.Stderr))
var tokenCreateResult vault.CreateTokenResponse
err = json.Unmarshal(output.Stdout, &tokenCreateResult)
require.NoError(t, err)
vaultToken := tokenCreateResult.Auth.Client_Token
// Create two Vault LDAP credential libraries: One with static, one with
// dynamic creds.
storeId, err := boundary.CreateCredentialStoreVaultCli(t, t.Context(), projectId, c.VaultAddr, vaultToken)
require.NoError(t, err)
staticCredVclId, err := boundary.CreateVaultLdapCredentialLibraryCli(t, t.Context(), storeId, fmt.Sprintf("%s/static-cred/%s", c.VaultLdapPath, c.LdapUserName))
require.NoError(t, err)
dynamicCredVclId, err := boundary.CreateVaultLdapCredentialLibraryCli(t, t.Context(), storeId, fmt.Sprintf("%s/creds/%s", c.VaultLdapPath, c.LdapGroupName))
require.NoError(t, err)
err = boundary.AddBrokeredCredentialSourceToTargetCli(t, t.Context(), targetId, staticCredVclId)
require.NoError(t, err)
err = boundary.AddBrokeredCredentialSourceToTargetCli(t, t.Context(), targetId, dynamicCredVclId)
require.NoError(t, err)
// Connect and assert that session credentials exist.
out := boundary.ConnectCli(t, t.Context(), targetId)
require.NotEmpty(t, out)
require.Len(t, out.Credentials, 2) // One for static, one for dynamic role.
slices.SortFunc(out.Credentials, func(a *targets.SessionCredential, b *targets.SessionCredential) int {
// Sort by static role credential library first.
if strings.EqualFold(staticCredVclId, a.CredentialSource.Id) {
return -1
}
return 1
})
// Determine the LDAP server's domain using LDAP dn. LdapDomainDn is a
// string like "dc=subdomain,dc=domain,dc=tld".
expDomain := strings.ReplaceAll(
strings.ReplaceAll(c.LdapDomainDn, "dc=", ""), // Transforms into "subdomain,domain,tld".
",", ".", // Then replace commas with periods to get "subdomain.domain.tld".
)
// Static credential.
require.NotEmpty(t, out.Credentials[0].Credential)
require.Contains(t, out.Credentials[0].Credential, "username")
require.Contains(t, out.Credentials[0].Credential, "password")
require.Contains(t, out.Credentials[0].Credential, "domain")
require.EqualValues(t, c.LdapUserName, out.Credentials[0].Credential["username"])
require.NotEmpty(t, out.Credentials[0].Credential["password"])
require.EqualValues(t, expDomain, out.Credentials[0].Credential["domain"])
require.NotNil(t, out.Credentials[0].CredentialSource)
require.EqualValues(t, staticCredVclId, out.Credentials[0].CredentialSource.Id)
require.Contains(t, out.Credentials[0].CredentialSource.Name, "e2e vault credential library")
require.EqualValues(t, "vault-ldap", out.Credentials[0].CredentialSource.Type)
require.EqualValues(t, storeId, out.Credentials[0].CredentialSource.CredentialStoreId)
require.EqualValues(t, "username_password_domain", out.Credentials[0].CredentialSource.CredentialType)
require.NotNil(t, out.Credentials[0].Secret)
require.NotEmpty(t, out.Credentials[0].Secret.Decoded)
require.Contains(t, out.Credentials[0].Secret.Decoded, "username")
require.Contains(t, out.Credentials[0].Secret.Decoded, "password")
require.Contains(t, out.Credentials[0].Secret.Decoded, "dn")
require.EqualValues(t, c.LdapUserName, out.Credentials[0].Secret.Decoded["username"])
require.EqualValues(t, out.Credentials[0].Credential["password"], out.Credentials[0].Secret.Decoded["password"])
require.EqualValues(t, fmt.Sprintf("cn=%s,%s", c.LdapUserName, c.LdapDomainDn), out.Credentials[0].Secret.Decoded["dn"])
// Dynamic credential.
require.NotEmpty(t, out.Credentials[1].Credential)
require.Contains(t, out.Credentials[1].Credential, "username")
require.Contains(t, out.Credentials[1].Credential, "password")
require.Contains(t, out.Credentials[1].Credential, "domain")
require.Contains(t, out.Credentials[1].Credential["username"], fmt.Sprintf("b_token_%s_", c.LdapGroupName))
require.NotEmpty(t, out.Credentials[1].Credential["password"])
require.EqualValues(t, expDomain, out.Credentials[1].Credential["domain"])
require.NotNil(t, out.Credentials[1].CredentialSource)
require.EqualValues(t, dynamicCredVclId, out.Credentials[1].CredentialSource.Id)
require.Contains(t, out.Credentials[1].CredentialSource.Name, "e2e vault credential library")
require.EqualValues(t, "vault-ldap", out.Credentials[1].CredentialSource.Type)
require.EqualValues(t, storeId, out.Credentials[1].CredentialSource.CredentialStoreId)
require.EqualValues(t, "username_password_domain", out.Credentials[1].CredentialSource.CredentialType)
require.NotNil(t, out.Credentials[1].Secret)
require.NotEmpty(t, out.Credentials[1].Secret.Decoded)
require.Contains(t, out.Credentials[1].Secret.Decoded, "username")
require.Contains(t, out.Credentials[1].Secret.Decoded, "password")
require.Contains(t, out.Credentials[1].Secret.Decoded, "distinguished_names")
require.EqualValues(t, out.Credentials[1].Credential["username"], out.Credentials[1].Secret.Decoded["username"])
require.EqualValues(t, out.Credentials[1].Credential["password"], out.Credentials[1].Secret.Decoded["password"])
distinguishedNames, ok := out.Credentials[1].Secret.Decoded["distinguished_names"].([]any)
require.True(t, ok)
require.NotEmpty(t, distinguishedNames)
require.Contains(t, distinguishedNames, fmt.Sprintf("cn=%s,%s", out.Credentials[1].Secret.Decoded["username"], c.LdapDomainDn))
require.Contains(t, distinguishedNames, fmt.Sprintf("cn=%s,%s", c.LdapGroupName, c.LdapDomainDn))
}
@@ -55,7 +55,7 @@ func TestCliVaultCredentialStore(t *testing.T) {
require.NoError(t, err)
// Configure vault
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
boundaryPolicyName, kvPolicyFilePath, _ := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
t.Cleanup(func() {
output := e2e.RunCommand(ctx, "vault",
e2e.WithArgs("policy", "delete", boundaryPolicyName),
@@ -243,7 +243,7 @@ func TestApiVaultCredentialStore(t *testing.T) {
require.NoError(t, err)
// Configure vault
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
boundaryPolicyName, kvPolicyFilePath, _ := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
output := e2e.RunCommand(ctx, "vault",
e2e.WithArgs("secrets", "enable", "-path="+c.VaultSecretPath, "kv-v2"),
)
@@ -16,7 +16,16 @@ type config struct {
VaultAddr string `envconfig:"E2E_VAULT_ADDR_PUBLIC" required:"true"` // e.g. "http://127.0.0.1:8200"
VaultAddrPrivate string `envconfig:"E2E_VAULT_ADDR_PRIVATE" required:"true"` // e.g. "http://10.10.10.10:8200"
VaultSecretPath string `envconfig:"E2E_VAULT_SECRET_PATH" default:"e2e_secrets"`
VaultLdapPath string `envconfig:"E2E_VAULT_LDAP_PATH" default:"e2e_ldap"`
MaxPageSize int `envconfig:"E2E_MAX_PAGE_SIZE" default:"1000"`
// Ldap configuration.
LdapAddress string `envconfig:"E2E_LDAP_ADDR" required:"true"` // e.g. ldap://ldap
LdapDomainDn string `envconfig:"E2E_LDAP_DOMAIN_DN" required:"true"` // e.g. dc=example,dc=org
LdapAdminDn string `envconfig:"E2E_LDAP_ADMIN_DN" required:"true"` // e.g. cn=admin,dc=example,dc=org
LdapAdminPassword string `envconfig:"E2E_LDAP_ADMIN_PASSWORD" required:"true"`
LdapUserName string `envconfig:"E2E_LDAP_USER_NAME" required:"true"`
LdapUserPassword string `envconfig:"E2E_LDAP_USER_PASSWORD" required:"true"`
LdapGroupName string `envconfig:"E2E_LDAP_GROUP_NAME" required:"true"`
}
func loadTestConfig() (*config, error) {
@@ -41,7 +41,7 @@ func TestCliPaginateCredentialLibraries(t *testing.T) {
require.NoError(t, err)
// Configure vault
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
boundaryPolicyName, kvPolicyFilePath, _ := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
t.Cleanup(func() {
output := e2e.RunCommand(ctx, "vault",
e2e.WithArgs("policy", "delete", boundaryPolicyName),
@@ -194,7 +194,7 @@ func TestApiPaginateCredentialLibraries(t *testing.T) {
require.NoError(t, err)
// Configure vault
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
boundaryPolicyName, kvPolicyFilePath, _ := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
t.Cleanup(func() {
output := e2e.RunCommand(ctx, "vault",
e2e.WithArgs("policy", "delete", boundaryPolicyName),
@@ -53,7 +53,7 @@ func TestCliTcpTargetVaultGenericConnectTargetWithAuthzToken(t *testing.T) {
require.NoError(t, err)
// Configure vault
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
boundaryPolicyName, kvPolicyFilePath, _ := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
t.Cleanup(func() {
output := e2e.RunCommand(ctx, "vault",
e2e.WithArgs("policy", "delete", boundaryPolicyName),
@@ -53,7 +53,7 @@ func TestCliTcpTargetVaultGenericConnectTargetWithSsh(t *testing.T) {
require.NoError(t, err)
// Configure vault
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
boundaryPolicyName, kvPolicyFilePath, _ := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
t.Cleanup(func() {
output := e2e.RunCommand(ctx, "vault",
e2e.WithArgs("policy", "delete", boundaryPolicyName),
@@ -54,7 +54,7 @@ func TestCliTcpTargetVaultGenericConnectTarget(t *testing.T) {
require.NoError(t, err)
// Configure vault
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
boundaryPolicyName, kvPolicyFilePath, _ := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
t.Cleanup(func() {
output := e2e.RunCommand(ctx, "vault",
e2e.WithArgs("policy", "delete", boundaryPolicyName),
@@ -49,7 +49,7 @@ func TestCliTcpTargetWorkerConnectTarget(t *testing.T) {
require.NoError(t, err)
// Configure vault
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
boundaryPolicyName, kvPolicyFilePath, _ := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
t.Cleanup(func() {
output := e2e.RunCommand(ctx, "vault",
e2e.WithArgs("policy", "delete", boundaryPolicyName),
@@ -278,7 +278,7 @@ func populateBoundaryDatabase(t testing.TB, ctx context.Context, c *config, te T
require.NoError(t, err)
// Create vault credentials
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
boundaryPolicyName, kvPolicyFilePath, _ := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
output := e2e.RunCommand(ctx, "vault",
e2e.WithArgs("secrets", "enable", "-path="+c.VaultSecretPath, "kv-v2"),
)
+111 -2
View File
@@ -9,6 +9,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/hashicorp/boundary/testing/internal/e2e"
@@ -25,7 +26,7 @@ type CreateTokenResponse struct {
// Setup verifies if appropriate credentials are set and adds the boundary controller
// policy to vault. Returns the vault address.
func Setup(t testing.TB, boundaryControllerFilePath string) (boundaryPolicyName string, kvPolicyFilePath string) {
func Setup(t testing.TB, boundaryControllerFilePath string) (boundaryPolicyName, kvPolicyFilePath, ldapPolicyFilePath string) {
// Set up boundary policy
boundaryPolicyFilePath, err := filepath.Abs(boundaryControllerFilePath)
require.NoError(t, err)
@@ -36,7 +37,115 @@ func Setup(t testing.TB, boundaryControllerFilePath string) (boundaryPolicyName
_, err = os.Create(kvPolicyFilePath)
require.NoError(t, err)
return boundaryPolicyName, kvPolicyFilePath
// Create ldap policy
ldapPolicyFilePath = fmt.Sprintf("%s/%s", t.TempDir(), "ldap-policy.hcl")
_, err = os.Create(ldapPolicyFilePath)
require.NoError(t, err)
return boundaryPolicyName, kvPolicyFilePath, ldapPolicyFilePath
}
// SetupLdap sets a Vault server up for LDAP against an OpenLDAP server. It
// enables the LDAP secrets engine, configures it and creates a static user
// according to what is in OpenLDAP. Additionally, it sets up Vault's ability to
// manage LDAP users dynamically. Note that this function does not put any
// clean-up in place to run after a test is complete. When applicable, callers
// should destroy the Vault LDAP policy and LDAP secrets engine this function
// creates.
func SetupLdap(t testing.TB, vaultLdapMountPath, vaultLdapPolicyFilePath, ldapAddr, ldapAdminDn, ldapAdminPw, ldapDn, ldapUser, ldapGroup string) string {
// Enable LDAP secrets engine.
output := e2e.RunCommand(t.Context(), "vault",
e2e.WithArgs("secrets", "enable", fmt.Sprintf("-path=%s", vaultLdapMountPath), "ldap"),
)
require.NoError(t, output.Err, string(output.Stderr))
// Define and write LDAP access policy to Vault.
f, err := os.OpenFile(vaultLdapPolicyFilePath, os.O_APPEND|os.O_WRONLY, 0o644)
require.NoError(t, err)
_, err = fmt.Fprintf(f, `
path "%[1]s/static-cred/%[2]s" {
capabilities = ["read"]
}
path "%[1]s/static-role/%[2]s" {
capabilities = ["create", "read", "update", "patch", "delete", "list"]
}
path "%[1]s/creds/%[3]s" {
capabilities = ["read"]
}
path "%[1]s/role/%[3]s" {
capabilities = ["create", "read", "update", "patch", "delete", "list"]
}
`, vaultLdapMountPath, ldapUser, ldapGroup)
require.NoError(t, err)
require.NoError(t, f.Sync())
_ = f.Close()
policyName := WritePolicy(t, t.Context(), vaultLdapPolicyFilePath)
// Configure LDAP secrets engine to point to existing OpenLDAP server.
output = e2e.RunCommand(t.Context(), "vault",
e2e.WithArgs(
"write",
fmt.Sprintf("%s/config", vaultLdapMountPath),
fmt.Sprintf("url=%s", ldapAddr),
fmt.Sprintf("binddn=%s", ldapAdminDn),
fmt.Sprintf("bindpass=%s", ldapAdminPw),
),
)
require.NoError(t, output.Err, string(output.Stderr))
// Create static LDAP user in Vault (already defined in OpenLDAP server).
output = e2e.RunCommand(t.Context(), "vault",
e2e.WithArgs(
"write",
fmt.Sprintf("%s/static-role/%s", vaultLdapMountPath, ldapUser),
fmt.Sprintf("dn=cn=%s,%s", ldapUser, ldapDn),
fmt.Sprintf("username=%s", ldapUser),
"rotation_period=24h",
),
)
require.NoError(t, output.Err, string(output.Stderr))
// Create Vault dynamic role for LDAP group.
createLdif := fmt.Sprintf(`
dn: cn={{.Username}},%[1]s
changetype: add
objectClass: inetOrgPerson
cn: {{.Username}}
sn: {{.Username}}
uid: {{.Username}}
userPassword:{{.Password}}
dn: cn=%s,%[1]s
changetype: modify
add: uniqueMember
uniqueMember: cn={{.Username}},%[1]s
-
`, ldapDn, ldapGroup)
createLdif = strings.ReplaceAll(createLdif, "\t", "")
deleteLdif := fmt.Sprintf(`
dn: cn={{.Username}},%s
changetype: delete
`, ldapDn)
deleteLdif = strings.ReplaceAll(deleteLdif, "\t", "")
output = e2e.RunCommand(t.Context(), "vault",
e2e.WithArgs(
"write",
fmt.Sprintf("%s/role/%s", vaultLdapMountPath, ldapGroup),
"username_template=b_{{.DisplayName}}_{{.RoleName}}_{{random 10}}_{{unix_time}}",
fmt.Sprintf("creation_ldif=%s", createLdif),
fmt.Sprintf("rollback_ldif=%s", deleteLdif),
fmt.Sprintf("deletion_ldif=%s", deleteLdif),
"default_ttl=1h",
"max_ttxl=24h",
),
)
require.NoError(t, output.Err, string(output.Stderr))
return policyName
}
// CreateKvPrivateKeyCredential creates a private key credential in vault and creates a vault policy