Docker audit fixes #1
@@ -17,6 +17,11 @@ ZITADEL_MASTERKEY=MasterkeyNeedsToHave32Characters
|
|||||||
ZITADEL_EXTERNAL_DOMAIN=host.docker.internal
|
ZITADEL_EXTERNAL_DOMAIN=host.docker.internal
|
||||||
ZITADEL_EXTERNAL_PORT=8080
|
ZITADEL_EXTERNAL_PORT=8080
|
||||||
ZITADEL_EXTERNAL_SECURE=false
|
ZITADEL_EXTERNAL_SECURE=false
|
||||||
|
ZITADEL_PUBLIC_SCHEME=http
|
||||||
|
# Login V2 UI (zitadel-login). In dev it has its own port; the URL has no
|
||||||
|
# trailing slash. Only read when the Zitadel instance is created.
|
||||||
|
ZITADEL_LOGIN_HOST_PORT=3100
|
||||||
|
ZITADEL_LOGIN_BASE_URL=http://host.docker.internal:3100/ui/v2/login
|
||||||
ZITADEL_DATABASE_PASSWORD=zitadel
|
ZITADEL_DATABASE_PASSWORD=zitadel
|
||||||
# Console login: <username>@zitadel.<external domain>
|
# Console login: <username>@zitadel.<external domain>
|
||||||
ZITADEL_ADMIN_USERNAME=admin
|
ZITADEL_ADMIN_USERNAME=admin
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ ZITADEL_EXTERNAL_DOMAIN=auth.example.com
|
|||||||
ZITADEL_EXTERNAL_PORT=443
|
ZITADEL_EXTERNAL_PORT=443
|
||||||
ZITADEL_EXTERNAL_SECURE=true
|
ZITADEL_EXTERNAL_SECURE=true
|
||||||
ZITADEL_TLS_MODE=external
|
ZITADEL_TLS_MODE=external
|
||||||
|
ZITADEL_PUBLIC_SCHEME=https
|
||||||
|
# Login V2 UI, routed by the edge under the same hostname. No trailing slash.
|
||||||
|
ZITADEL_LOGIN_BASE_URL=https://auth.example.com/ui/v2/login
|
||||||
ZITADEL_DATABASE_PASSWORD=CHANGE_ME
|
ZITADEL_DATABASE_PASSWORD=CHANGE_ME
|
||||||
ZITADEL_ORG_NAME=Iker Demo
|
ZITADEL_ORG_NAME=Iker Demo
|
||||||
ZITADEL_ADMIN_USERNAME=admin@ikerdemo.cat
|
ZITADEL_ADMIN_USERNAME=admin@ikerdemo.cat
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ cp .env.example .env
|
|||||||
docker compose -f compose.yml -f compose.dev.yml up -d
|
docker compose -f compose.yml -f compose.dev.yml up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
That starts Postgres, Zitadel, SeaweedFS, RabbitMQ and Jaeger, with their
|
That starts Postgres, Zitadel (and its login UI), SeaweedFS, RabbitMQ and Jaeger, with their
|
||||||
ports published to `localhost`. Add `--profile tools` to also start pgAdmin.
|
ports published to `localhost`. Add `--profile tools` to also start pgAdmin.
|
||||||
|
|
||||||
In production, the base `compose.yml` file **is** the production shape
|
In production, the base `compose.yml` file **is** the production shape
|
||||||
@@ -60,6 +60,7 @@ external network to reach `postgres`, `zitadel`, `seaweedfs`, `rabbitmq` and
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| PostgreSQL | `5432` | one instance, one database per project |
|
| PostgreSQL | `5432` | one instance, one database per project |
|
||||||
| Zitadel | `8080` | OIDC/OAuth2, console at `/ui/console` |
|
| Zitadel | `8080` | OIDC/OAuth2, console at `/ui/console` |
|
||||||
|
| Zitadel login | `3100` | Login V2 UI at `/ui/v2/login` (under `8080`'s hostname in prod) |
|
||||||
| SeaweedFS | `8333` / `8888` / `9333` | S3 API / filer / master |
|
| SeaweedFS | `8333` / `8888` / `9333` | S3 API / filer / master |
|
||||||
| RabbitMQ | `5672` / `15672` | AMQP / management UI |
|
| RabbitMQ | `5672` / `15672` | AMQP / management UI |
|
||||||
| Jaeger | `16686` / `4317` / `4318` | UI / OTLP gRPC / OTLP HTTP |
|
| Jaeger | `16686` / `4317` / `4318` | UI / OTLP gRPC / OTLP HTTP |
|
||||||
@@ -95,6 +96,37 @@ with an example value that assumes these repos happen to sit as sibling
|
|||||||
folders. If yours don't, change it. Not versioned, and regenerated whenever
|
folders. If yours don't, change it. Not versioned, and regenerated whenever
|
||||||
the Zitadel volume is reset.
|
the Zitadel volume is reset.
|
||||||
|
|
||||||
|
### The Zitadel login (Login V2)
|
||||||
|
|
||||||
|
Zitadel v4 serves its sign-in pages from a separate container,
|
||||||
|
`zitadel-login` (Next.js), under `/ui/v2/login`. Zitadel redirects every
|
||||||
|
login there, so it has to be running for anyone to sign in. It calls the
|
||||||
|
Zitadel API with its own service account (`login-client`), whose PAT Zitadel
|
||||||
|
writes on init to the `zitadel_bootstrap` volume, which only `zitadel-login`
|
||||||
|
mounts.
|
||||||
|
|
||||||
|
- **Dev**: no proxy, so the login has its own port (`ZITADEL_LOGIN_HOST_PORT`,
|
||||||
|
`3100`) and `ZITADEL_LOGIN_BASE_URL` points the browser at it.
|
||||||
|
- **Prod**: the edge routes `/ui/v2/login` on Zitadel's hostname to
|
||||||
|
`infra-zitadel-login`, and everything else to Zitadel.
|
||||||
|
- `zitadel` and `zitadel-login` are pinned to the same version; bump both
|
||||||
|
together.
|
||||||
|
|
||||||
|
`ZITADEL_LOGIN_BASE_URL` and the `login-client` PAT are only applied when the
|
||||||
|
instance is created. On an instance created before `zitadel-login` existed:
|
||||||
|
|
||||||
|
1. In the console, create a service user `login-client`, give it the
|
||||||
|
instance role **IAM Login Client**, and create a PAT for it.
|
||||||
|
2. Copy it into the volume and restart the login:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker cp login-client.pat infra-zitadel:/bootstrap/login-client.pat
|
||||||
|
docker restart infra-zitadel-login
|
||||||
|
```
|
||||||
|
|
||||||
|
3. If the instance still uses the old login, set Login V2 as required with
|
||||||
|
base URI `${ZITADEL_LOGIN_BASE_URL}/` in the instance's feature settings.
|
||||||
|
|
||||||
## The public edge
|
## The public edge
|
||||||
|
|
||||||
`edge/compose.yml` is the only thing that publishes host ports 80/443: one
|
`edge/compose.yml` is the only thing that publishes host ports 80/443: one
|
||||||
@@ -138,7 +170,7 @@ re-creates its piece on the next `up`, since both are idempotent.
|
|||||||
|
|
||||||
```
|
```
|
||||||
Infrastructure/
|
Infrastructure/
|
||||||
├─ compose.yml # postgres, zitadel, seaweedfs, rabbitmq, jaeger: the prod shape
|
├─ compose.yml # postgres, zitadel (+ login), seaweedfs, rabbitmq, jaeger: the prod shape
|
||||||
├─ edge/ # shared public nginx + certbot (prod only): compose.yml, conf.d/, snippets/
|
├─ edge/ # shared public nginx + certbot (prod only): compose.yml, conf.d/, snippets/
|
||||||
├─ compose.dev.yml # dev overlay: publishes ports, adds pgAdmin
|
├─ compose.dev.yml # dev overlay: publishes ports, adds pgAdmin
|
||||||
├─ .env.example / .env.prod.example
|
├─ .env.example / .env.prod.example
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "${ZITADEL_EXTERNAL_PORT}:8080"
|
- "${ZITADEL_EXTERNAL_PORT}:8080"
|
||||||
|
|
||||||
|
# No edge in dev to put the login under Zitadel's own port, so it gets its
|
||||||
|
# own (ZITADEL_LOGIN_BASE_URL in .env points the browser at it).
|
||||||
|
zitadel-login:
|
||||||
|
ports:
|
||||||
|
- "${ZITADEL_LOGIN_HOST_PORT}:3000"
|
||||||
|
|
||||||
seaweedfs:
|
seaweedfs:
|
||||||
ports:
|
ports:
|
||||||
- "9333:9333" # master / admin UI
|
- "9333:9333" # master / admin UI
|
||||||
|
|||||||
+53
-1
@@ -45,8 +45,13 @@ services:
|
|||||||
# setup (both idempotent) and then starts, so the same command works on a
|
# setup (both idempotent) and then starts, so the same command works on a
|
||||||
# cold and on a warm volume.
|
# cold and on a warm volume.
|
||||||
zitadel:
|
zitadel:
|
||||||
image: ghcr.io/zitadel/zitadel:latest
|
# Pinned together with zitadel-login below: bump both at once.
|
||||||
|
image: ghcr.io/zitadel/zitadel:v4.19.1
|
||||||
container_name: infra-zitadel
|
container_name: infra-zitadel
|
||||||
|
# The image runs as uid 1000, but Docker creates both the bind mount
|
||||||
|
# (on Linux) and the bootstrap volume as root, so Zitadel couldn't write
|
||||||
|
# the PATs into them. Same as Zitadel's own compose.
|
||||||
|
user: "0"
|
||||||
# `disabled` (dev, plain http) makes Zitadel advertise http URLs; behind a
|
# `disabled` (dev, plain http) makes Zitadel advertise http URLs; behind a
|
||||||
# TLS-terminating proxy it must be `external` or the console calls
|
# TLS-terminating proxy it must be `external` or the console calls
|
||||||
# http://<domain> and the CSP blocks it.
|
# http://<domain> and the CSP blocks it.
|
||||||
@@ -86,11 +91,57 @@ services:
|
|||||||
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME: Admin Service Account
|
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME: Admin Service Account
|
||||||
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_PAT_EXPIRATIONDATE: "2099-01-01T00:00:00Z"
|
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_PAT_EXPIRATIONDATE: "2099-01-01T00:00:00Z"
|
||||||
ZITADEL_FIRSTINSTANCE_PATPATH: /output/admin-sa.pat
|
ZITADEL_FIRSTINSTANCE_PATPATH: /output/admin-sa.pat
|
||||||
|
# Service account the login UI (zitadel-login) calls the API with. Its
|
||||||
|
# PAT goes to a volume only that container mounts, not to /output.
|
||||||
|
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_USERNAME: login-client
|
||||||
|
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_NAME: Automatically Initialized IAM_LOGIN_CLIENT
|
||||||
|
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_PAT_EXPIRATIONDATE: "2099-01-01T00:00:00Z"
|
||||||
|
ZITADEL_FIRSTINSTANCE_LOGINCLIENTPATPATH: /bootstrap/login-client.pat
|
||||||
|
# Login V2: sign-in pages are served by zitadel-login, not by Zitadel
|
||||||
|
# itself. Like the FIRSTINSTANCE values, these are only read when the
|
||||||
|
# instance is created (see README for an existing instance).
|
||||||
|
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED: "true"
|
||||||
|
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_BASEURI: ${ZITADEL_LOGIN_BASE_URL}/
|
||||||
|
ZITADEL_OIDC_DEFAULTLOGINURLV2: ${ZITADEL_LOGIN_BASE_URL}/login?authRequest=
|
||||||
|
ZITADEL_OIDC_DEFAULTLOGOUTURLV2: ${ZITADEL_LOGIN_BASE_URL}/logout?post_logout_redirect=
|
||||||
|
ZITADEL_SAML_DEFAULTLOGINURLV2: ${ZITADEL_LOGIN_BASE_URL}/login?samlRequest=
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
volumes:
|
volumes:
|
||||||
- ./zitadel/.output:/output
|
- ./zitadel/.output:/output
|
||||||
|
- zitadel_bootstrap:/bootstrap
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "/app/zitadel", "ready"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 12
|
||||||
|
start_period: 20s
|
||||||
|
networks: [infra-net]
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Login V2 UI (Next.js), served under /ui/v2/login. It calls the API
|
||||||
|
# directly on infra-net, so it has to present the public Host/scheme for
|
||||||
|
# Zitadel to find the instance.
|
||||||
|
zitadel-login:
|
||||||
|
image: ghcr.io/zitadel/zitadel-login:v4.19.1
|
||||||
|
container_name: infra-zitadel-login
|
||||||
|
environment:
|
||||||
|
ZITADEL_API_URL: http://zitadel:8080
|
||||||
|
NEXT_PUBLIC_BASE_PATH: /ui/v2/login
|
||||||
|
ZITADEL_SERVICE_USER_TOKEN_FILE: /bootstrap/login-client.pat
|
||||||
|
CUSTOM_REQUEST_HEADERS: Host:${ZITADEL_EXTERNAL_DOMAIN},X-Forwarded-Proto:${ZITADEL_PUBLIC_SCHEME}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "/bin/sh", "-c", "node /app/healthcheck.mjs http://localhost:3000/ui/v2/login/healthy"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 12
|
||||||
|
start_period: 20s
|
||||||
|
depends_on:
|
||||||
|
zitadel:
|
||||||
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
- zitadel_bootstrap:/bootstrap:ro
|
||||||
networks: [infra-net]
|
networks: [infra-net]
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
@@ -166,5 +217,6 @@ services:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
|
zitadel_bootstrap:
|
||||||
seaweedfs_data:
|
seaweedfs_data:
|
||||||
rabbitmq_data:
|
rabbitmq_data:
|
||||||
|
|||||||
@@ -80,11 +80,17 @@ server {
|
|||||||
ssl_certificate_key /etc/letsencrypt/live/musify.ikerdemo.cat/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/musify.ikerdemo.cat/privkey.pem;
|
||||||
include /etc/nginx/snippets/tls.conf;
|
include /etc/nginx/snippets/tls.conf;
|
||||||
|
|
||||||
|
# Login V2 UI, a separate container (see ../compose.yml).
|
||||||
|
location /ui/v2/login {
|
||||||
|
set $upstream http://infra-zitadel-login:3000;
|
||||||
|
proxy_pass $upstream;
|
||||||
|
include /etc/nginx/snippets/proxy.conf;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
set $upstream http://zitadel:8080;
|
set $upstream http://zitadel:8080;
|
||||||
proxy_pass $upstream;
|
proxy_pass $upstream;
|
||||||
include /etc/nginx/snippets/proxy.conf;
|
include /etc/nginx/snippets/proxy.conf;
|
||||||
grpc_set_header Host $host;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user