From b2e878eae905a46aa1114446408cdfc48e421d92 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Mon, 11 May 2026 15:42:26 +0200 Subject: [PATCH 1/3] feat: add Gitea Docker registry deployment with user provisioning (#142) Deploy a standalone Gitea instance configured as an OCI Docker registry, with automated user/SSH-key provisioning and personal access token generation for docker login. Two-stage build (golang:alpine builder, gitea/gitea:latest runtime) matches the #141 reference pattern. --- .../docker/admin/gitea-registry/.dockerignore | 7 + .../docker/admin/gitea-registry/.env.example | 29 +++ .../docker/admin/gitea-registry/Dockerfile | 28 +++ .../docker/admin/gitea-registry/Makefile | 98 ++++++++++ .../docker/admin/gitea-registry/README.md | 179 ++++++++++++++++++ .../docker/admin/gitea-registry/compose.yml | 93 +++++++++ .../admin/gitea-registry/config/app.ini | 27 +++ .../admin/gitea-registry/provisioning/init.sh | 153 +++++++++++++++ .../gitea-registry/provisioning/users.yml | 33 ++++ 9 files changed, 647 insertions(+) create mode 100644 03_container_layer/docker/admin/gitea-registry/.dockerignore create mode 100644 03_container_layer/docker/admin/gitea-registry/.env.example create mode 100644 03_container_layer/docker/admin/gitea-registry/Dockerfile create mode 100644 03_container_layer/docker/admin/gitea-registry/Makefile create mode 100644 03_container_layer/docker/admin/gitea-registry/README.md create mode 100644 03_container_layer/docker/admin/gitea-registry/compose.yml create mode 100644 03_container_layer/docker/admin/gitea-registry/config/app.ini create mode 100644 03_container_layer/docker/admin/gitea-registry/provisioning/init.sh create mode 100644 03_container_layer/docker/admin/gitea-registry/provisioning/users.yml diff --git a/03_container_layer/docker/admin/gitea-registry/.dockerignore b/03_container_layer/docker/admin/gitea-registry/.dockerignore new file mode 100644 index 0000000..a3f6cd0 --- /dev/null +++ b/03_container_layer/docker/admin/gitea-registry/.dockerignore @@ -0,0 +1,7 @@ +.env +*.env +*.key +*.pem +*.p12 +*.pfx +README.md diff --git a/03_container_layer/docker/admin/gitea-registry/.env.example b/03_container_layer/docker/admin/gitea-registry/.env.example new file mode 100644 index 0000000..cff2baf --- /dev/null +++ b/03_container_layer/docker/admin/gitea-registry/.env.example @@ -0,0 +1,29 @@ +# +# ISSUE 142 +# +# Copy to .env and fill in values before running: cp .env.example .env +# .env is gitignored — never commit real secrets. +# + +# ── Gitea ────────────────────────────────────────────────────────────────── +# Registry domain (used for docker login) +GITEA_DOMAIN=localhost +GITEA_BASE_URL=http://localhost:3000 + +# Generate with: openssl rand -hex 32 +GITEA_SECRET_KEY=CHANGEME_generate_with_openssl_rand_hex_32 +# Generate with: gitea generate secret INTERNAL_TOKEN +GITEA_INTERNAL_TOKEN=CHANGEME_generate_with_gitea_generate_secret + +# ── Initial admin (must match admins[0] in provisioning/users.yml) ───────── +GITEA_ADMIN_USER=gitea-admin +GITEA_ADMIN_PASS=Admin1234! + +# ── PostgreSQL ────────────────────────────────────────────────────────────── +POSTGRES_USER=gitea +POSTGRES_PASSWORD=CHANGEME_replace_before_deploying +POSTGRES_DB=gitea + +# ── Host ports ────────────────────────────────────────────────────────────── +HTTP_PORT=3000 +SSH_PORT=2222 diff --git a/03_container_layer/docker/admin/gitea-registry/Dockerfile b/03_container_layer/docker/admin/gitea-registry/Dockerfile new file mode 100644 index 0000000..07c979e --- /dev/null +++ b/03_container_layer/docker/admin/gitea-registry/Dockerfile @@ -0,0 +1,28 @@ +# +# ISSUE 142 +# + +# +# BUILDER — golang:alpine (same base as official Gitea Dockerfile) +# Installs yq for YAML parsing; copies and pre-validates provisioning scripts. +# +FROM golang:alpine AS builder + +RUN apk add --no-cache yq jq + +COPY provisioning/ /provisioning/ +RUN chmod +x /provisioning/init.sh + +# +# RUNTIME — gitea/gitea:latest (Alpine-based, ships the gitea CLI) +# Copies tooling and provisioning scripts from builder. +# Acts as the provisioner sidecar: creates users via CLI + SSH keys via REST API +# and generates personal access tokens for Docker registry usage. +# +FROM gitea/gitea:latest AS runtime + +COPY --from=builder /usr/bin/yq /usr/bin/yq +COPY --from=builder /usr/bin/jq /usr/bin/jq +COPY --from=builder /provisioning/ /provisioning/ + +ENTRYPOINT ["/provisioning/init.sh"] diff --git a/03_container_layer/docker/admin/gitea-registry/Makefile b/03_container_layer/docker/admin/gitea-registry/Makefile new file mode 100644 index 0000000..24cbe06 --- /dev/null +++ b/03_container_layer/docker/admin/gitea-registry/Makefile @@ -0,0 +1,98 @@ +# +# ISSUE 142 +# + +SERVICE = gitea-registry +PROVISIONER = gitea-registry-provisioner +DEBUG_SERVICE = $(SERVICE)-debug + +help: + @echo "" + @echo "" + @echo " Available : " + @echo "" + @echo " make up - run the full stack (db + gitea + provisioner) in background" + @echo " make down - stop and remove all containers" + @echo " make stop - stop $(SERVICE) only" + @echo "" + @echo " make build - build the provisioner image" + @echo " make build-up - build provisioner image then start the full stack" + @echo "" + @echo " make rebuild - full rebuild without cache" + @echo " make rebuild-up - full rebuild without cache then start" + @echo "" + @echo " make reprovision - remove the provisioning stamp and restart provisioner" + @echo " make logs-provisioner - tail provisioner output" + @echo "" + @echo " make tokens - display generated registry tokens" + @echo "" + @echo " make term - open shell in $(SERVICE) container" + @echo " make term-debug-build - build and open shell in $(DEBUG_SERVICE)" + @echo " make clean - delete all containers, images, volumes and unused network" + @echo " make print - print this help" + @echo "" + @echo "" + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +up: + docker compose up -d +down: + docker compose down +stop: + docker compose stop $(SERVICE) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +build: + docker compose build + +rebuild: + docker compose down + docker compose build --no-cache + +# # # # # # # # # # # # + +build-up: + docker compose build + docker compose up -d + +rebuild-up: + docker compose down + docker compose build --no-cache + docker compose up -d + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +reprovision: + docker exec $(SERVICE) rm -f /data/gitea/.provisioned + docker compose restart $(PROVISIONER) + +logs-provisioner: + docker compose logs -f $(PROVISIONER) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +tokens: + docker exec $(PROVISIONER) cat /tokens/tokens.txt + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +clean: + docker system prune -a --volumes -f + -docker stop $(shell docker ps -aq) + -docker rm -f $(shell docker ps -aq) + -docker rmi -f $(shell docker images -q) + -docker volume rm -f $(shell docker volume ls -q) + -docker network prune -f + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +term: + docker exec -it $(SERVICE) /bin/sh + +term-debug-build: + docker compose up --build -d debug + docker exec -it $(DEBUG_SERVICE) /bin/sh + +print: help diff --git a/03_container_layer/docker/admin/gitea-registry/README.md b/03_container_layer/docker/admin/gitea-registry/README.md new file mode 100644 index 0000000..0ade316 --- /dev/null +++ b/03_container_layer/docker/admin/gitea-registry/README.md @@ -0,0 +1,179 @@ +# gitea-registry + +Standalone [Gitea](https://gitea.io) instance configured as an OCI Docker registry, with automated user provisioning and personal access token generation. + +Gitea's built-in [Packages / Container Registry](https://docs.gitea.io/en-us/packages/container/) feature is enabled via `GITEA__packages__ENABLED=true`. Each provisioned user receives a `registry-token` personal access token that can be used directly with `docker login`. + +--- + +## Quick Start + +```bash +# 1. Copy and fill in secrets +cp .env.example .env +# Edit .env — replace all CHANGEME_ values + +# 2. Declare users in provisioning/users.yml + +# 3. Build and start +make build-up + +# 4. Retrieve generated tokens +make tokens +``` + +--- + +## Build & Push (Makefile targets) + +| Target | Description | +|---|---| +| `make up` | Start full stack in background | +| `make down` | Stop and remove all containers | +| `make build` | Build provisioner image | +| `make build-up` | Build then start | +| `make rebuild-up` | Full rebuild without cache then start | +| `make reprovision` | Remove stamp and re-run provisioner | +| `make logs-provisioner` | Tail provisioner logs | +| `make tokens` | Print generated registry tokens | +| `make term` | Shell into gitea-registry container | +| `make clean` | Destroy all containers, images, volumes | + +--- + +## Declaring Users + +Edit `provisioning/users.yml` before the first `make up`: + +```yaml +admins: + - username: gitea-admin + email: admin@range42.local + password: "StrongPassw0rd!" + ssh_keys: + - "ssh-ed25519 AAAA... admin@host" + +users: + - username: trainee01 + email: trainee01@range42.local + password: "TraineePass1!" + ssh_keys: [] +``` + +The provisioner is **idempotent** — running it again on an already-provisioned stack exits immediately (stamp at `/data/gitea/.provisioned`). + +To re-provision: `make reprovision` + +--- + +## SSH Key Format + +SSH keys must be valid OpenSSH public keys. Supported types: + +- `ssh-ed25519 AAAA...` +- `ssh-rsa AAAA...` +- `ecdsa-sha2-nistp256 AAAA...` + +Set `ssh_keys: []` for users who do not need SSH access. + +--- + +## Docker Registry Usage + +Gitea acts as an OCI-compatible registry at `DOMAIN:PORT`. + +### Login + +```bash +# Using a personal access token (recommended) +docker login localhost:3000 -u trainee01 -p TOKEN + +# Or use your Gitea password +docker login localhost:3000 -u trainee01 -p Trainee1234! +``` + +### Push an image + +```bash +docker tag myimage:latest localhost:3000/trainee01/myimage:latest +docker push localhost:3000/trainee01/myimage:latest +``` + +### Pull an image + +```bash +docker pull localhost:3000/trainee01/myimage:latest +``` + +### List packages via API + +```bash +curl -s http://localhost:3000/api/v1/packages/trainee01 \ + -u trainee01:TOKEN | jq . +``` + +--- + +## Token Retrieval + +Personal access tokens (`registry-token`) are generated for every user during provisioning and written to a named volume. + +```bash +# Print all tokens +make tokens + +# Or directly +docker exec gitea-registry-provisioner cat /tokens/tokens.txt +``` + +Token file format: +``` +# Generated by gitea-registry provisioner +# docker login usage: docker login DOMAIN:PORT -u USERNAME -p TOKEN +gitea-admin:abc123...sha1 +trainee01:def456...sha1 +``` + +--- + +## Environment Variables + +| Variable | Default | Description | +|---|---|---| +| `GITEA_DOMAIN` | `localhost` | Domain for Gitea server and SSH | +| `GITEA_BASE_URL` | `http://localhost:3000` | Full root URL | +| `GITEA_SECRET_KEY` | `please-change-me-in-production` | App secret key (`openssl rand -hex 32`) | +| `GITEA_INTERNAL_TOKEN` | `please-change-me-in-production` | Internal token (`gitea generate secret INTERNAL_TOKEN`) | +| `GITEA_ADMIN_USER` | `gitea-admin` | Admin username (must match `admins[0]` in users.yml) | +| `GITEA_ADMIN_PASS` | `Admin1234!` | Admin password | +| `POSTGRES_USER` | `gitea` | Database user | +| `POSTGRES_PASSWORD` | `gitea` | Database password | +| `POSTGRES_DB` | `gitea` | Database name | +| `HTTP_PORT` | `3000` | Host port mapped to Gitea HTTP | +| `SSH_PORT` | `2222` | Host port mapped to Gitea SSH | + +--- + +## Troubleshooting + +**Provisioner exits before Gitea is ready** + +The provisioner retries for up to 180 s. If Gitea takes longer (cold pull), increase `start_period` in `compose.yml` or run `make reprovision` after Gitea is healthy. + +**Token shows `ERROR`** + +The Gitea API returned an empty response. Check provisioner logs: + +```bash +make logs-provisioner +``` + +Common causes: Gitea not yet fully initialized, or the user creation step failed silently. Run `make reprovision` after verifying Gitea is up. + +**`docker login` fails with 401** + +Ensure the Packages feature is enabled. Check the Gitea admin panel at `http://localhost:3000/-/admin/self` or verify `GITEA__packages__ENABLED=true` is set in `compose.yml`. + +**Port conflict** + +Change `HTTP_PORT` or `SSH_PORT` in `.env` and run `make down && make up`. diff --git a/03_container_layer/docker/admin/gitea-registry/compose.yml b/03_container_layer/docker/admin/gitea-registry/compose.yml new file mode 100644 index 0000000..2fc20e4 --- /dev/null +++ b/03_container_layer/docker/admin/gitea-registry/compose.yml @@ -0,0 +1,93 @@ +# +# ISSUE 142 +# + +services: + + db: + image: postgres:16-alpine + container_name: gitea-registry-db + environment: + POSTGRES_USER: ${POSTGRES_USER:-gitea} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-gitea} + POSTGRES_DB: ${POSTGRES_DB:-gitea} + volumes: + - gitea-registry-db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-gitea}"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + + gitea: &main + image: gitea/gitea:latest + container_name: gitea-registry + environment: + USER_UID: "1000" + USER_GID: "1000" + GITEA__database__DB_TYPE: postgres + GITEA__database__HOST: db:5432 + GITEA__database__NAME: ${POSTGRES_DB:-gitea} + GITEA__database__USER: ${POSTGRES_USER:-gitea} + GITEA__database__PASSWD: ${POSTGRES_PASSWORD:-gitea} + GITEA__server__DOMAIN: ${GITEA_DOMAIN:-localhost} + GITEA__server__ROOT_URL: ${GITEA_BASE_URL:-http://localhost:3000} + GITEA__server__HTTP_PORT: "3000" + GITEA__server__SSH_DOMAIN: ${GITEA_DOMAIN:-localhost} + GITEA__server__START_SSH_SERVER: "true" + GITEA__server__SSH_PORT: "22" + GITEA__packages__ENABLED: "true" + GITEA__service__DISABLE_REGISTRATION: "true" + GITEA__security__INSTALL_LOCK: "true" + GITEA__security__SECRET_KEY: ${GITEA_SECRET_KEY:-please-change-me-in-production} + GITEA__security__INTERNAL_TOKEN: ${GITEA_INTERNAL_TOKEN:-please-change-me-in-production} + volumes: + - gitea-registry-data:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "${HTTP_PORT:-3000}:3000" + - "${SSH_PORT:-2222}:22" + depends_on: + db: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/v1/version"] + interval: 15s + timeout: 10s + retries: 10 + start_period: 30s + restart: unless-stopped +# +# #### #### do not remove. +# +# debug: +# <<: *main +# container_name: gitea-registry-debug +# ports: +# - "60443:443" +# command: ["sleep", "infinity"] + + provisioner: + build: + context: . + dockerfile: Dockerfile + container_name: gitea-registry-provisioner + environment: + GITEA_URL: http://gitea:3000 + GITEA_ADMIN_USER: ${GITEA_ADMIN_USER:-gitea-admin} + GITEA_ADMIN_PASS: ${GITEA_ADMIN_PASS:-Admin1234!} + USERS_FILE: /provisioning/users.yml + volumes: + - gitea-registry-data:/data + - gitea-registry-tokens:/tokens + depends_on: + gitea: + condition: service_healthy + restart: "no" + +volumes: + gitea-registry-data: + gitea-registry-db-data: + gitea-registry-tokens: diff --git a/03_container_layer/docker/admin/gitea-registry/config/app.ini b/03_container_layer/docker/admin/gitea-registry/config/app.ini new file mode 100644 index 0000000..8a90911 --- /dev/null +++ b/03_container_layer/docker/admin/gitea-registry/config/app.ini @@ -0,0 +1,27 @@ +# +# ISSUE 142 +# +# Reference minimal app.ini for Gitea Docker registry. +# NOT mounted by compose.yml — settings are applied via GITEA__ env vars. +# Mount at /data/gitea/conf/app.ini only if you need to override env-var defaults. +# + +[server] +DOMAIN = localhost +ROOT_URL = http://localhost:3000 +HTTP_PORT = 3000 +SSH_DOMAIN = localhost +START_SSH_SERVER = true +SSH_PORT = 22 + +[database] +DB_TYPE = postgres + +[packages] +ENABLED = true + +[service] +DISABLE_REGISTRATION = true + +[security] +INSTALL_LOCK = true diff --git a/03_container_layer/docker/admin/gitea-registry/provisioning/init.sh b/03_container_layer/docker/admin/gitea-registry/provisioning/init.sh new file mode 100644 index 0000000..01bcbd8 --- /dev/null +++ b/03_container_layer/docker/admin/gitea-registry/provisioning/init.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env sh +# +# ISSUE 142 +# +# Bootstrap script for the Gitea Docker registry provisioner sidecar. +# Runs once after Gitea is healthy; guarded by a stamp file for idempotency. +# +# User/SSH-key declarations come from USERS_FILE (default: /provisioning/users.yml). +# Admin users are created via the gitea CLI (direct DB access via app.ini). +# SSH keys are injected via the Gitea REST API. +# Personal access tokens are generated for each user and written to /tokens/tokens.txt. +# +set -eu + +GITEA_URL="${GITEA_URL:-http://gitea:3000}" +GITEA_ADMIN_USER="${GITEA_ADMIN_USER:-gitea-admin}" +GITEA_ADMIN_PASS="${GITEA_ADMIN_PASS:-Admin1234!}" +USERS_FILE="${USERS_FILE:-/provisioning/users.yml}" +GITEA_CONFIG="/data/gitea/conf/app.ini" +PROVISION_STAMP="/data/gitea/.provisioned" + +# ── 1. Wait for Gitea HTTP (max 180 s) ───────────────────────────────────── +echo "[init] Waiting for Gitea at ${GITEA_URL} ..." +attempts=0 +until curl -sf "${GITEA_URL}/api/v1/version" >/dev/null 2>&1; do + attempts=$((attempts + 1)) + if [ "${attempts}" -ge 60 ]; then + echo "[fatal] Gitea did not become healthy after 180 s. Aborting." + exit 1 + fi + sleep 3 +done +echo "[init] Gitea is up." + +# ── 2. Idempotency guard ──────────────────────────────────────────────────── +if [ -f "${PROVISION_STAMP}" ]; then + echo "[init] Already provisioned (stamp found at ${PROVISION_STAMP}). Exiting." + exit 0 +fi + +# ── 3. Admin users (gitea CLI — direct DB, no HTTP auth needed) ───────────── +admin_count=$(yq e '.admins | length' "${USERS_FILE}") +echo "[init] Creating ${admin_count} admin user(s) ..." + +i=0 +while [ "${i}" -lt "${admin_count}" ]; do + username=$(yq e ".admins[${i}].username" "${USERS_FILE}") + email=$(yq e ".admins[${i}].email" "${USERS_FILE}") + password=$(yq e ".admins[${i}].password" "${USERS_FILE}") + + echo "[init] + admin: ${username}" + gitea admin user create \ + --config "${GITEA_CONFIG}" \ + --admin \ + --username "${username}" \ + --password "${password}" \ + --email "${email}" \ + --must-change-password=false 2>/dev/null \ + || echo "[warn] ${username} may already exist — skipping" + + i=$((i + 1)) +done + +# ── 4. Regular users (gitea CLI) ──────────────────────────────────────────── +user_count=$(yq e '.users | length' "${USERS_FILE}") +echo "[init] Creating ${user_count} regular user(s) ..." + +i=0 +while [ "${i}" -lt "${user_count}" ]; do + username=$(yq e ".users[${i}].username" "${USERS_FILE}") + email=$(yq e ".users[${i}].email" "${USERS_FILE}") + password=$(yq e ".users[${i}].password" "${USERS_FILE}") + + echo "[init] + user: ${username}" + gitea admin user create \ + --config "${GITEA_CONFIG}" \ + --username "${username}" \ + --password "${password}" \ + --email "${email}" \ + --must-change-password=false 2>/dev/null \ + || echo "[warn] ${username} may already exist — skipping" + + i=$((i + 1)) +done + +# ── 5. SSH keys (REST API — first admin in users.yml acts as auth) ────────── +inject_keys() { + local section="${1}" + local count j k uname key_count key + + count=$(yq e ".${section} | length" "${USERS_FILE}") + j=0 + while [ "${j}" -lt "${count}" ]; do + uname=$(yq e ".${section}[${j}].username" "${USERS_FILE}") + key_count=$(yq e ".${section}[${j}].ssh_keys | length" "${USERS_FILE}") + + k=0 + while [ "${k}" -lt "${key_count}" ]; do + key=$(yq e ".${section}[${j}].ssh_keys[${k}]" "${USERS_FILE}") + echo "[init] + SSH key ${k} -> ${uname}" + # Use jq to build the JSON payload to avoid injection via crafted key strings. + payload=$(jq -n --arg k "${key}" --arg t "${uname}-key-${k}" \ + '{"key":$k,"read_only":false,"title":$t}') + curl -sf -X POST "${GITEA_URL}/api/v1/admin/users/${uname}/keys" \ + -u "${GITEA_ADMIN_USER}:${GITEA_ADMIN_PASS}" \ + -H "Content-Type: application/json" \ + -d "${payload}" \ + >/dev/null \ + || echo "[warn] SSH key ${k} for ${uname} may already exist — skipping" + k=$((k + 1)) + done + + j=$((j + 1)) + done +} + +echo "[init] Injecting SSH keys ..." +inject_keys admins +inject_keys users + +# ── 6. Registry tokens ─────────────────────────────────────────────────────── +create_tokens() { + local section="${1}" + local count j uname token_resp token_val payload + + count=$(yq e ".${section} | length" "${USERS_FILE}") + j=0 + while [ "${j}" -lt "${count}" ]; do + uname=$(yq e ".${section}[${j}].username" "${USERS_FILE}") + echo "[init] + registry token for ${uname}" + payload=$(jq -n --arg n "registry-token" '{"name":$n}') + token_resp=$(curl -sf -X POST "${GITEA_URL}/api/v1/users/${uname}/tokens" \ + -u "${GITEA_ADMIN_USER}:${GITEA_ADMIN_PASS}" \ + -H "Content-Type: application/json" \ + -d "${payload}" || echo '{}') + token_val=$(echo "${token_resp}" | jq -r '.sha1 // "ERROR"') + echo "[token] ${uname}: ${token_val}" + echo "${uname}:${token_val}" >> /tokens/tokens.txt + j=$((j + 1)) + done +} + +mkdir -p /tokens +echo "# Generated by gitea-registry provisioner" > /tokens/tokens.txt +echo "# docker login usage: docker login DOMAIN:PORT -u USERNAME -p TOKEN" >> /tokens/tokens.txt +echo "[init] Generating registry tokens ..." +create_tokens admins +create_tokens users +echo "[init] Tokens written to /tokens/tokens.txt" + +# ── 7. Mark as provisioned ────────────────────────────────────────────────── +touch "${PROVISION_STAMP}" +echo "[init] Provisioning complete." diff --git a/03_container_layer/docker/admin/gitea-registry/provisioning/users.yml b/03_container_layer/docker/admin/gitea-registry/provisioning/users.yml new file mode 100644 index 0000000..d1d1b33 --- /dev/null +++ b/03_container_layer/docker/admin/gitea-registry/provisioning/users.yml @@ -0,0 +1,33 @@ +# Declarative user provisioning for the Gitea Docker registry instance. +# Processed by provisioning/init.sh on first container start. +# +# !! CHANGE ALL PASSWORDS AND SSH KEYS BEFORE DEPLOYING !! +# Passwords here are committed in plaintext. Use strong, unique values in production. +# +# SSH public keys must be real ed25519 / rsa / ecdsa keys. +# Replace the placeholder key strings with actual public keys before deploying. +# +# To scale: add/remove entries under admins: or users: +# The provisioner is idempotent — re-running skips already-created accounts. + +admins: + - username: gitea-admin + email: admin@range42.local + password: "Admin1234!" + ssh_keys: + - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleAdminKeyReplaceMeBeforeDeployment admin@range42" + +users: + - username: trainee01 + email: trainee01@range42.local + password: "Trainee1234!" + ssh_keys: + - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleTrainee01KeyReplaceMeBeforeDeployment trainee01@range42" + - username: trainee02 + email: trainee02@range42.local + password: "Trainee1234!" + ssh_keys: [] + - username: trainee03 + email: trainee03@range42.local + password: "Trainee1234!" + ssh_keys: [] From 5a0f0859f33a49de97af24f386b4b1e4bf414081 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Mon, 11 May 2026 16:13:52 +0200 Subject: [PATCH 2/3] fix: address critical review findings in gitea-registry provisioner (#149) - Fix make tokens: use docker run against named volume (docker exec fails on stopped container) - Fix term-debug-build: guard against missing debug service - Fix yq: explicitly install mikefarah/yq v4.44.1 via wget - Fix SSH port: SSH_LISTEN_PORT=22 + SSH_PORT advertises correct host-mapped port - Fix silent CLI errors: capture stderr, distinguish already-exists from real failures - Fix make clean: scope to project containers only (no system-wide prune) - Require POSTGRES_PASSWORD to be set explicitly (no weak default) --- .../docker/admin/gitea-registry/Dockerfile | 4 +++- .../docker/admin/gitea-registry/Makefile | 12 +++------- .../docker/admin/gitea-registry/compose.yml | 7 +++--- .../admin/gitea-registry/provisioning/init.sh | 22 ++++++++++++++----- 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/03_container_layer/docker/admin/gitea-registry/Dockerfile b/03_container_layer/docker/admin/gitea-registry/Dockerfile index 07c979e..2c2bd51 100644 --- a/03_container_layer/docker/admin/gitea-registry/Dockerfile +++ b/03_container_layer/docker/admin/gitea-registry/Dockerfile @@ -8,7 +8,9 @@ # FROM golang:alpine AS builder -RUN apk add --no-cache yq jq +RUN apk add --no-cache jq && \ + wget -q https://github.com/mikefarah/yq/releases/download/v4.44.1/yq_linux_amd64 \ + -O /usr/bin/yq && chmod +x /usr/bin/yq COPY provisioning/ /provisioning/ RUN chmod +x /provisioning/init.sh diff --git a/03_container_layer/docker/admin/gitea-registry/Makefile b/03_container_layer/docker/admin/gitea-registry/Makefile index 24cbe06..757abe7 100644 --- a/03_container_layer/docker/admin/gitea-registry/Makefile +++ b/03_container_layer/docker/admin/gitea-registry/Makefile @@ -74,17 +74,12 @@ logs-provisioner: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # tokens: - docker exec $(PROVISIONER) cat /tokens/tokens.txt + docker run --rm -v gitea-registry-tokens:/tokens alpine cat /tokens/tokens.txt # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # clean: - docker system prune -a --volumes -f - -docker stop $(shell docker ps -aq) - -docker rm -f $(shell docker ps -aq) - -docker rmi -f $(shell docker images -q) - -docker volume rm -f $(shell docker volume ls -q) - -docker network prune -f + docker compose down -v --rmi all # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @@ -92,7 +87,6 @@ term: docker exec -it $(SERVICE) /bin/sh term-debug-build: - docker compose up --build -d debug - docker exec -it $(DEBUG_SERVICE) /bin/sh + @echo "Uncomment the 'debug' service in compose.yml first, then re-run." print: help diff --git a/03_container_layer/docker/admin/gitea-registry/compose.yml b/03_container_layer/docker/admin/gitea-registry/compose.yml index 2fc20e4..95a5ade 100644 --- a/03_container_layer/docker/admin/gitea-registry/compose.yml +++ b/03_container_layer/docker/admin/gitea-registry/compose.yml @@ -9,7 +9,7 @@ services: container_name: gitea-registry-db environment: POSTGRES_USER: ${POSTGRES_USER:-gitea} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-gitea} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set in .env} POSTGRES_DB: ${POSTGRES_DB:-gitea} volumes: - gitea-registry-db-data:/var/lib/postgresql/data @@ -30,13 +30,14 @@ services: GITEA__database__HOST: db:5432 GITEA__database__NAME: ${POSTGRES_DB:-gitea} GITEA__database__USER: ${POSTGRES_USER:-gitea} - GITEA__database__PASSWD: ${POSTGRES_PASSWORD:-gitea} + GITEA__database__PASSWD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set in .env} GITEA__server__DOMAIN: ${GITEA_DOMAIN:-localhost} GITEA__server__ROOT_URL: ${GITEA_BASE_URL:-http://localhost:3000} GITEA__server__HTTP_PORT: "3000" GITEA__server__SSH_DOMAIN: ${GITEA_DOMAIN:-localhost} GITEA__server__START_SSH_SERVER: "true" - GITEA__server__SSH_PORT: "22" + GITEA__server__SSH_LISTEN_PORT: "22" + GITEA__server__SSH_PORT: "${SSH_PORT:-2222}" GITEA__packages__ENABLED: "true" GITEA__service__DISABLE_REGISTRATION: "true" GITEA__security__INSTALL_LOCK: "true" diff --git a/03_container_layer/docker/admin/gitea-registry/provisioning/init.sh b/03_container_layer/docker/admin/gitea-registry/provisioning/init.sh index 01bcbd8..b69dc29 100644 --- a/03_container_layer/docker/admin/gitea-registry/provisioning/init.sh +++ b/03_container_layer/docker/admin/gitea-registry/provisioning/init.sh @@ -49,14 +49,19 @@ while [ "${i}" -lt "${admin_count}" ]; do password=$(yq e ".admins[${i}].password" "${USERS_FILE}") echo "[init] + admin: ${username}" - gitea admin user create \ + cli_out=$(gitea admin user create \ --config "${GITEA_CONFIG}" \ --admin \ --username "${username}" \ --password "${password}" \ --email "${email}" \ - --must-change-password=false 2>/dev/null \ - || echo "[warn] ${username} may already exist — skipping" + --must-change-password=false 2>&1) || { + case "${cli_out}" in + *"user already exists"*|*"name already exists"*) + echo "[warn] ${username} already exists — skipping" ;; + *) echo "[error] Failed to create ${username}: ${cli_out}"; exit 1 ;; + esac + } i=$((i + 1)) done @@ -72,13 +77,18 @@ while [ "${i}" -lt "${user_count}" ]; do password=$(yq e ".users[${i}].password" "${USERS_FILE}") echo "[init] + user: ${username}" - gitea admin user create \ + cli_out=$(gitea admin user create \ --config "${GITEA_CONFIG}" \ --username "${username}" \ --password "${password}" \ --email "${email}" \ - --must-change-password=false 2>/dev/null \ - || echo "[warn] ${username} may already exist — skipping" + --must-change-password=false 2>&1) || { + case "${cli_out}" in + *"user already exists"*|*"name already exists"*) + echo "[warn] ${username} already exists — skipping" ;; + *) echo "[error] Failed to create ${username}: ${cli_out}"; exit 1 ;; + esac + } i=$((i + 1)) done From 248e497459762dac9c6f348861f63b80686b4518 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Mon, 11 May 2026 16:51:45 +0200 Subject: [PATCH 3/3] fix: add .PHONY declarations to Makefile --- 03_container_layer/docker/admin/gitea-registry/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/03_container_layer/docker/admin/gitea-registry/Makefile b/03_container_layer/docker/admin/gitea-registry/Makefile index 757abe7..7e41bc3 100644 --- a/03_container_layer/docker/admin/gitea-registry/Makefile +++ b/03_container_layer/docker/admin/gitea-registry/Makefile @@ -6,6 +6,8 @@ SERVICE = gitea-registry PROVISIONER = gitea-registry-provisioner DEBUG_SERVICE = $(SERVICE)-debug +.PHONY: help up down stop build rebuild build-up rebuild-up reprovision logs-provisioner tokens term term-debug-build clean print + help: @echo "" @echo ""