Add integration test for LDAP StartTLS

This commit is contained in:
Ryan Richard 2021-05-20 13:39:48 -07:00
parent 7e76b66639
commit 8b549f66d4
5 changed files with 16 additions and 6 deletions

View File

@ -333,6 +333,7 @@ export PINNIPED_TEST_SUPERVISOR_HTTP_ADDRESS="127.0.0.1:12345"
export PINNIPED_TEST_SUPERVISOR_HTTPS_ADDRESS="localhost:12344" export PINNIPED_TEST_SUPERVISOR_HTTPS_ADDRESS="localhost:12344"
export PINNIPED_TEST_PROXY=http://127.0.0.1:12346 export PINNIPED_TEST_PROXY=http://127.0.0.1:12346
export PINNIPED_TEST_LDAP_HOST=ldap.tools.svc.cluster.local export PINNIPED_TEST_LDAP_HOST=ldap.tools.svc.cluster.local
export PINNIPED_TEST_LDAP_STARTTLS_ONLY_HOST=ldapstarttls.tools.svc.cluster.local
export PINNIPED_TEST_LDAP_LDAPS_CA_BUNDLE="${test_ca_bundle_pem}" export PINNIPED_TEST_LDAP_LDAPS_CA_BUNDLE="${test_ca_bundle_pem}"
export PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME="cn=admin,dc=pinniped,dc=dev" export PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME="cn=admin,dc=pinniped,dc=dev"
export PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD=password export PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD=password

View File

@ -26,6 +26,7 @@ import (
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers" "go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
"go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/oidc/provider" "go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/upstreamldap" "go.pinniped.dev/internal/upstreamldap"
) )
@ -266,15 +267,18 @@ func (c *ldapWatcherController) testConnection(
tlsLDAPProvider := upstreamldap.New(*config) tlsLDAPProvider := upstreamldap.New(*config)
err := tlsLDAPProvider.TestConnection(ctx) err := tlsLDAPProvider.TestConnection(ctx)
if err != nil { if err != nil {
plog.InfoErr("testing LDAP connection using TLS failed, so trying again with StartTLS", err, "host", config.Host)
// If there was any error, try again with StartTLS instead. // If there was any error, try again with StartTLS instead.
config.ConnectionProtocol = upstreamldap.StartTLS config.ConnectionProtocol = upstreamldap.StartTLS
startTLSLDAPProvider := upstreamldap.New(*config) startTLSLDAPProvider := upstreamldap.New(*config)
startTLSErr := startTLSLDAPProvider.TestConnection(ctx) startTLSErr := startTLSLDAPProvider.TestConnection(ctx)
if startTLSErr == nil { if startTLSErr == nil {
plog.Info("testing LDAP connection using StartTLS succeeded", "host", config.Host)
// Successfully able to fall back to using StartTLS, so clear the original // Successfully able to fall back to using StartTLS, so clear the original
// error and consider the connection test to be successful. // error and consider the connection test to be successful.
err = nil err = nil
} else { } else {
plog.InfoErr("testing LDAP connection using StartTLS also failed", err, "host", config.Host)
// Falling back to StartTLS also failed, so put TLS back into the config // Falling back to StartTLS also failed, so put TLS back into the config
// and consider the connection test to be failed. // and consider the connection test to be failed.
config.ConnectionProtocol = upstreamldap.TLS config.ConnectionProtocol = upstreamldap.TLS

View File

@ -69,7 +69,7 @@ func TestSupervisorLogin(t *testing.T) {
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups, wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
}, },
{ {
name: "ldap with email as username and groups names as DNs", name: "ldap with email as username and groups names as DNs and using an LDAP provider which supports TLS",
createIDP: func(t *testing.T) { createIDP: func(t *testing.T) {
t.Helper() t.Helper()
secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth, secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
@ -126,7 +126,7 @@ func TestSupervisorLogin(t *testing.T) {
wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs, wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs,
}, },
{ {
name: "ldap with CN as username and group names as CNs", // try another variation of configuration options name: "ldap with CN as username and group names as CNs and using an LDAP provider which only supports StartTLS", // try another variation of configuration options
createIDP: func(t *testing.T) { createIDP: func(t *testing.T) {
t.Helper() t.Helper()
secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth, secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
@ -136,7 +136,7 @@ func TestSupervisorLogin(t *testing.T) {
}, },
) )
ldapIDP := library.CreateTestLDAPIdentityProvider(t, idpv1alpha1.LDAPIdentityProviderSpec{ ldapIDP := library.CreateTestLDAPIdentityProvider(t, idpv1alpha1.LDAPIdentityProviderSpec{
Host: env.SupervisorUpstreamLDAP.Host, Host: env.SupervisorUpstreamLDAP.StartTLSOnlyHost,
TLS: &idpv1alpha1.TLSSpec{ TLS: &idpv1alpha1.TLSSpec{
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.CABundle)), CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.CABundle)),
}, },
@ -161,7 +161,7 @@ func TestSupervisorLogin(t *testing.T) {
}, idpv1alpha1.LDAPPhaseReady) }, idpv1alpha1.LDAPPhaseReady)
expectedMsg := fmt.Sprintf( expectedMsg := fmt.Sprintf(
`successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`, `successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`,
env.SupervisorUpstreamLDAP.Host, env.SupervisorUpstreamLDAP.BindUsername, env.SupervisorUpstreamLDAP.StartTLSOnlyHost, env.SupervisorUpstreamLDAP.BindUsername,
secret.Name, secret.ResourceVersion, secret.Name, secret.ResourceVersion,
) )
requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg) requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg)
@ -176,7 +176,7 @@ func TestSupervisorLogin(t *testing.T) {
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute // the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta( wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(
"ldaps://" + env.SupervisorUpstreamLDAP.Host + "?sub=" + env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue, "ldaps://" + env.SupervisorUpstreamLDAP.StartTLSOnlyHost + "?sub=" + env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue,
), ),
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserDN), wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserDN),

View File

@ -437,7 +437,10 @@ func CreateTestLDAPIdentityProvider(t *testing.T, spec idpv1alpha1.LDAPIdentityP
return false return false
} }
return result.Status.Phase == expectedPhase return result.Status.Phase == expectedPhase
}, 60*time.Second, 1*time.Second, "expected the LDAPIdentityProvider to go into phase %s, LDAPIdentityProvider was: %s", expectedPhase, Sdump(result)) },
2*time.Minute, // it takes 1 minute for a failed LDAP TLS connection test to timeout before it tries using StartTLS, so wait longer than that
1*time.Second,
"expected the LDAPIdentityProvider to go into phase %s, LDAPIdentityProvider was: %s", expectedPhase, Sdump(result))
return result return result
} }

View File

@ -73,6 +73,7 @@ type TestOIDCUpstream struct {
type TestLDAPUpstream struct { type TestLDAPUpstream struct {
Host string `json:"host"` Host string `json:"host"`
StartTLSOnlyHost string `json:"startTLSOnlyHost"`
CABundle string `json:"caBundle"` CABundle string `json:"caBundle"`
BindUsername string `json:"bindUsername"` BindUsername string `json:"bindUsername"`
BindPassword string `json:"bindPassword"` BindPassword string `json:"bindPassword"`
@ -240,6 +241,7 @@ func loadEnvVars(t *testing.T, result *TestEnv) {
result.SupervisorUpstreamLDAP = TestLDAPUpstream{ result.SupervisorUpstreamLDAP = TestLDAPUpstream{
Host: needEnv(t, "PINNIPED_TEST_LDAP_HOST"), Host: needEnv(t, "PINNIPED_TEST_LDAP_HOST"),
StartTLSOnlyHost: needEnv(t, "PINNIPED_TEST_LDAP_STARTTLS_ONLY_HOST"),
CABundle: base64Decoded(t, os.Getenv("PINNIPED_TEST_LDAP_LDAPS_CA_BUNDLE")), CABundle: base64Decoded(t, os.Getenv("PINNIPED_TEST_LDAP_LDAPS_CA_BUNDLE")),
BindUsername: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME"), BindUsername: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME"),
BindPassword: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD"), BindPassword: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD"),