diff --git a/tools/gitea-disable-package-push.sh b/tools/gitea-disable-package-push.sh new file mode 100755 index 0000000..0baa979 --- /dev/null +++ b/tools/gitea-disable-package-push.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# Disable package pushes on coulombcore Gitea while keeping pull/index (read-only mirror). +# 1) Traefik IngressRoute: only GET/HEAD/OPTIONS on /v2 and /api/packages +# 2) Remove those paths from the catch-all gitea Ingress (so POST does not fall through) +# 3) Gitea app.ini: zero upload limits as defense-in-depth +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +KUBECONFIG="${KUBECONFIG:-$HOME/.kube/config}" +NS="${GITEA_NAMESPACE:-default}" +DRY_RUN="${DRY_RUN:-0}" + +run() { + if [[ "$DRY_RUN" == "1" ]]; then + echo "DRY $*" + else + "$@" + fi +} + +echo "=== Gitea package push disable (namespace=${NS}) ===" + +echo "==> Apply read-only IngressRoute for /v2 and /api/packages" +if [[ "$DRY_RUN" == "1" ]]; then + echo "DRY kubectl apply -f ${ROOT}/tools/gitea-package-readonly-ingressroute.yaml" +else + kubectl apply -f "${ROOT}/tools/gitea-package-readonly-ingressroute.yaml" +fi + +echo "==> Patch gitea Ingress: drop /v2 and /api/packages (handled by IngressRoute)" +PATCH='[ + {"op":"replace","path":"/spec/rules/0/http/paths","value":[ + {"path":"/","pathType":"Prefix","backend":{"service":{"name":"gitea-http","port":{"number":3000}}}} + ]} +]' +if [[ "$DRY_RUN" == "1" ]]; then + echo "DRY kubectl patch ingress gitea -n ${NS} --type=json -p '${PATCH}'" +else + kubectl patch ingress gitea -n "$NS" --type=json -p "$PATCH" +fi + +echo "==> Set Gitea package upload limits to 0 (defense-in-depth)" +APP_INI_BLOCK=' +[packages] +ENABLED = true +LIMIT_TOTAL_OWNER_SIZE = 0 +LIMIT_SIZE_CONTAINER = 0 +LIMIT_SIZE_PYPI = 0 +LIMIT_SIZE_NPM = 0 +LIMIT_SIZE_GENERIC = 0 +LIMIT_SIZE_HELM = 0 +LIMIT_SIZE_MAVEN = 0 +LIMIT_SIZE_NUGET = 0 +LIMIT_SIZE_RPM = 0 +LIMIT_SIZE_DEB = 0 +LIMIT_SIZE_CONAN = 0 +LIMIT_SIZE_COMPOSER = 0 +LIMIT_SIZE_CRAN = 0 +LIMIT_SIZE_ALPINE = 0 +LIMIT_SIZE_CARGO = 0 +LIMIT_SIZE_CHEF = 0 +' +if [[ "$DRY_RUN" == "1" ]]; then + echo "DRY append [packages] zero limits to app.ini + restart gitea" +else + kubectl -n "$NS" exec deploy/gitea -- sh -c ' + ini=/data/gitea/conf/app.ini + if grep -q "^\[packages\]" "$ini"; then + echo "packages section already present — leaving app.ini unchanged" + else + cat >>"$ini" <<'"'"'EOF'"'"' +[packages] +ENABLED = true +LIMIT_TOTAL_OWNER_SIZE = 0 +LIMIT_SIZE_CONTAINER = 0 +LIMIT_SIZE_PYPI = 0 +LIMIT_SIZE_NPM = 0 +LIMIT_SIZE_GENERIC = 0 +LIMIT_SIZE_HELM = 0 +LIMIT_SIZE_MAVEN = 0 +LIMIT_SIZE_NUGET = 0 +LIMIT_SIZE_RPM = 0 +LIMIT_SIZE_DEB = 0 +LIMIT_SIZE_CONAN = 0 +LIMIT_SIZE_COMPOSER = 0 +LIMIT_SIZE_CRAN = 0 +LIMIT_SIZE_ALPINE = 0 +LIMIT_SIZE_CARGO = 0 +LIMIT_SIZE_CHEF = 0 +EOF + echo "appended [packages] zero-limit block" + fi + ' + kubectl -n "$NS" rollout restart deploy/gitea + kubectl -n "$NS" rollout status deploy/gitea --timeout=180s +fi + +echo "==> Verify edge behavior" +if [[ "$DRY_RUN" == "1" ]]; then + echo "DRY curl -X POST https://gitea.coulomb.social/v2/ (expect 404)" +else + sleep 3 + post_code=$(curl -sS --max-time 15 -o /dev/null -w '%{http_code}' -X POST "https://gitea.coulomb.social/v2/" || true) + get_code=$(curl -sS --max-time 15 -o /dev/null -w '%{http_code}' "https://gitea.coulomb.social/v2/" || true) + echo "POST /v2/ -> ${post_code} (want 404/405)" + echo "GET /v2/ -> ${get_code} (want 401/200)" +fi + +echo "=== Done ===" \ No newline at end of file diff --git a/tools/gitea-package-readonly-ingressroute.yaml b/tools/gitea-package-readonly-ingressroute.yaml new file mode 100644 index 0000000..553cf65 --- /dev/null +++ b/tools/gitea-package-readonly-ingressroute.yaml @@ -0,0 +1,24 @@ +# Read-only package registry paths on coulombcore Gitea. +# Allows GET/HEAD/OPTIONS (pull/index); blocks POST/PUT/PATCH/DELETE at the edge. +# Apply together with tools/gitea-disable-package-push.sh (patches main Ingress). +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: gitea-packages-readonly + namespace: default + labels: + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + app.kubernetes.io/part-of: railiance-forge + railiance/component: gitea-package-readonly +spec: + entryPoints: + - websecure + routes: + - match: Host(`gitea.coulomb.social`) && (PathPrefix(`/v2`) || PathPrefix(`/api/packages`)) && (Method(`GET`) || Method(`HEAD`) || Method(`OPTIONS`)) + kind: Rule + services: + - name: gitea-http + port: 3000 + tls: + secretName: gitea-tls \ No newline at end of file