feedc0de a8128e2a81
Validate and deploy Anubis / Validate manifests (push) Successful in 11s
Validate and deploy Anubis / Deploy to Kubernetes (push) Successful in 33s
Bypass LAN addresses from anubis check
2026-10-14 20:15:41 +02:00
2026-09-04 10:36:18 +02:00
2026-10-13 18:16:12 +02:00

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.

LAN and CI bypass

Traefik sends clients in 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, and fc00::/7 directly to legacy-gitea-service, without Anubis middleware. This priority-500 route covers every path, including Git HTTP, action downloads, API requests and comparisons. The homelab Gitea runner uses this bypass too.

The separate priority-400 route sends internet requests for /.within.website to Anubis. Do not combine the LAN match with that route: doing so sends LAN requests directly to Anubis instead of bypassing it.

ClientIP matches the connection peer, not X-Forwarded-For or X-Real-IP. Traefik's LoadBalancer must retain externalTrafficPolicy: Local to preserve internet source addresses. The homelab's NAT loopback currently presents LAN requests as 192.168.0.254; internet clients retain their public address. If a proxy or NAT configuration changes, repeat both checks below before assuming this trust boundary still holds.

From outside the homelab, verify the redirect to Anubis and then direct LAN access (HTTP 200 with Gitea HTML, no Anubis challenge):

curl -v https://code.brunner.ninja/papnkukn/qrcode-svg
ssh dahoam curl -v https://code.brunner.ninja/papnkukn/qrcode-svg
ssh dahoam git ls-remote https://code.brunner.ninja/actions/checkout.git refs/tags/v4

Protected internet routes

Anubis is used for:

  • repository and API paths belonging to the current third-party mirror owners;
  • selected oversized public feedc0de forks (at least 512 MiB);
  • comparison pages under every owner, including feedc0de.

The existing catch-all Gitea route remains unprotected, so login, about, explore, owner pages, and ordinary feedc0de/* repositories remain unchanged outside their comparison pages.

Proof-of-work difficulty is 5 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 of main.

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

  1. Run ./install.sh with an administrator identity so all application resources already exist.

  2. Run:

    ./create-ci-kubeconfig.sh
    

    If kubectl is available only through arschrock, use remote mode:

    KUBECTL_SSH_HOST=arschrock ./create-ci-kubeconfig.sh
    
  3. Copy only the final base64 line into the Gitea repository Actions secret named KUBE_CONFIG_BASE64.

  4. Protect main, require successful validation before merging, and review manifest and workflow changes.

  5. 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. Public feedc0de forks at or above the documented size threshold must be added to both their specific route expressions and robots.txt. Comparison pages are matched generically and need no owner-list maintenance.

S
Description
Anubis is used in my cluster to protect cpu-intensive webpages like git compares on gitea, to keep out nasty AI and crawler bots
Readme GPL-3.0
85 KiB
Languages
Shell 100%