Skip to content

feat(gitea): HTTPS support and training-ready seeding (#171)#197

Open
t0kubetsu wants to merge 18 commits into
feat/gitea-docker-stackfrom
feat/gitea-training-seeding
Open

feat(gitea): HTTPS support and training-ready seeding (#171)#197
t0kubetsu wants to merge 18 commits into
feat/gitea-docker-stackfrom
feat/gitea-training-seeding

Conversation

@t0kubetsu

Copy link
Copy Markdown
Contributor

Summary

Implements all WAVE_04 training-readiness items from issue #171 for the Gitea admin Docker stack. Builds on top of #194 (feat/gitea-docker-stack).

HTTPS / TLS

  • New cert-gen init container (reuses provisioner image): auto-generates a self-signed cert (GITEA_TLS_MODE=self-signed), validates an operator-provided cert (provided), or skips entirely (disabled, default)
  • gitea-certs named volume shared between cert-gen (write) and gitea (read-only)
  • GITEA_PROTOCOL / GITEA__server__PROTOCOL wired through compose.yml and .env.example
  • All provisioner curl calls use -k (safe on the internal Docker network)

Organisation and team seeding (provision-org.sh, new — ~240 lines)

  • Creates org GITEA_ORG_NAME (default: range42-training)
  • instructors team (owner perm) + one write-perm team per GITEA_TEAMS entry
  • Two pre-seeded repos: workshop-linux-basics, workshop-ctf-intro
    • 5 labels each: exercise, hint, solution, bug, in-progress
    • 2 milestones (Day 1, Day 2) with 3 assigned issues each
    • Feature branch + file commit + pre-opened PR (shows branch-protection workflow)
    • Branch protection on main: require 1 approving review, no direct push
    • Topics and optional webhook (GITEA_WEBHOOK_URL)
    • Added to instructors team and all per-team Gitea teams
  • Augments gitea-credentials.json with service_version, provisioned_at, org, sample_repos (backward-compatible — provision-tokens.sh reads only .baseurl and .users[])
  • Idempotency stamp: /tokens/.org-provisioned (independent of the existing /tokens/.provisioned)

Other changes

  • provision.sh: adds provision-org.sh between users and tokens steps
  • Makefile reprovision: clears both stamps
  • catalog_try_init_timeout: 90 → 120 s
  • .env.example: new vars GITEA_TLS_MODE, GITEA_PROTOCOL, GITEA_ORG_NAME, GITEA_WEBHOOK_URL

Test plan

  • make up with default .env (HTTP, no TLS) — provisioner runs all three scripts, gitea-credentials.json contains org and sample_repos fields
  • GITEA_TLS_MODE=self-signed GITEA_PROTOCOL=https make rebuild-up — cert generated, Gitea serves HTTPS, healthcheck passes, provisioner connects over HTTPS
  • GITEA_TLS_MODE=provided with operator cert mounted — provisioner validates files, continues
  • make reprovision — both stamps removed, full re-provisioning runs cleanly
  • Gitea UI: org range42-training visible, teams, two sample repos with labels/milestones/issues/PRs/branch protection
  • make keys — credentials JSON includes service_version, provisioned_at, org, sample_repos

Closes #171
Depends on #194

t0kubetsu added 18 commits June 15, 2026 12:09
Implements all WAVE_04 training-readiness checklist items for the Gitea
admin Docker stack:

TLS / HTTPS
- cert-gen init container generates a self-signed cert (GITEA_TLS_MODE=self-signed)
  or validates an operator-mounted cert (provided); exits immediately when disabled
- gitea-certs named volume shared between cert-gen (write) and gitea (read-only)
- GITEA_PROTOCOL and GITEA__server__PROTOCOL wired through compose.yml and .env.example
- All provisioning curl calls use -k to tolerate self-signed certs on the internal network

Organisation and team seeding (provision-org.sh)
- Creates org GITEA_ORG_NAME (default: range42-training)
- instructors team (owner perm) + one write-perm team per entry in GITEA_TEAMS
- Two sample repos: workshop-linux-basics and workshop-ctf-intro
  - 5 labels each (exercise, hint, solution, bug, in-progress)
  - 2 milestones (Day 1, Day 2) with assigned issues
  - Feature branch + file commit + pre-seeded PR (demonstrates review workflow)
  - Branch protection on main (require 1 approving review, no direct push)
  - Topics and optional webhook (GITEA_WEBHOOK_URL)
- Augments gitea-credentials.json with service_version, provisioned_at, org, sample_repos
- Idempotency stamp at /tokens/.org-provisioned (separate from /tokens/.provisioned)

Other changes
- provision.sh: inserts provision-org.sh between users and tokens steps
- Makefile reprovision: clears both stamps (.provisioned + .org-provisioned)
- catalog_try_init_timeout bumped 90 → 120s to cover org-provisioning API calls

Closes #171
Adds maybe_create_mirror() to provision-org.sh — mirrors a remote git
repo under the org as a private, read-only Gitea mirror (8h sync interval).
Skipped silently when GITEA_MIRROR_URL is empty.

New env vars in .env.example:
  GITEA_MIRROR_URL      — remote clone URL to mirror (empty = skip)
  GITEA_MIRROR_REPO_NAME — repo name inside the org (default: public-mirror)

The mirrored repo is automatically added to the instructors team.

Closes #171 (read-only mirror item)
Adds upload_ssh_keys() to provision-users.sh.  After every user is
created, the function scans /ssh_keys/ (bind-mounted from ./ssh_keys/ on
the host) for .pub files whose name contains _<username>_ (r42playbooks
convention: r42.<scenario>-student-key_<username>_<n>.pub) or is named
<username>.pub, then registers each one via
POST /api/v1/admin/users/{username}/keys.

compose.yml: adds ./ssh_keys:/ssh_keys:ro mount to the provisioner
service (Docker silently skips the mount when the host dir is absent,
so the default no-key-seeding path is unchanged).

.env.example: documents expected key-file layout and seeding workflow.

Closes the "End-to-end SSH cycle" item tracked in ISSUE 171.
… key pattern

Two bugs in upload_ssh_keys():
- Search root was the entire ssh_keys/ directory, picking up deployer
  and jump keys (backend_keys/, jump_keys/) that must never be uploaded
  to Gitea.  Now prefers ssh_keys/student_keys/ when present.
- Pattern "${username}.pub" matched only a bare filename; actual
  r42playbooks naming is *-student-key_<user>.pub.  Changed to
  "*_${username}.pub" to match the un-numbered key alongside the
  numbered variants (*_${username}_*.pub).

Depth of additional.students/ is 2 from student_keys/, well within
the existing -maxdepth 3.
Adds upload_backend_keys(username) which scans ssh_keys/backend_keys/
for all .pub files (maxdepth 1) and registers each via
POST /api/v1/admin/users/{username}/keys.

Called immediately after admin creation so the range42 backend can
clone repos over SSH using the same deployer key (alice) already
deployed to the VM.
Bash requires a function to be defined before it is called.  The call
was placed before the function body, causing 'command not found' at
runtime.  Moved the call to after upload_backend_keys() is defined,
immediately before section 6 (instructors).
Two bugs:
- cert-gen ran as git (uid 1000) and couldn't write to /certs/ volume;
  add `user: root` to compose.yml cert-gen service
- mktemp template had a .cnf suffix which BusyBox mktemp rejects;
  drop the extension (OpenSSL accepts any filename as config)
- gen_password: replace printf|head pipe (SIGPIPE under pipefail) with
  printf '%.16s' format-string truncation — no subprocess, no signal
- create_user: separate jq into its own variable to prevent set -e
  triggering silently before the || guard; capture HTTP status code
  explicitly so API errors are visible instead of swallowed
Replaces GITEA_MIRROR_URL + GITEA_MIRROR_REPO_NAME with GITEA_MIRRORS,
a semicolon-separated list of "url|repo-name" pairs, allowing an
arbitrary number of read-only mirrors to be seeded under the org.
Add --max-time 30 to prevent indefinite hang when Gitea is slow to
respond on the token endpoint. Drop -f and guard with || true so an
HTTP error falls through to the existing warn path instead of killing
the script via set -e.
…llowing it

Replace blind >/dev/null 2>&1 with HTTP status capture so mirror
failures print the real Gitea error message. Also adds --max-time 120
since the migrate endpoint triggers a server-side clone.
Drop the workshop-linux-basics and workshop-ctf-intro seeding blocks.
Repos are now seeded exclusively via GITEA_MIRRORS. Cleans up
credentials JSON and script header accordingly.
…>=1.21

Mirror: split 409 (exists) from 422 (unreachable/invalid) and show the
actual Gitea error message instead of a misleading 'already exists' warn.

Tokens: check .token field first (.sha1 is deprecated since Gitea 1.21);
print actual API error message when token creation fails.
…Gitea ≥1.21

- provision-org.sh: rename clone_url → clone_addr in migrate request body
  (Gitea API schema uses clone_addr; clone_url caused HTTP 422 [CloneAddr]: Required)
- provision-tokens.sh: add scopes array to token creation request
  (Gitea ≥1.22 rejects tokens with no scopes; covers repo, issue, user, org read/write)
- Add GITEA__oauth2__JWT_SECRET env var to enable id_token signing for
  full OIDC compliance; without it the discovery endpoint exists but
  id_token is absent (OAuth2-only mode)
- Add provision-oauth2.sh: idempotent script to register OAuth2 apps
  (Mattermost, Rocket.Chat, Nextcloud, …) via the Gitea REST API;
  appends client_id + client_secret to gitea-credentials.json
- Add service.configure.gitea_oauth2_app Ansible role: registers an
  app via uri module, outputs gitea_oauth2_client_id / _client_secret
  facts for use by consumer service roles
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant