Refactor Authentik blueprints into common.yaml (shared admins group), grafana.yaml (updated with !Find and groups scope), and forgejo.yaml (new provider + application). Add forgejo-client-secret to ExternalSecret and worker deployment. Configure Forgejo oauth2_client for auto-registration with login-based account linking to safely preserve existing accounts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.4 KiB
| title | modified | tags | |||
|---|---|---|---|---|---|
| Forgejo | 2026-02-20 |
|
Forgejo
Git forge and CI/CD platform. Primary source of truth for blumeops (mirrored to GitHub).
Quick Reference
| Property | Value |
|---|---|
| URL | https://forge.ops.eblu.me |
| SSH | ssh://forgejo@forge.ops.eblu.me:2222 |
| Local Ports | 3001 (HTTP), 2200 (SSH) |
| Config | ansible/roles/forgejo/templates/app.ini.j2 |
Repositories
| Repo | Description |
|---|---|
eblume/blumeops |
Infrastructure as code (primary) |
eblume/alloy |
Grafana Alloy fork (CGO build) |
eblume/tesla_auth |
Tesla OAuth helper |
| Helm chart mirrors | cloudnative-pg-charts, grafana-helm-charts |
CI/CD (Forgejo Actions)
Runners:
| Runner | Host | Labels | Purpose |
|---|---|---|---|
| k8s DinD pod | indri (minikube) | k8s |
Dockerfile builds via Dagger |
| ringtail-nix-builder | ringtail (native) | nix-container-builder |
Nix builds via nix-build + skopeo |
Workflows: .forgejo/workflows/
build-container.yaml- Dockerfile builds on tag (runs onk8s)build-container-nix.yaml- Nix builds on tag (runs onnix-container-builder)build-blumeops.yaml- Documentation builds and releases
Both container workflows trigger on the same tag pattern (*-v[0-9]*). Each checks for its build file (Dockerfile or default.nix) and skips if not present. See build-container-image.
Secrets (Forgejo Config)
Server configuration secrets managed via 1Password → Ansible:
lfs-jwt-secret,internal-token,oauth2-jwt-secret- Forgejo server tokensrunner_reg- Runner registration token (also in k8s via external-secrets)
Forgejo Actions Secrets
Repository-level secrets for CI/CD workflows, synced from 1Password via Ansible.
| Secret | 1Password Field | Used By | Purpose |
|---|---|---|---|
ARGOCD_AUTH_TOKEN |
argocd_token |
build-blumeops.yaml |
Sync docs app after release |
These secrets are injected as ${{ secrets.SECRET_NAME }} in workflow files.
IaC: The forgejo_actions_secrets Ansible role syncs these secrets from 1Password to Forgejo via the Forgejo API. Run with:
mise run provision-indri -- --tags forgejo_actions_secrets
API Token Setup (Manual, One-Time)
The Ansible role authenticates to the Forgejo API using a Personal Access Token (PAT). This PAT must be created manually:
- Go to https://forge.ops.eblu.me/user/settings/applications
- Create a new token with
write:repositoryscope - Store it in 1Password → "Forgejo Secrets" item →
api-tokenfield
This is a bootstrapping requirement - the PAT enables IaC for all other secrets.
Identity Provider
authentik is the BlumeOps OIDC identity provider and source of truth for user identity. Forgejo authenticates against Authentik as an OIDC client.
Configuration:
- OAuth2 provider and application defined in Authentik blueprints (
argocd/manifests/authentik/configmap-blueprint.yaml) - Auth source created via
forgejo admin auth add-oauthwith--skip-local-2fa(lives in Forgejo's SQLite database, not app.ini) [oauth2_client]section inapp.ini.j2controls auto-registration and account linking behavior
MFA: SSO logins skip Forgejo's local 2FA (--skip-local-2fa on the auth source) — Authentik enforces MFA instead. Local password logins still require Forgejo's own TOTP. Note: the --skip-local-2fa CLI flag has a known bug where it doesn't persist via update-oauth; it was set directly in the login_source.cfg JSON (SkipLocalTwoFA: true).
Account linking: ACCOUNT_LINKING = login — when an Authentik user's email matches an existing local account, Forgejo prompts for the local password (and local MFA) to confirm the link. This is a one-time operation that preserves existing accounts, API tokens, SSH keys, and repository ownership.
Group-based admin: The admins group in Authentik maps to Forgejo admin status via --admin-group admins on the auth source. Manage admin access in Authentik, not Forgejo.
Break-glass: Local password login always works (with local MFA). Authentik SSO is additive — if Authentik is down, log in with local credentials.
Future: Public Access
Forgejo can be exposed publicly at forge.eblu.me via flyio-proxy. Since Forgejo runs natively on indri (not in k8s), the pattern is:
- Create a k8s ExternalName Service pointing to indri's Tailscale IP
- Create a Tailscale Ingress with
tailscale.com/tags: "tag:k8s,tag:flyio-target" - Add the nginx server block and DNS CNAME
Exposing a dynamic, authenticated service like Forgejo requires a full security review before going live:
- Disable all local registration — only allow login via authentik (
DISABLE_REGISTRATION = true,ALLOW_ONLY_EXTERNAL_REGISTRATION = true) - Configure fail2ban on indri with a filter for Forgejo's log format
- Ensure Forgejo logs the forwarded client IP (
X-Real-IP) rather than the proxy's Tailscale IP - Audit repository visibility defaults and permissions
- Rehearse the break-glass shutoff (
mise run fly-shutoff)
See expose-service-publicly for the full howto and dynamic service checklist.