Registry testing - part 9
This commit is contained in:
167
tests/docker/INSTRUCTIONS.md
Normal file
167
tests/docker/INSTRUCTIONS.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# 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, auto-valid challenges |
|
||||
| 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) Details
|
||||
|
||||
- **Directory URL**: `https://localhost:14000/dir`
|
||||
- **Management URL**: `https://localhost:15000`
|
||||
- **TLS**: Self-signed (use `PEBBLE_VA_ALWAYS_VALID=1` — all challenges auto-pass)
|
||||
- Stalwart must be configured to trust the Pebble CA or skip TLS verification.
|
||||
|
||||
## 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
|
||||
```
|
||||
248
tests/docker/docker-compose.yml
Normal file
248
tests/docker/docker-compose.yml
Normal file
@@ -0,0 +1,248 @@
|
||||
###############################################################################
|
||||
# Stalwart – Test Infrastructure
|
||||
# All services are ephemeral (no volumes = fresh on every restart)
|
||||
# Ports exposed on localhost; 80/443/8080 are remapped
|
||||
###############################################################################
|
||||
|
||||
services:
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Init: generate shared self-signed TLS certificates
|
||||
# ---------------------------------------------------------------------------
|
||||
cert-init:
|
||||
image: alpine/openssl:latest
|
||||
volumes:
|
||||
- certs:/certs
|
||||
- ./scripts/gen-certs.sh:/gen-certs.sh:ro
|
||||
entrypoint: [ "sh", "/gen-certs.sh" ]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PostgreSQL
|
||||
# ---------------------------------------------------------------------------
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: stalwart
|
||||
POSTGRES_PASSWORD: stalwart
|
||||
POSTGRES_DB: stalwart
|
||||
ports:
|
||||
- "127.0.0.1:5432:5432"
|
||||
tmpfs:
|
||||
- /var/lib/postgresql/data
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# MySQL
|
||||
# ---------------------------------------------------------------------------
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: stalwart
|
||||
MYSQL_DATABASE: stalwart
|
||||
MYSQL_USER: stalwart
|
||||
MYSQL_PASSWORD: stalwart
|
||||
ports:
|
||||
- "127.0.0.1:3306:3306"
|
||||
tmpfs:
|
||||
- /var/lib/mysql
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# FoundationDB
|
||||
# ---------------------------------------------------------------------------
|
||||
foundationdb:
|
||||
image: foundationdb/foundationdb:7.3.43
|
||||
platform: linux/amd64
|
||||
ports:
|
||||
- "127.0.0.1:4500:4500"
|
||||
environment:
|
||||
FDB_NETWORKING_MODE: container
|
||||
volumes:
|
||||
- fdb-config:/var/fdb
|
||||
tmpfs:
|
||||
- /var/fdb/data
|
||||
- /var/fdb/logs
|
||||
|
||||
fdb-init:
|
||||
image: foundationdb/foundationdb:7.3.43
|
||||
platform: linux/amd64
|
||||
depends_on:
|
||||
- foundationdb
|
||||
volumes:
|
||||
- fdb-config:/var/fdb
|
||||
- ./scripts/init-fdb.sh:/init-fdb.sh:ro
|
||||
entrypoint: [ "bash", "/init-fdb.sh" ]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Redis
|
||||
# ---------------------------------------------------------------------------
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "127.0.0.1:6379:6379"
|
||||
command: redis-server --save "" --appendonly no
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# OpenSearch (ElasticSearch-compatible)
|
||||
# ---------------------------------------------------------------------------
|
||||
opensearch:
|
||||
image: opensearchproject/opensearch:2
|
||||
environment:
|
||||
discovery.type: single-node
|
||||
DISABLE_SECURITY_PLUGIN: "true"
|
||||
OPENSEARCH_JAVA_OPTS: "-Xms256m -Xmx256m"
|
||||
DISABLE_INSTALL_DEMO_CONFIG: "true"
|
||||
ports:
|
||||
- "127.0.0.1:9200:9200"
|
||||
tmpfs:
|
||||
- /usr/share/opensearch/data:uid=1000,gid=1000
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Meilisearch
|
||||
# ---------------------------------------------------------------------------
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:latest
|
||||
environment:
|
||||
MEILI_ENV: development
|
||||
MEILI_NO_ANALYTICS: "true"
|
||||
MEILI_MASTER_KEY: stalwart-master-key
|
||||
ports:
|
||||
- "127.0.0.1:7700:7700"
|
||||
tmpfs:
|
||||
- /meili_data
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# MinIO (S3-compatible)
|
||||
# ---------------------------------------------------------------------------
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
environment:
|
||||
MINIO_ROOT_USER: minioadmin
|
||||
MINIO_ROOT_PASSWORD: minioadmin
|
||||
ports:
|
||||
- "127.0.0.1:9000:9000"
|
||||
- "127.0.0.1:9001:9001"
|
||||
command: server /data --console-address ":9001"
|
||||
tmpfs:
|
||||
- /data
|
||||
|
||||
minio-init:
|
||||
image: minio/mc:latest
|
||||
depends_on:
|
||||
- minio
|
||||
volumes:
|
||||
- ./scripts/init-minio.sh:/init-minio.sh:ro
|
||||
entrypoint: [ "bash", "/init-minio.sh" ]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Keycloak (OIDC Provider) – port 9080 (moved from 8080)
|
||||
# ---------------------------------------------------------------------------
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:latest
|
||||
depends_on:
|
||||
cert-init:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
KC_BOOTSTRAP_ADMIN_USERNAME: admin
|
||||
KC_BOOTSTRAP_ADMIN_PASSWORD: admin
|
||||
KC_HTTP_PORT: 9080
|
||||
KC_HEALTH_ENABLED: "true"
|
||||
ports:
|
||||
- "127.0.0.1:9080:9080"
|
||||
volumes:
|
||||
- ./keycloak/stalwart-realm.json:/opt/keycloak/data/import/stalwart-realm.json:ro
|
||||
- certs:/certs:ro
|
||||
command: >
|
||||
start-dev --import-realm
|
||||
tmpfs:
|
||||
- /opt/keycloak/data:uid=1000,gid=1000
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# OpenLDAP
|
||||
# ---------------------------------------------------------------------------
|
||||
openldap:
|
||||
image: osixia/openldap:1.5.0
|
||||
depends_on:
|
||||
cert-init:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
LDAP_ORGANISATION: "Stalwart Test"
|
||||
LDAP_DOMAIN: "stalwart.test"
|
||||
LDAP_BASE_DN: "dc=stalwart,dc=test"
|
||||
LDAP_ADMIN_PASSWORD: "admin"
|
||||
LDAP_READONLY_USER: "true"
|
||||
LDAP_READONLY_USER_USERNAME: "readonly"
|
||||
LDAP_READONLY_USER_PASSWORD: "readonly"
|
||||
LDAP_TLS: "true"
|
||||
LDAP_TLS_CRT_FILENAME: "cert.pem"
|
||||
LDAP_TLS_KEY_FILENAME: "key.pem"
|
||||
LDAP_TLS_CA_CRT_FILENAME: "cert.pem"
|
||||
LDAP_TLS_VERIFY_CLIENT: "never"
|
||||
LDAP_SEED_INTERNAL_LDIF_PATH: "/seed"
|
||||
ports:
|
||||
- "127.0.0.1:389:389"
|
||||
- "127.0.0.1:636:636"
|
||||
volumes:
|
||||
- ./ldap/seed.ldif:/seed/50-stalwart.ldif:ro
|
||||
- certs:/certs-shared:ro
|
||||
entrypoint: [ "/bin/bash", "-c", "cp /certs-shared/* /container/service/slapd/assets/certs/ 2>/dev/null; exec /container/tool/run" ]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Pebble (ACME server) – ports 14000 (directory) + 15000 (management)
|
||||
# ---------------------------------------------------------------------------
|
||||
pebble:
|
||||
image: ghcr.io/letsencrypt/pebble:latest
|
||||
environment:
|
||||
PEBBLE_VA_NOSLEEP: "1"
|
||||
PEBBLE_VA_ALWAYS_VALID: "1"
|
||||
ports:
|
||||
- "127.0.0.1:14000:14000"
|
||||
- "127.0.0.1:15000:15000"
|
||||
volumes:
|
||||
- ./pebble/pebble-config.json:/test/config/pebble-config.json:ro
|
||||
command: -config /test/config/pebble-config.json
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PowerDNS (DNS with TLSA + RFC2136) – port 5300 (moved from 53)
|
||||
# ---------------------------------------------------------------------------
|
||||
powerdns:
|
||||
image: powerdns/pdns-auth-49:latest
|
||||
environment:
|
||||
PDNS_AUTH_API_KEY: stalwart-api-key
|
||||
ports:
|
||||
- "127.0.0.1:5300:53/tcp"
|
||||
- "127.0.0.1:5300:53/udp"
|
||||
- "127.0.0.1:8081:8081"
|
||||
volumes:
|
||||
- ./powerdns/pdns.conf:/etc/powerdns/pdns.d/stalwart.conf:ro
|
||||
- ./powerdns/init-zone.sh:/etc/powerdns/init-zone.sh:ro
|
||||
tmpfs:
|
||||
- /var/lib/powerdns
|
||||
|
||||
powerdns-init:
|
||||
image: powerdns/pdns-auth-49:latest
|
||||
depends_on:
|
||||
- powerdns
|
||||
volumes:
|
||||
- ./powerdns/entrypoint.sh:/init.sh:ro
|
||||
- ./powerdns/init-zone.sh:/etc/powerdns/init-zone.sh:ro
|
||||
entrypoint: [ "bash", "/init.sh" ]
|
||||
network_mode: "service:powerdns"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# NATS (message queue, core mode)
|
||||
# ---------------------------------------------------------------------------
|
||||
nats:
|
||||
image: nats:latest
|
||||
ports:
|
||||
- "127.0.0.1:4222:4222"
|
||||
- "127.0.0.1:8222:8222"
|
||||
command: "--addr 0.0.0.0 --port 4222 --http_port 8222"
|
||||
|
||||
# =============================================================================
|
||||
# Shared volumes (ephemeral – docker compose down removes them)
|
||||
# =============================================================================
|
||||
volumes:
|
||||
certs:
|
||||
driver: local
|
||||
fdb-config:
|
||||
driver: local
|
||||
113
tests/docker/keycloak/stalwart-realm.json
Normal file
113
tests/docker/keycloak/stalwart-realm.json
Normal file
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"realm": "stalwart",
|
||||
"enabled": true,
|
||||
"registrationAllowed": false,
|
||||
"loginWithEmailAllowed": true,
|
||||
"duplicateEmailsAllowed": false,
|
||||
"sslRequired": "none",
|
||||
"clients": [
|
||||
{
|
||||
"clientId": "stalwart",
|
||||
"enabled": true,
|
||||
"clientAuthenticatorType": "client-secret",
|
||||
"secret": "stalwart-secret",
|
||||
"redirectUris": ["*"],
|
||||
"webOrigins": ["*"],
|
||||
"publicClient": false,
|
||||
"protocol": "openid-connect",
|
||||
"directAccessGrantsEnabled": true,
|
||||
"standardFlowEnabled": true,
|
||||
"serviceAccountsEnabled": true,
|
||||
"defaultClientScopes": ["openid", "email", "profile", "roles"],
|
||||
"protocolMappers": [
|
||||
{
|
||||
"name": "groups",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-group-membership-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"full.path": "false",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "groups",
|
||||
"userinfo.token.claim": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "email-claim",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "email",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "email",
|
||||
"userinfo.token.claim": "true",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"username": "john.doe@example.org",
|
||||
"enabled": true,
|
||||
"email": "john.doe@example.org",
|
||||
"emailVerified": true,
|
||||
"firstName": "John",
|
||||
"lastName": "Doe",
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
"value": "this is an OIDC password",
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"groups": ["/sales@example.org"]
|
||||
},
|
||||
{
|
||||
"username": "jane.smith@example.org",
|
||||
"enabled": true,
|
||||
"email": "jane.smith@example.org",
|
||||
"emailVerified": true,
|
||||
"firstName": "Jane",
|
||||
"lastName": "Smith",
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
"value": "this is an OIDC password",
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"groups": ["/sales@example.org", "/corporate@example.org"]
|
||||
},
|
||||
{
|
||||
"username": "bill.foobar@example.org",
|
||||
"enabled": true,
|
||||
"email": "bill.foobar@example.org",
|
||||
"emailVerified": true,
|
||||
"firstName": "Bill",
|
||||
"lastName": "Foobar",
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
"value": "this is an OIDC password",
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"groups": ["/corporate@example.org"]
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"name": "sales@example.org",
|
||||
"path": "/sales@example.org"
|
||||
},
|
||||
{
|
||||
"name": "corporate@example.org",
|
||||
"path": "/corporate@example.org"
|
||||
}
|
||||
]
|
||||
}
|
||||
69
tests/docker/ldap/seed.ldif
Normal file
69
tests/docker/ldap/seed.ldif
Normal file
@@ -0,0 +1,69 @@
|
||||
# Organizational Units
|
||||
dn: ou=users,dc=stalwart,dc=test
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
|
||||
dn: ou=groups,dc=stalwart,dc=test
|
||||
objectClass: organizationalUnit
|
||||
ou: groups
|
||||
|
||||
# Users
|
||||
dn: uid=john.doe,ou=users,dc=stalwart,dc=test
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
uid: john.doe
|
||||
cn: John Doe
|
||||
sn: Doe
|
||||
givenName: John
|
||||
mail: john.doe@example.org
|
||||
userPassword: this is an LDAP password
|
||||
uidNumber: 10001
|
||||
gidNumber: 10001
|
||||
homeDirectory: /home/john.doe
|
||||
loginShell: /bin/bash
|
||||
|
||||
dn: uid=jane.smith,ou=users,dc=stalwart,dc=test
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
uid: jane.smith
|
||||
cn: Jane Smith
|
||||
sn: Smith
|
||||
givenName: Jane
|
||||
mail: jane.smith@example.org
|
||||
userPassword: this is an LDAP password
|
||||
uidNumber: 10002
|
||||
gidNumber: 10002
|
||||
homeDirectory: /home/jane.smith
|
||||
loginShell: /bin/bash
|
||||
|
||||
dn: uid=bill.foobar,ou=users,dc=stalwart,dc=test
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
uid: bill.foobar
|
||||
cn: Bill Foobar
|
||||
sn: Foobar
|
||||
givenName: Bill
|
||||
mail: bill.foobar@example.org
|
||||
userPassword: this is an LDAP password
|
||||
uidNumber: 10003
|
||||
gidNumber: 10003
|
||||
homeDirectory: /home/bill.foobar
|
||||
loginShell: /bin/bash
|
||||
|
||||
# Groups with email addresses
|
||||
dn: cn=sales,ou=groups,dc=stalwart,dc=test
|
||||
objectClass: groupOfNames
|
||||
cn: sales
|
||||
mail: sales@example.org
|
||||
member: uid=john.doe,ou=users,dc=stalwart,dc=test
|
||||
member: uid=jane.smith,ou=users,dc=stalwart,dc=test
|
||||
|
||||
dn: cn=corporate,ou=groups,dc=stalwart,dc=test
|
||||
objectClass: groupOfNames
|
||||
cn: corporate
|
||||
mail: corporate@example.org
|
||||
member: uid=bill.foobar,ou=users,dc=stalwart,dc=test
|
||||
member: uid=jane.smith,ou=users,dc=stalwart,dc=test
|
||||
17
tests/docker/pebble/pebble-config.json
Normal file
17
tests/docker/pebble/pebble-config.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"pebble": {
|
||||
"listenAddress": "0.0.0.0:14000",
|
||||
"managementListenAddress": "0.0.0.0:15000",
|
||||
"certificate": "/test/certs/localhost/cert.pem",
|
||||
"privateKey": "/test/certs/localhost/key.pem",
|
||||
"httpPort": 5002,
|
||||
"tlsPort": 5001,
|
||||
"ocspResponderURL": "",
|
||||
"externalAccountBindingRequired": false,
|
||||
"domainBlocklist": [],
|
||||
"retryAfter": {
|
||||
"authz": 3,
|
||||
"order": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
14
tests/docker/powerdns/entrypoint.sh
Executable file
14
tests/docker/powerdns/entrypoint.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Wait for PowerDNS to be ready (started by default entrypoint)
|
||||
echo "Waiting for PowerDNS to start..."
|
||||
for i in $(seq 1 30); do
|
||||
if pdnsutil list-all-zones 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Run zone initialization
|
||||
bash /etc/powerdns/init-zone.sh
|
||||
34
tests/docker/powerdns/init-zone.sh
Executable file
34
tests/docker/powerdns/init-zone.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Wait for the SQLite database to be ready
|
||||
sleep 2
|
||||
|
||||
# Create the zone
|
||||
pdnsutil create-zone stalwart.test ns1.stalwart.test
|
||||
pdnsutil set-kind stalwart.test native
|
||||
|
||||
# Add basic records
|
||||
pdnsutil add-record stalwart.test '' SOA 'ns1.stalwart.test. admin.stalwart.test. 2024010101 3600 900 604800 86400'
|
||||
pdnsutil add-record stalwart.test '' NS 'ns1.stalwart.test.'
|
||||
pdnsutil add-record stalwart.test 'ns1' A '127.0.0.1'
|
||||
pdnsutil add-record stalwart.test '' A '127.0.0.1'
|
||||
pdnsutil add-record stalwart.test '' MX '10 mail.stalwart.test.'
|
||||
pdnsutil add-record stalwart.test 'mail' A '127.0.0.1'
|
||||
|
||||
# Add a sample TLSA record
|
||||
# Usage=3 (DANE-EE), Selector=1 (SubjectPublicKeyInfo), Matching=1 (SHA-256)
|
||||
pdnsutil add-record stalwart.test '_25._tcp.mail' TLSA '3 1 1 0000000000000000000000000000000000000000000000000000000000000000'
|
||||
|
||||
# Import static TSIG key for RFC2136 dynamic updates
|
||||
# Key: stalwart-update-key / HMAC-SHA256
|
||||
# Base64 secret: c3RhbHdhcnQtdGVzdC10c2lnLXNlY3JldC1rZXkxMjM0NTY3ODkw
|
||||
pdnsutil import-tsig-key stalwart-update-key hmac-sha256 'c3RhbHdhcnQtdGVzdC10c2lnLXNlY3JldC1rZXkxMjM0NTY3ODkw'
|
||||
pdnsutil activate-tsig-key stalwart.test stalwart-update-key master
|
||||
pdnsutil set-meta stalwart.test TSIG-ALLOW-DNSUPDATE stalwart-update-key
|
||||
pdnsutil set-meta stalwart.test ALLOW-DNSUPDATE-FROM '0.0.0.0/0'
|
||||
|
||||
echo "PowerDNS zone setup complete."
|
||||
echo "TSIG key name: stalwart-update-key"
|
||||
echo "TSIG algorithm: hmac-sha256"
|
||||
echo "TSIG secret (b64): c3RhbHdhcnQtdGVzdC10c2lnLXNlY3JldC1rZXkxMjM0NTY3ODkw"
|
||||
2
tests/docker/powerdns/pdns.conf
Normal file
2
tests/docker/powerdns/pdns.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
dnsupdate=yes
|
||||
allow-dnsupdate-from=0.0.0.0/0
|
||||
26
tests/docker/scripts/gen-certs.sh
Executable file
26
tests/docker/scripts/gen-certs.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CERT_DIR=/certs
|
||||
|
||||
if [ ! -f "$CERT_DIR/cert.pem" ]; then
|
||||
echo "Generating self-signed certificate..."
|
||||
openssl req -x509 -newkey rsa:2048 -nodes \
|
||||
-keyout "$CERT_DIR/key.pem" \
|
||||
-out "$CERT_DIR/cert.pem" \
|
||||
-days 365 \
|
||||
-subj "/CN=localhost/O=Stalwart Test/C=US" \
|
||||
-addext "subjectAltName=DNS:localhost,DNS:keycloak,DNS:openldap,DNS:pebble,DNS:*.stalwart.test,IP:127.0.0.1"
|
||||
|
||||
# Create combined PEM for services that need it
|
||||
cat "$CERT_DIR/cert.pem" "$CERT_DIR/key.pem" > "$CERT_DIR/combined.pem"
|
||||
|
||||
# Create PKCS12 for Keycloak
|
||||
openssl pkcs12 -export -in "$CERT_DIR/cert.pem" -inkey "$CERT_DIR/key.pem" \
|
||||
-out "$CERT_DIR/keystore.p12" -name localhost -password pass:changeit
|
||||
|
||||
chmod 644 "$CERT_DIR"/*
|
||||
echo "Certificates generated."
|
||||
else
|
||||
echo "Certificates already exist."
|
||||
fi
|
||||
7
tests/docker/scripts/init-fdb.sh
Executable file
7
tests/docker/scripts/init-fdb.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
fdbcli --exec "configure new single memory"
|
||||
echo "FoundationDB configured."
|
||||
exit 0
|
||||
|
||||
12
tests/docker/scripts/init-minio.sh
Executable file
12
tests/docker/scripts/init-minio.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Wait for MinIO to be ready
|
||||
echo "Waiting for MinIO..."
|
||||
until mc alias set local http://minio:9000 minioadmin minioadmin 2>/dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Create the stalwart bucket
|
||||
mc mb local/stalwart --ignore-existing
|
||||
echo "MinIO bucket 'stalwart' created."
|
||||
Reference in New Issue
Block a user