|
| 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. |
0 commit comments