Skip to content

Commit 063c21d

Browse files
authored
test(ci): add e2e ldap authentication tests (#23642)
1 parent 936aef2 commit 063c21d

File tree

15 files changed

+877
-15
lines changed

15 files changed

+877
-15
lines changed

ci/docker-compose.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,15 @@ services:
223223
- ..:/risingwave
224224
stop_grace_period: 30s
225225

226+
ldap-test-env:
227+
image: public.ecr.aws/w1p7b4n3/rw-build-env:${BUILD_ENV_VERSION:?}
228+
depends_on:
229+
ldap-server:
230+
condition: service_healthy
231+
volumes:
232+
- ..:/risingwave
233+
stop_grace_period: 30s
234+
226235
rw-build-env:
227236
image: public.ecr.aws/w1p7b4n3/rw-build-env:${BUILD_ENV_VERSION:?}
228237
volumes:
@@ -486,3 +495,56 @@ services:
486495
interval: 5s
487496
timeout: 5s
488497
retries: 5
498+
499+
ldap-server:
500+
image: osixia/openldap:1.5.0
501+
hostname: ldap-server
502+
environment:
503+
LDAP_DISABLE_CHOWN: "true"
504+
LDAP_ORGANISATION: "Example Corp"
505+
LDAP_DOMAIN: "example.com"
506+
LDAP_BASE_DN: "dc=example,dc=com"
507+
LDAP_ADMIN_PASSWORD: "admin123"
508+
LDAP_CONFIG_PASSWORD: "config123"
509+
LDAP_READONLY_USER: "false"
510+
LDAP_RFC2307BIS_SCHEMA: "false"
511+
LDAP_BACKEND: "mdb"
512+
LDAP_REMOVE_CONFIG_AFTER_SETUP: "false"
513+
LDAP_TLS: "true"
514+
LDAP_TLS_CRT_FILENAME: "server.crt"
515+
LDAP_TLS_KEY_FILENAME: "server.key"
516+
LDAP_TLS_CA_CRT_FILENAME: "ca.crt"
517+
LDAP_TLS_DH_PARAM_FILENAME: "dhparam.pem"
518+
LDAP_TLS_ENFORCE: "false"
519+
LDAP_TLS_VERIFY_CLIENT: "try"
520+
LDAP_TLS_PROTOCOL_MIN: "3.1"
521+
LDAP_TLS_CIPHER_SUITE: "SECURE256:+SECURE128:-VERS-TLS-ALL:+VERS-TLS1.2:-RSA:-DHE-DSS:-CAMELLIA-128-CBC:-CAMELLIA-256-CBC"
522+
ports:
523+
- "389:389"
524+
- "636:636"
525+
volumes:
526+
- ./ldap-test/ldif:/workspace/ldif
527+
- ./ldap-test/certs:/container/service/slapd/assets/certs
528+
- ./ldap-test/setup-ldap-certs.sh:/workspace/setup-ldap-certs.sh
529+
entrypoint:
530+
- /bin/bash
531+
- -c
532+
- |
533+
set -euo pipefail
534+
535+
echo "=== Generating certificates ==="
536+
export CERT_DIR=/container/service/slapd/assets/certs
537+
bash /workspace/setup-ldap-certs.sh
538+
539+
echo "=== Importing LDIF files ==="
540+
mkdir -p /container/service/slapd/assets/config/bootstrap/ldif/custom
541+
cp -r /workspace/ldif/* /container/service/slapd/assets/config/bootstrap/ldif/custom/
542+
543+
echo "=== Certificates ready, starting OpenLDAP ==="
544+
exec /container/tool/run --loglevel debug
545+
healthcheck:
546+
test: ["CMD-SHELL", "ldapsearch -x -H ldap://localhost:389 -b 'dc=example,dc=com' -D 'cn=admin,dc=example,dc=com' -w 'admin123' >/dev/null 2>&1"]
547+
interval: 5s
548+
timeout: 10s
549+
retries: 10
550+
start_period: 10s

ci/ldap-test/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Ignore generated TLS certificates
2+
certs/
3+
4+
# Ignore docker volumes
5+
ldap_data/
6+
ldap_config/

ci/ldap-test/README.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# LDAP Authentication E2E Tests
2+
3+
This directory contains end-to-end tests for LDAP authentication in RisingWave.
4+
5+
**Note: These tests are designed to run in CI only. The LDAP server is managed by `ci/docker-compose.yml`.**
6+
7+
## Overview
8+
9+
These tests verify that RisingWave can authenticate users against an LDAP server using two authentication modes:
10+
11+
1. **Simple Bind**: Uses `ldapprefix` and `ldapsuffix` to construct the DN directly
12+
2. **Search and Bind**: Uses `ldapbasedn`, `ldapsearchattribute`, `ldapbinddn`, and `ldapbindpasswd` to search for the user first, then bind
13+
14+
Each authentication mode is tested with and without client certificates (mutual TLS), and with both legacy parameter format and URL format.
15+
16+
## Directory Structure
17+
18+
```
19+
ci/
20+
├── docker-compose.yml # Contains ldap-server service
21+
└── ldap-test/
22+
├── setup-ldap-certs.sh # Script to generate TLS certificates
23+
├── ldif/
24+
│ └── 01-users.ldif # LDAP test users definition
25+
├── certs/ # Generated TLS certificates (gitignored, auto-generated)
26+
└── README.md # This file
27+
28+
src/config/
29+
├── ci-ldap-simple-bind.toml # RisingWave config for simple bind
30+
├── ci-ldap-search-bind.toml # RisingWave config for search+bind
31+
├── ci-ldap-simple-bind-url.toml # RisingWave config for simple bind (URL format)
32+
└── ci-ldap-search-bind-url.toml # RisingWave config for search+bind (URL format)
33+
34+
e2e_test/ldap/
35+
└── ldap_auth.slt # LDAP authentication tests (reused for all scenarios)
36+
37+
ci/scripts/
38+
└── e2e-ldap-test.sh # Test script
39+
```
40+
41+
## Test Users
42+
43+
One test user is created in LDAP:
44+
45+
- `testuser1` with password `testpass1`
46+
47+
The user is under the organizational unit: `ou=people,dc=example,dc=com`
48+
49+
## How It Works
50+
51+
### Certificate Generation
52+
53+
Certificates are generated **inside the ldap-server container** during startup:
54+
55+
1. The `ldap-server` entrypoint runs `setup-ldap-certs.sh` with `CERT_DIR=/container/service/slapd/assets/certs`
56+
2. Certificates (CA, server, client) are generated directly at the location where OpenLDAP expects them
57+
3. The same directory is bind-mounted to the host at `./ldap-test/certs/` so test scripts can access them
58+
4. OpenLDAP then starts using the generated certificates
59+
60+
### Test Execution
61+
62+
The test script `ci/scripts/e2e-ldap-test.sh` performs minimal setup:
63+
64+
1. Installs `ldap-utils` for user management
65+
2. Waits for STARTTLS to be available (verifies TLS is ready)
66+
3. Verifies test user exists in LDAP
67+
4. Sets the test user's password
68+
5. Exports environment variables for RisingWave:
69+
- `LDAPTLS_CACERT`: Path to CA certificate
70+
- `LDAPTLS_REQCERT`: Set to "demand" for strict verification
71+
- `LDAPTLS_CERT` / `LDAPTLS_KEY`: Client certificate (for mutual TLS tests)
72+
6. Runs 6 test scenarios
73+
74+
### Test Scenarios
75+
76+
1. Simple Bind without client certificate
77+
2. Simple Bind with client certificate
78+
3. Search and Bind without client certificate
79+
4. Search and Bind with client certificate
80+
5. Simple Bind URL format without client certificate
81+
6. Search and Bind URL format without client certificate
82+
83+
Each scenario:
84+
- Starts RisingWave with the appropriate config file
85+
- Runs the same SQLLogicTest suite (`e2e_test/ldap/ldap_auth.slt`)
86+
- Tests failure scenarios (wrong password, non-existent user, LDAP injection)
87+
- Stops RisingWave
88+
89+
## RisingWave LDAP TLS Configuration
90+
91+
RisingWave reads LDAP TLS configuration from environment variables:
92+
93+
- **`LDAPTLS_CACERT`**: Path to CA certificate file (required for self-signed certificates)
94+
- **`LDAPTLS_CERT`**: Path to client certificate file (optional, for mutual TLS)
95+
- **`LDAPTLS_KEY`**: Path to client private key file (optional, for mutual TLS)
96+
- **`LDAPTLS_REQCERT`**: Certificate verification policy (`never`, `allow`, `try`, `demand`)
97+
98+
These environment variables must be set **before** starting RisingWave:
99+
100+
```bash
101+
export LDAPTLS_CACERT="$(pwd)/ci/ldap-test/certs/ca.crt"
102+
export LDAPTLS_REQCERT="demand"
103+
risedev ci-start ci-ldap-simple-bind
104+
```
105+
106+
If `LDAPTLS_CACERT` is not set, RisingWave will use the system's native certificate store, which won't include our self-signed CA certificate, causing authentication to fail with `invalid peer certificate` errors.
107+
108+
## Local Development
109+
110+
### Prerequisites
111+
112+
Add hostname to `/etc/hosts`:
113+
114+
```bash
115+
sudo bash -c 'echo "127.0.0.1 ldap-server" >> /etc/hosts'
116+
```
117+
118+
This allows using the `ldap-server` hostname (matching the certificate CN) both locally and in CI.
119+
120+
### Starting the LDAP Server
121+
122+
```bash
123+
# From the repository root
124+
docker-compose -f ci/docker-compose.yml up -d ldap-server
125+
```
126+
127+
This will:
128+
1. Generate TLS certificates inside the container
129+
2. Start OpenLDAP with STARTTLS support
130+
3. Load test users from `ldif/01-users.ldif`
131+
132+
### Setting Test User Password
133+
134+
```bash
135+
ldappasswd -x -H ldap://ldap-server:389 \
136+
-D "cn=admin,dc=example,dc=com" -w "admin123" \
137+
-s "testpass1" "uid=testuser1,ou=people,dc=example,dc=com"
138+
```
139+
140+
### Testing LDAP Connection
141+
142+
```bash
143+
export LDAPTLS_CACERT="$(pwd)/ci/ldap-test/certs/ca.crt"
144+
export LDAPTLS_REQCERT="demand"
145+
146+
# Test with ldapwhoami (may need Homebrew openldap on macOS)
147+
ldapwhoami -x -H ldap://ldap-server:389 -ZZ \
148+
-D "cn=admin,dc=example,dc=com" -w "admin123"
149+
```
150+
151+
**macOS Note**: Install OpenLDAP via Homebrew for proper TLS support:
152+
```bash
153+
brew install openldap
154+
# Use: /opt/homebrew/opt/openldap/bin/ldapwhoami
155+
```
156+
157+
The macOS system LDAP client may not work properly with STARTTLS.
158+
159+
### Cleanup
160+
161+
```bash
162+
docker-compose -f ci/docker-compose.yml down
163+
rm -rf ci/ldap-test/certs/
164+
```
165+
166+
## Implementation Details
167+
168+
The `setup-ldap-certs.sh` script generates:
169+
170+
- **CA Certificate** (`ca.crt`, `ca.key`): Self-signed root CA
171+
- **Server Certificate** (`server.crt`, `server.key`):
172+
- CN: `ldap-server`
173+
- SAN: `DNS:ldap-server`, `DNS:localhost`, `DNS:ldap.example.com`, `IP:127.0.0.1`, `IP:::1`
174+
- Key Usage: digitalSignature, keyEncipherment
175+
- Extended Key Usage: serverAuth
176+
- **Client Certificate** (`client.crt`, `client.key`):
177+
- CN: `RisingWave Client`
178+
- Key Usage: digitalSignature, keyEncipherment
179+
- Extended Key Usage: clientAuth
180+
- **DH Parameters** (`dhparam.pem`): 2048-bit, reused if already exists
181+
182+
All certificates use RSA 4096-bit keys and SHA256 signatures.

ci/ldap-test/ldif/01-users.ldif

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Create organizational unit for people
2+
dn: ou=people,dc=example,dc=com
3+
objectClass: organizationalUnit
4+
ou: people
5+
6+
# Test user for LDAP authentication
7+
dn: uid=testuser1,ou=people,dc=example,dc=com
8+
objectClass: inetOrgPerson
9+
objectClass: posixAccount
10+
objectClass: shadowAccount
11+
uid: testuser1
12+
cn: Test User 1
13+
sn: User1
14+
givenName: Test
15+
16+
userPassword: {SSHA}testpass1
17+
uidNumber: 10001
18+
gidNumber: 10001
19+
homeDirectory: /home/testuser1
20+
loginShell: /bin/bash

0 commit comments

Comments
 (0)