Files
stalwart/tests/docker/INSTRUCTIONS.md
2026-04-02 19:32:51 +02:00

221 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Stalwart Test Infrastructure
Ephemeral Docker Compose stack for testing Stalwart against external services.
All data is lost on `docker compose down` every restart is a clean slate.
## Quick Start
```bash
cd stalwart-test
docker compose up -d
```
Wait ~30 seconds for all services to initialize (Keycloak takes the longest).
## Connection Reference
| Service | Host | Port(s) | Credentials / Notes |
|----------------|-------------------|-----------------|--------------------------------------------|
| PostgreSQL | localhost | 5432 | `stalwart` / `stalwart`, db: `stalwart` |
| MySQL | localhost | 3306 | `stalwart` / `stalwart`, db: `stalwart` |
| FoundationDB | localhost | 4500 | Cluster file from container |
| Redis | localhost | 6379 | No auth |
| OpenSearch | localhost | 9200 | No auth, security plugin disabled |
| Meilisearch | localhost | 7700 | Master key: `stalwart-master-key` |
| MinIO (S3) | localhost | 9000 / 9001 | `minioadmin` / `minioadmin`, bucket: `stalwart` |
| Keycloak (OIDC)| localhost | 9080 | Admin: `admin` / `admin` |
| OpenLDAP | localhost | 389 / 636 (TLS) | Admin DN: `cn=admin,dc=stalwart,dc=test`, pw: `admin` |
| Pebble (ACME) | localhost | 14000 / 15000 | Self-signed TLS, uses challtestsrv |
| Challtestsrv | localhost | 8055 | ACME challenge test server (management API)|
| PowerDNS | localhost | 5300 / 8081 | API key: `stalwart-api-key` |
| NATS | localhost | 4222 / 8222 | No auth |
## OIDC (Keycloak) Details
- **OIDC Discovery**: `http://localhost:9080/realms/stalwart/.well-known/openid-configuration`
- **Token Endpoint**: `http://localhost:9080/realms/stalwart/protocol/openid-connect/token`
- **Client ID**: `stalwart`
- **Client Secret**: `stalwart-secret`
- **Realm**: `stalwart`
### Test Users
| Username | Password | Groups |
|---------------------------|--------------------------|----------------------------------------|
| john.doe@example.org | this is an OIDC password | sales@example.org |
| jane.smith@example.org | this is an OIDC password | sales@example.org, corporate@example.org |
| bill.foobar@example.org | this is an OIDC password | corporate@example.org |
### Example: Get a Token
```bash
curl -X POST http://localhost:9080/realms/stalwart/protocol/openid-connect/token \
-d "grant_type=password" \
-d "client_id=stalwart" \
-d "client_secret=stalwart-secret" \
-d "username=john.doe@example.org" \
-d "password=this is an OIDC password"
```
## LDAP Details
- **Base DN**: `dc=stalwart,dc=test`
- **Admin DN**: `cn=admin,dc=stalwart,dc=test`
- **Admin Password**: `admin`
- **Read-only DN**: `cn=readonly,dc=stalwart,dc=test`
- **Read-only Password**: `readonly`
- **User DN pattern**: `uid={username},ou=users,dc=stalwart,dc=test`
### Test Users
| DN | Mail | Password |
|------------------------------------------------|--------------------------|--------------------------|
| uid=john.doe,ou=users,dc=stalwart,dc=test | john.doe@example.org | this is an LDAP password |
| uid=jane.smith,ou=users,dc=stalwart,dc=test | jane.smith@example.org | this is an LDAP password |
| uid=bill.foobar,ou=users,dc=stalwart,dc=test | bill.foobar@example.org | this is an LDAP password |
### Groups
| DN | Mail | Members |
|---------------------------------------------|-------------------------|------------------|
| cn=sales,ou=groups,dc=stalwart,dc=test | sales@example.org | john.doe, jane.smith |
| cn=corporate,ou=groups,dc=stalwart,dc=test | corporate@example.org | bill.foobar, jane.smith |
### Example: Search by Email
```bash
ldapsearch -x -H ldap://localhost:389 \
-D "cn=admin,dc=stalwart,dc=test" -w admin \
-b "dc=stalwart,dc=test" "(mail=john.doe@example.org)"
```
## S3 (MinIO) Details
- **Endpoint**: `http://localhost:9000`
- **Access Key**: `minioadmin`
- **Secret Key**: `minioadmin`
- **Bucket**: `stalwart`
- **Console**: `http://localhost:9001`
- **Region**: `us-east-1` (MinIO default)
## DNS (PowerDNS) Details
- **DNS port**: 5300 (TCP+UDP)
- **API**: `http://localhost:8081` (API key: `stalwart-api-key`)
- **Zone**: `stalwart.test`
- **TSIG key name**: `stalwart-update-key`
- **TSIG algorithm**: `hmac-sha256`
- **TSIG secret (base64)**: `c3RhbHdhcnQtdGVzdC10c2lnLXNlY3JldC1rZXkxMjM0NTY3ODkw`
> **Note on SIG(0):** PowerDNS does not support SIG(0) authentication for RFC2136
> updates. Only BIND has (limited) SIG(0) support. If you need to test SIG(0),
> a separate BIND instance would be required.
### Example: Query TLSA Record
```bash
dig @localhost -p 5300 _25._tcp.mail.stalwart.test TLSA
```
### Example: RFC2136 Dynamic Update
```bash
nsupdate -y hmac-sha256:stalwart-update-key:c3RhbHdhcnQtdGVzdC10c2lnLXNlY3JldC1rZXkxMjM0NTY3ODkw <<EOF
server 127.0.0.1 5300
zone stalwart.test
update add test.stalwart.test 300 A 192.168.1.100
send
EOF
```
## ACME (Pebble + Challenge Test Server) Details
- **Directory URL**: `https://localhost:14000/dir`
- **Pebble Management URL**: `https://localhost:15000`
- **Challenge Test Server API**: `http://localhost:8055`
- **TLS**: Self-signed — Stalwart must trust the Pebble CA or skip TLS verification
- Pebble uses the challenge test server (`pebble-challtestsrv`) as its DNS resolver,
so challenge validation goes through controllable DNS/HTTP/TLS-ALPN responders.
### Challenge Test Server (challtestsrv)
The challenge test server provides a management API on port 8055 to programmatically
control DNS records and challenge responses used during ACME validation.
**Default behavior**: All A/AAAA queries resolve to `host.docker.internal` (the Docker
host), so Pebble can reach your test server on localhost automatically. Tests only need
to add challenge-specific records (TXT for DNS-01, HTTP tokens, etc.).
### How Pebble Reaches Your Test Server
When Pebble validates an HTTP-01 or TLS-ALPN-01 challenge, it:
1. Resolves the domain via challtestsrv — by default all domains resolve to the Docker host
2. Connects to the resolved IP on port **5002** (HTTP-01) or **5001** (TLS-ALPN-01)
These ports are configured in `pebble/pebble-config.json` (`httpPort` / `tlsPort`).
Change them to match whatever port your test Stalwart instance listens on.
#### Management API Examples
```bash
# Add a DNS-01 TXT challenge response
curl -s -X POST http://localhost:8055/add-dns \
-d '{"host": "_acme-challenge.mail.stalwart.test.", "value": "dns-challenge-token"}'
# Remove a DNS-01 TXT challenge response
curl -s -X POST http://localhost:8055/del-dns \
-d '{"host": "_acme-challenge.mail.stalwart.test."}'
# Add an HTTP-01 challenge response (served by challtestsrv itself)
curl -s -X POST http://localhost:8055/add-http \
-d '{"token": "challenge-token", "content": "challenge-key-authorization"}'
# Remove an HTTP-01 challenge response
curl -s -X POST http://localhost:8055/del-http \
-d '{"token": "challenge-token"}'
# Add a TLS-ALPN-01 challenge response (served by challtestsrv itself)
curl -s -X POST http://localhost:8055/add-tlsalpn \
-d '{"host": "mail.stalwart.test", "content": "base64-encoded-key-authz"}'
# Remove a TLS-ALPN-01 challenge response
curl -s -X POST http://localhost:8055/del-tlsalpn \
-d '{"host": "mail.stalwart.test"}'
# Clear all mock DNS/challenge data
curl -s -X POST http://localhost:8055/clear-request-count
```
## Self-Signed TLS Certificate
A shared self-signed certificate is generated at startup and mounted into services
that need it. The cert is valid for:
- `localhost`, `keycloak`, `openldap`, `pebble`, `*.stalwart.test`, `127.0.0.1`
To extract the cert for use with Stalwart:
```bash
docker compose cp cert-init:/certs/cert.pem ./test-cert.pem
docker compose cp cert-init:/certs/key.pem ./test-key.pem
```
## Troubleshooting
```bash
# Check all services are running
docker compose ps
# View logs for a specific service
docker compose logs -f keycloak
# Restart everything fresh
docker compose down && docker compose up -d
# Check FoundationDB status
docker compose exec foundationdb fdbcli --exec "status"
# Verify TSIG key is loaded
docker compose exec powerdns pdnsutil list-tsig-keys
```