test(e2e): Implement Vault LDAP API and CLI tests
This commit is contained in:
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
+1
-1
@@ -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),
|
||||
|
||||
+1
-1
@@ -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"),
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user