Gitea Anubis protection
This repository deploys Anubis 1.27.0 in front of selected public Gitea paths
on code.brunner.ninja. It uses a pinned container image, a Traefik
ForwardAuth middleware, restricted Gitea Actions deployment credentials, and
an out-of-repository signing key.
Protected routes
Anubis is used for:
- repository and API paths belonging to the current third-party mirror owners;
- the complete
EFeruowner subtree, including the owner page itself; - comparison pages under every owner, including
feedc0de.
The existing catch-all Gitea route remains unprotected, so login, about,
explore, owner pages other than EFeru, and every feedc0de/* repository
remain unchanged outside their comparison pages.
Proof-of-work difficulty is 6 and authorization cookies expire after one hour. Each additional difficulty level multiplies expected work by 16. Solve time is random and depends on the client's CPU and browser.
Repository contents
anubis.yaml: policy ConfigMap, Deployment, Service, Middleware, and IngressRoute.install.sh: signing-key creation, server-side dry-run, apply, and rollout.robots.txt: the matching Gitea crawler policy installed on arschrock.test.sh: local shell and Kubernetes schema validation.ci-deployer.yaml: restricted Gitea Actions identity and revocable token.create-ci-kubeconfig.sh: one-time CI credential bootstrap..gitea/workflows/deploy.yml: validation for pushes and pull requests, plus deployment after successful validation ofmain.
Install manually
With an administrator kubeconfig active, run:
./install.sh
The installer creates default/gitea-anubis-key only if it is missing. That
Secret contains Anubis's Ed25519 signing key and is deliberately neither stored
in Git nor accessible to the CI identity. The script then validates and applies
anubis.yaml, restarts Anubis so policy-only changes take effect, and waits up
to five minutes for the rollout.
Validate locally
./test.sh
This checks both shell scripts and validates the Kubernetes resources with kubeconform. Traefik resources are skipped by schema validation because the standard catalog does not contain the cluster-specific CRDs; the Kubernetes API validates them during the deployment's server-side dry-run.
Automatic deployment with Gitea Actions
The workflow validates every push and pull request. A successful push to
main applies anubis.yaml and waits up to five minutes for Anubis to become
ready. CI substitutes the Git commit SHA into the pod-template annotation, so a
ConfigMap-only policy edit starts a new pod instead of leaving the old policy in
memory.
The workflow requires one Gitea Actions repository secret:
KUBE_CONFIG_BASE64: a base64-encoded, restricted Anubis deployment kubeconfig.
One-time CI setup
-
Run
./install.shwith an administrator identity so all application resources already exist. -
Run:
./create-ci-kubeconfig.shIf kubectl is available only through arschrock, use remote mode:
KUBECTL_SSH_HOST=arschrock ./create-ci-kubeconfig.sh -
Copy only the final base64 line into the Gitea repository Actions secret named
KUBE_CONFIG_BASE64. -
Protect
main, require successful validation before merging, and review manifest and workflow changes. -
Push a harmless README change and confirm that the deployment job succeeds.
The workflow matches the existing brunner.ninja runner: Kubernetes API
https://host.containers.internal:6443, with TLS server name 192.168.0.2.
CI can get, patch, and update only these existing named objects in default:
- ConfigMap
gitea-anubis-policy; - Deployment and Service
gitea-anubis; - Middleware
gitea-anubis; - IngressRoute
code-brunner-ninja-anubis.
It cannot create or delete resources, cannot access the Kubernetes Secrets API,
and has no permission over legacy-gitea-service. Revoke CI access by deleting
default/gitea-anubis-deployer-token and the Gitea repository secret. Rerun
create-ci-kubeconfig.sh to issue a replacement token.
Kubernetes RBAC cannot constrain individual fields inside an allowed Deployment
patch. A malicious change merged into main could alter the pod specification,
so branch protection and review remain part of the security boundary.
Gitea robots.txt
Install robots.txt as /var/lib/gitea/custom/public/robots.txt on arschrock,
owned by gitea:gitea, then restart Gitea. Gitea requires a restart to load
new custom public files. This host-level file is documented in the repository
but is not changed by CI.
Scope maintenance
The mirror-owner list was generated from Gitea's public repository API on
2026-08-31. Add newly created third-party mirror owners to both robots.txt
and the two owner expressions in anubis.yaml. Comparison pages are matched
generically and need no owner-list maintenance.