feat(gitea): Stage 4 — hardened compose with OIDC-ready config + Keycloak CLI auth source

stacks/gitea/compose.yml — full production-grade env:
- Server posture: PROTOCOL=http (nginx terminates TLS), DOMAIN=git.wbd-rd.nl,
  DISABLE_SSH=true, INSTALL_LOCK=true (skip web wizard).
- Postgres backend (DB+role auto-provisioned by sql/config/init.d/).
- Local registration disabled; users provisioned via Keycloak OIDC with
  ENABLE_AUTO_REGISTRATION=true so first OIDC login auto-creates the
  matching local account (USERNAME=nickname, ACCOUNT_LINKING=auto).
- Mail stub via postfix on app network (ENABLED=false until postfix is up).
- Repos default to private.
- GITEA_OAUTH_* env vars are pass-through values consumed only by the
  post-deploy CLI step; gitea itself doesn't read them.

stacks/gitea/.env.example — DB connection, OAuth client ID/secret/discovery
URL, mail-from. Empty placeholders for secrets.

stacks/gitea/README.md — full Stage 5 deploy script:
  1. Fill GITEA_DB_PASSWORD + GITEA_OAUTH_CLIENT_SECRET in cloud/.env
  2. docker compose up -d gitea
  3. gitea admin user create --admin --random-password
  4. gitea admin auth add-oauth --provider openidConnect
     --auto-discover-url https://auth.wbd-rd.nl/realms/wbd/.well-known/openid-configuration
  5. Browse https://git.wbd-rd.nl/ → "Sign in with keycloak"

cloud/compose.yml — uncomment gitea include.
cloud/.env.example — add GITEA_DOMAIN, GITEA_OAUTH_*, GITEA_MAIL_FROM.

.gitignore line 2 (`.env`) already catches .env files at any depth
(verified with `git check-ignore`). Secrets won't be committed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-05-21 14:10:54 +02:00
parent af354a4b9e
commit 035ac757ae
5 changed files with 155 additions and 13 deletions

View File

@@ -1,5 +1,17 @@
GITEA_ROOT_URL=
# Gitea — HTTPS via nginx-proxy at https://git.wbd-rd.nl
GITEA_ROOT_URL=https://git.wbd-rd.nl
GITEA_DOMAIN=git.wbd-rd.nl
# Postgres backend (DB+role auto-provisioned by sql/config/init.d/01-databases.sh)
GITEA_DB_HOST=sql:5432
GITEA_DB_NAME=gitea
GITEA_DB_USER=gitea
GITEA_DB_PASSWORD=
# OIDC via Keycloak (auth source added via CLI post-deploy — see README)
GITEA_OAUTH_CLIENT_ID=gitea
GITEA_OAUTH_CLIENT_SECRET=
GITEA_OAUTH_DISCOVERY_URL=https://auth.wbd-rd.nl/realms/wbd/.well-known/openid-configuration
# Mail (gitea reads SMTP_ADDR=postfix on the app network)
GITEA_MAIL_FROM=gitea@wbd-rd.nl

View File

@@ -1,8 +1,89 @@
# gitea
Self-hosted git server. **Cloud-only.** (External repos live at `gitea.wbd-rd.nl`; this is for R&D internal use.)
Self-hosted git server for R&D. **Cloud-only.** Hostname `git.wbd-rd.nl` (reverse-proxied via nginx-proxy; HTTP internally).
- **Networks**: `app` (UI) + `data` (postgres backend in `sql` stack)
- **Volume**: `gitea-data` (repos + LFS + actions runners)
- **SSH ingress**: **disabled** (`DISABLE_SSH=true`). All clones over HTTPS via nginx-proxy. Re-evaluate only if Gitea Actions runners need SSH push semantics.
- **TODO**: Keycloak OIDC, Gitea Actions runners, LFS storage policy
- **Networks**: `app` (nginx → gitea) + `data` (gitea → postgres)
- **Volume**: `gitea-data` (repos, LFS, hooks, actions runners)
- **SSH ingress**: disabled (`DISABLE_SSH=true`). All clones over HTTPS.
- **Auth**: SSO via Keycloak (`wbd` realm, client `gitea`). Local registration disabled — users come in via OIDC.
The web installer is skipped (`INSTALL_LOCK=true`); admin user + OIDC source are provisioned with CLI commands post-deploy.
## Stage 5 — deploy
Prereqs (Stages 13, done by hand in Keycloak):
- `sql`, `nginx-proxy`, `keycloak`, `portainer` already up
- Keycloak `wbd` realm created
- Keycloak client `gitea` created with redirect URI `https://git.wbd-rd.nl/user/oauth2/keycloak/callback`
- Client secret captured
### 1. Fill cloud/.env
```
GITEA_DB_PASSWORD=<from your password manager>
GITEA_OAUTH_CLIENT_SECRET=<from Keycloak → Clients → gitea → Credentials>
```
### 2. Bring gitea up
```bash
cd /mnt/d/gitea/RnD/infra/cloud
docker compose up -d gitea
docker compose logs -f gitea
# Wait for: "Listen: http://0.0.0.0:3000"
```
### 3. Create the initial admin user (one-time)
```bash
docker compose exec -u git gitea \
gitea admin user create \
--admin --username admin \
--email admin@wbd-rd.nl \
--random-password \
--must-change-password=true
```
Note the printed random password; sign in once at `https://git.wbd-rd.nl/`, change it.
### 4. Add the Keycloak OIDC auth source
```bash
docker compose exec -u git gitea sh -c '
gitea admin auth add-oauth \
--name keycloak \
--provider openidConnect \
--key "$GITEA_OAUTH_CLIENT_ID" \
--secret "$GITEA_OAUTH_CLIENT_SECRET" \
--auto-discover-url "$GITEA_OAUTH_DISCOVERY_URL" \
--scopes "openid email profile"
'
```
(The env vars are passed through from `cloud/.env` via the gitea container's `environment:` block.)
### 5. Verify
Browse `https://git.wbd-rd.nl/` → "Sign in with keycloak" → first OIDC user is auto-created (because `oauth2_client.ENABLE_AUTO_REGISTRATION=true`).
## Mail
`GITEA__mailer__ENABLED=false` by default. Flip to `true` (in cloud/.env or here) once the `postfix` stack is up; Gitea then sends notifications via `smtp://postfix:25`.
## Migrating from Versio Gitea (deferred)
Plan once the new instance is verified working end-to-end:
1. `pg_dump` Versio Gitea's database
2. Restore to the new `gitea` postgres DB
3. `rsync` `/data/gitea/{repositories,lfs,avatars}` from old to new over the WG tunnel
4. Restart gitea
5. DNS cutover: point `gitea.wbd-rd.nl` at the new cloud IP (or leave both names; `git.wbd-rd.nl` is the canonical going forward)
## TODO
- Gitea Actions runners (post-cutover)
- Webhook → Jenkins on push (CI trigger)
- LFS storage policy + size limits
- Mirror policy for external GitHub orgs (read-only mirrors)
- Repo / org templates for new R&D projects

View File

@@ -1,6 +1,8 @@
# gitea — git server (cloud only)
# Networks: app + data (uses sql stack as DB backend)
# Ingress: HTTPS-only via nginx-proxy. No SSH port published.
# gitea — self-hosted git server (cloud only)
# Hostname: git.wbd-rd.nl (reverse-proxied via nginx-proxy; HTTP internally)
# Networks: app (UI) + data (postgres backend)
# Auth: SSO via Keycloak (`wbd` realm, client `gitea`)
# OIDC auth source is added via CLI post-deploy — see README.
services:
gitea:
@@ -8,16 +10,58 @@ services:
restart: unless-stopped
networks: [app, data]
environment:
# User / TZ
USER_UID: "1000"
USER_GID: "1000"
TZ: ${TZ:-Europe/Amsterdam}
# Server posture — nginx terminates TLS, gitea speaks HTTP on 3000
GITEA__server__ROOT_URL: ${GITEA_ROOT_URL}
GITEA__server__DOMAIN: ${GITEA_DOMAIN:-git.wbd-rd.nl}
GITEA__server__PROTOCOL: http
GITEA__server__DISABLE_SSH: "true"
GITEA__server__OFFLINE_MODE: "false"
# Skip web installer (admin user + OIDC source provisioned via CLI)
GITEA__security__INSTALL_LOCK: "true"
# Database — postgres on sql stack (DB+role auto-provisioned)
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: ${GITEA_DB_HOST:-sql:5432}
GITEA__database__NAME: ${GITEA_DB_NAME:-gitea}
GITEA__database__USER: ${GITEA_DB_USER}
GITEA__database__USER: ${GITEA_DB_USER:-gitea}
GITEA__database__PASSWD: ${GITEA_DB_PASSWORD}
TZ: ${TZ:-Europe/Amsterdam}
# Disable local registration; users come in via Keycloak OIDC
GITEA__service__DISABLE_REGISTRATION: "true"
GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION: "true"
GITEA__service__SHOW_REGISTRATION_BUTTON: "false"
GITEA__service__REQUIRE_SIGNIN_VIEW: "true"
GITEA__openid__ENABLE_OPENID_SIGNIN: "false"
GITEA__openid__ENABLE_OPENID_SIGNUP: "false"
# OIDC client behavior — auth source itself added via CLI (see README)
GITEA__oauth2_client__ENABLE_AUTO_REGISTRATION: "true"
GITEA__oauth2_client__UPDATE_AVATAR: "true"
GITEA__oauth2_client__USERNAME: nickname
GITEA__oauth2_client__ACCOUNT_LINKING: auto
# Mail (flip ENABLED to true after postfix stack is up)
GITEA__mailer__ENABLED: "false"
GITEA__mailer__SMTP_ADDR: postfix
GITEA__mailer__SMTP_PORT: "25"
GITEA__mailer__FROM: ${GITEA_MAIL_FROM:-gitea@wbd-rd.nl}
# Repo defaults
GITEA__repository__DEFAULT_PRIVATE: private
GITEA__repository__DEFAULT_PUSH_CREATE_PRIVATE: "true"
# Exposed inside the container so the OIDC-source CLI command can pick them up
# (gitea itself doesn't read these — they're only for the post-deploy CLI step)
GITEA_OAUTH_CLIENT_ID: ${GITEA_OAUTH_CLIENT_ID:-gitea}
GITEA_OAUTH_CLIENT_SECRET: ${GITEA_OAUTH_CLIENT_SECRET}
GITEA_OAUTH_DISCOVERY_URL: ${GITEA_OAUTH_DISCOVERY_URL:-https://auth.wbd-rd.nl/realms/wbd/.well-known/openid-configuration}
volumes:
- gitea-data:/data