From 69b886266a94dafde84c89f3da348d738dd0dca5 Mon Sep 17 00:00:00 2001 From: Sanchit Khanna Date: Thu, 19 Sep 2024 11:52:45 +0530 Subject: [PATCH] roachtest: add ldap conn. latency test via roachtest No test previously existed to compute and monitor LDAP connection latency Created a roachtest which leverages the workload to get the stats for LDAP connection latency The test provisions an openLDAP service and it's user `jdoe` which is authenticated on the CRDB via LDAP. The test * provisions openLDAP with TLS connection * Creates a user named jdoe into CRDB * Sets the HBA conf and custom CA into the cluster settings * runs the workload binary to compute the connection latency Epic: CRDB-40412 Fixes: #127358 Release note: None --- .../testdata/ldap_authentication_hba_conf | 3 + .../testdata/ldap_base_structure.ldif | 11 + .../roachtest/testdata/ldap_user_group.ldif | 63 +++ pkg/cmd/roachtest/tests/connection_latency.go | 404 ++++++++++++++++++ pkg/cmd/roachtest/tests/registry.go | 1 + 5 files changed, 482 insertions(+) create mode 100644 pkg/cmd/roachtest/testdata/ldap_authentication_hba_conf create mode 100644 pkg/cmd/roachtest/testdata/ldap_base_structure.ldif create mode 100644 pkg/cmd/roachtest/testdata/ldap_user_group.ldif diff --git a/pkg/cmd/roachtest/testdata/ldap_authentication_hba_conf b/pkg/cmd/roachtest/testdata/ldap_authentication_hba_conf new file mode 100644 index 000000000000..77755b627839 --- /dev/null +++ b/pkg/cmd/roachtest/testdata/ldap_authentication_hba_conf @@ -0,0 +1,3 @@ +host all roachprod 0.0.0.0/0 password +host all all all ldap ldapserver=%s ldapport=636 "ldapbasedn=OU=Users,DC=example,DC=com" "ldapbinddn=CN=John Doe,OU=Users,DC=example,DC=com" ldapbindpasswd=%s ldapsearchattribute=uid "ldapsearchfilter=(mail=*)" +host all root 0.0.0.0/0 password diff --git a/pkg/cmd/roachtest/testdata/ldap_base_structure.ldif b/pkg/cmd/roachtest/testdata/ldap_base_structure.ldif new file mode 100644 index 000000000000..a6c913d9273d --- /dev/null +++ b/pkg/cmd/roachtest/testdata/ldap_base_structure.ldif @@ -0,0 +1,11 @@ +# Organizational Unit: Users +dn: ou=Users,dc=example,dc=com +objectClass: top +objectClass: organizationalUnit +ou: Users + +# Organizational Unit: Groups +dn: ou=Groups,dc=example,dc=com +objectClass: top +objectClass: organizationalUnit +ou: Groups diff --git a/pkg/cmd/roachtest/testdata/ldap_user_group.ldif b/pkg/cmd/roachtest/testdata/ldap_user_group.ldif new file mode 100644 index 000000000000..cfde4f814393 --- /dev/null +++ b/pkg/cmd/roachtest/testdata/ldap_user_group.ldif @@ -0,0 +1,63 @@ +# User 1: John Doe +dn: cn=John Doe,ou=Users,dc=example,dc=com +objectClass: top +objectClass: inetOrgPerson +objectClass: posixAccount +objectClass: shadowAccount +uid: jdoe +cn: John Doe +sn: Doe +givenName: John +displayName: John Doe +mail: jdoe@example.com +uidNumber: 1001 +gidNumber: 5000 +homeDirectory: /home/jdoe +loginShell: /bin/bash +userPassword: {SSHA}UweAl2O1Zh95nijbT+SaQB5FuaHi7xnE + +# User 2: Alice Smith +dn: cn=Alice Smith,ou=Users,dc=example,dc=com +objectClass: top +objectClass: inetOrgPerson +objectClass: posixAccount +objectClass: shadowAccount +uid: asmith +cn: Alice Smith +sn: Smith +givenName: Alice +displayName: Alice Smith +mail: asmith@example.com +uidNumber: 1002 +gidNumber: 5000 +homeDirectory: /home/asmith +loginShell: /bin/bash +userPassword: {SSHA}PK9Mq7jpwPR4hslWym9zFpGDyz92iiSs + +# User 3: Robert Brown +dn: cn=Robert Brown,ou=Users,dc=example,dc=com +objectClass: top +objectClass: inetOrgPerson +objectClass: posixAccount +objectClass: shadowAccount +uid: rbrown +cn: Robert Brown +sn: Brown +givenName: Robert +displayName: Robert Brown +mail: rbrown@example.com +uidNumber: 1003 +gidNumber: 5000 +homeDirectory: /home/rbrown +loginShell: /bin/bash +userPassword: {SSHA}WA/veP8/qFKW74DrCjTw+6DEGxm6Pqb9 + +# Group: Developers +dn: cn=Developers,ou=Groups,dc=example,dc=com +objectClass: top +objectClass: groupOfUniqueNames +cn: Developers +description: Group for software development team members +uniqueMember: cn=John Doe,ou=Users,dc=example,dc=com +uniqueMember: cn=Alice Smith,ou=Users,dc=example,dc=com +uniqueMember: cn=Robert Brown,ou=Users,dc=example,dc=com diff --git a/pkg/cmd/roachtest/tests/connection_latency.go b/pkg/cmd/roachtest/tests/connection_latency.go index 3fe08db279cc..5351ae673746 100644 --- a/pkg/cmd/roachtest/tests/connection_latency.go +++ b/pkg/cmd/roachtest/tests/connection_latency.go @@ -16,7 +16,9 @@ import ( "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/roachtestutil" "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/spec" "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/test" + "github.com/cockroachdb/cockroach/pkg/roachprod" "github.com/cockroachdb/cockroach/pkg/roachprod/install" + "github.com/jackc/pgx/v4" "github.com/stretchr/testify/require" ) @@ -25,6 +27,10 @@ const ( regionUsCentral = "us-central1-b" regionUsWest = "us-west1-b" regionEuWest = "europe-west2-b" + + ldapTestUserName = "jdoe" + ldapTestUserPassword = "password" + ldapAdminPassword = "password" ) func runConnectionLatencyTest( @@ -145,3 +151,401 @@ func registerConnectionLatencyTest(r registry.Registry) { }, }) } + +func registerLDAPConnectionLatencyTest(r registry.Registry) { + + // Single region, 1 node test for LDAP connection latency + numNodes := 1 + r.Add(registry.TestSpec{ + Name: "ldap_connection_latency", + Owner: registry.OwnerProductSecurity, + Benchmark: true, + Cluster: r.MakeClusterSpec(numNodes, spec.GCEZones(regionUsCentral)), + RequiresDeprecatedWorkload: true, + // Cannot be run locally as it is dependent on Linux. + CompatibleClouds: registry.OnlyGCE, + Suites: registry.Suites(registry.Nightly), + Run: func(ctx context.Context, t test.Test, c cluster.Cluster) { + runLDAPConnectionLatencyTest(ctx, t, c, numNodes) + }, + }) +} + +// runLDAPConnectionLatencyTest creates a local openLDAP server +// and measures the connection latency. +// NOTE: The tests accesses the testdata. Since this is an implicit dependency +// of the test, the test will fail if the roachtest binary is executed +// outside the project directory. +func runLDAPConnectionLatencyTest( + ctx context.Context, t test.Test, c cluster.Cluster, numNodes int, +) { + + // put the workload binary onto each node + err := c.PutE(ctx, t.L(), t.DeprecatedWorkload(), "./workload") + require.NoError(t, err) + + // Initialize default cluster settings for the cluster + settings := install.MakeClusterSettings() + + // Don't start a backup schedule as this roachtest reports roachperf results. + err = c.StartE(ctx, t.L(), + option.NewStartOpts(option.NoBackupSchedule), settings) + require.NoError(t, err) + + // Prepare the user which will later be used for ldap bind. + prepareSQLUserForLDAP(ctx, t, c, ldapTestUserName) + + nodeArr := make([]int, numNodes) + for i := 1; i <= numNodes; i++ { + nodeArr[i-1] = i + } + nodeListOptions := c.Nodes(nodeArr...) + + // Get the hostname, to be used for openLDAP configuration setup. + hostName := getLDAPHostName(ctx, t, c) + + // These functions are responsible for setting up openLDAP on the cluster. + installOpenLDAPOnHost(ctx, t, c, nodeListOptions) + createCertificates(ctx, t, c, nodeListOptions, hostName) + updateOpenLDAPConfig(ctx, t, c, nodeListOptions) + + // Import the user and groups to make LDAP directory structure, + // this creates 3 users and adds them as members of the group. + importUserAndGroupsToLDAPServer(ctx, t, c, nodeListOptions) + + // Setup CRDB specific configs for LDAP authentication. + // Update the relevant cluster settings for configuring and debugging. + setupCRDBForLDAPAuth(ctx, t, c, hostName) + + urlTemplate := func(host string) string { + return fmt.Sprintf("postgresql://%s:%s@localhost:{pgport:1}", + ldapTestUserName, ldapTestUserPassword) + } + + // Create the SQL authenticating URL and test the connection. + testAuthCmd := fmt.Sprintf( + "./cockroach sql --url \"%s\" --certs-dir=certs", urlTemplate("localhost")) + err = c.RunE(ctx, option.WithNodes(nodeListOptions), testAuthCmd) + require.NoError(t, err) + + runWorkload := func(roachNodes, loadNode option.NodeListOption, locality string) { + var urlString string + var urls []string + externalIps, err := c.ExternalIP(ctx, t.L(), roachNodes) + require.NoError(t, err) + + for _, u := range externalIps { + url := urlTemplate(u) + urls = append(urls, fmt.Sprintf("'%s'", url)) + } + urlString = strings.Join(urls, " ") + + t.L().Printf("running workload in %q against urls:\n%s", locality, strings.Join(urls, "\n")) + + workloadCmd := fmt.Sprintf( + `./workload run connectionlatency %s --secure --duration 30s --histograms=%s/stats.json --locality %s`, + urlString, + t.PerfArtifactsDir(), + locality, + ) + err = c.RunE(ctx, option.WithNodes(loadNode), workloadCmd) + require.NoError(t, err) + } + + //Run only on the load node. + runWorkload(c.Range(1, numNodes), c.Node(1), regionUsCentral) +} + +// getLDAPHostName retrieves the hostname of the cluster node +// the result is returned by trimming the trailing '\n'. +func getLDAPHostName(ctx context.Context, t test.Test, c cluster.Cluster) string { + cmd := "hostname -f" + out, err := c.RunWithDetailsSingleNode(ctx, t.L(), + option.WithNodes(c.Node(1)), cmd) + require.NoError(t, err) + + return strings.ReplaceAll(out.Stdout, "\n", "") +} + +// Install the packages to set up openLDAP on ubuntu. +func installOpenLDAPOnHost( + ctx context.Context, t test.Test, c cluster.Cluster, nodeListOptions option.NodeListOption, +) { + // debconf-set-selections must be updated to preset the + // prompts for installing `slapd` silently. + configCmd := `cat <