git http-backend now gets every clone path, smart and dumb (HEAD, info/refs, objects, packs), without GIT_HTTP_EXPORT_ALL, so it serves only repos with git-daemon-export-ok and a private repo is the 404 a missing one is. Nothing else inside a bare repo is served any more: the raw file server used to hand out forge/ (review logs, comments, collaborators), config and hooks to anyone who asked. Without fcgiwrap there are no HTTPS clones; the container and provision.sh install it.
- commit
- cc80fa3fa9b28afe1bacda9a7f2d7b7b9245f091
- parent
- 70f465d24fa0c76cc3b5f12a474b4066ba130744
- author
- cat <admin@dev.site>
- kind
- fix risk score 2.3 of 3, Jev's estimate
| M | bin/ | 20 | ++++++----- |
| M | server/ | 22 | +++++++++--- |
| M | server/ | 4 | +- |
| A | test/ | 55 | ++++++++++++++++++++++++++++++ |
@@ -857,18 +857,21 @@ $addr {
857857 respond "<!DOCTYPE html><meta name=\"go-import\" content=\"$gohost/{re.goget.1}/{re.goget.2} git https://$gohost/{re.goget.1}/{re.goget.2}.git\"><p>go get $gohost/{re.goget.1}/{re.goget.2}</p>" 200858858 }859859 EOF860- # Smart HTTP for clones and fetches (git http-backend through fcgiwrap):860+ # Clones and fetches go to git http-backend (through fcgiwrap): smart HTTP861861 # packs only what the client needs and supports shallow fetches, which the862- # go command uses. Read-only: only upload-pack is routed; pushes are ssh.862+ # go command uses, and it answers the dumb protocol's paths too (HEAD,863+ # info/refs, objects). It serves only repos with git-daemon-export-ok, so864+ # a private repo is a 404, the same as a missing one. Read-only: only865+ # upload-pack is routed; pushes are ssh. Nothing else inside a bare repo866+ # is served (no forge/, config or hooks).863867 if command -v fcgiwrap >/dev/null 2>&1; then864868 cat <<EOF865- @smart path_regexp smart ^/[A-Za-z0-9_-][^/]*/[^/]+\\.git/(info/refs|git-upload-pack)$866- handle @smart {869+ @git path_regexp git ^/[A-Za-z0-9_-][^/]*/[^/]+\\.git/(HEAD|info/refs|objects/info/(alternates|http-alternates|packs)|objects/[0-9a-f]{2}/([0-9a-f]{38}|[0-9a-f]{62})|objects/pack/pack-([0-9a-f]{40}|[0-9a-f]{64})\\.(pack|idx)|git-upload-pack)$870+ handle @git {867871 reverse_proxy unix//run/fcgiwrap.socket {868872 transport fastcgi {869873 env SCRIPT_FILENAME /usr/lib/git-core/git-http-backend870874 env GIT_PROJECT_ROOT $FORGE_ROOT871- env GIT_HTTP_EXPORT_ALL 1872875 env PATH_INFO {path}873876 }874877 }@@ -876,10 +879,9 @@ EOF
876879 EOF877880 fi878881 cat <<EOF879- @repo path_regexp repo ^/[A-Za-z0-9_-][^/]*/[^/]+\\.git(/.*)?$880- handle @repo {881- root * $FORGE_ROOT882- file_server882+ @bare path_regexp bare ^/[A-Za-z0-9_-][^/]*/[^/]+\\.git(/.*)?$883+ handle @bare {884+ respond 404883885 }884886 EOF885887 # Old site paths redirect to each site's hostname (lib/pages-server.ts).@@ -1,9 +1,21 @@
1-# Pages, plus dumb-HTTP clones: git clone https://git.example.com/dami/greet.git1+# Pages, plus clones: git clone https://git.example.com/dami/greet.git. Git's2+# paths, smart and dumb, go to git http-backend, which serves only repos with3+# git-daemon-export-ok (not private ones); nothing else inside a bare repo is4+# served. forge server caddyfile prints the full config.25 git.example.com {3- @repo path_regexp repo ^/[A-Za-z0-9_-][^/]*/[^/]+\.git(/.*)?$4- handle @repo {5- root * /srv/git6- file_server6+ @git path_regexp git ^/[A-Za-z0-9_-][^/]*/[^/]+\.git/(HEAD|info/refs|objects/info/(alternates|http-alternates|packs)|objects/[0-9a-f]{2}/([0-9a-f]{38}|[0-9a-f]{62})|objects/pack/pack-([0-9a-f]{40}|[0-9a-f]{64})\.(pack|idx)|git-upload-pack)$7+ handle @git {8+ reverse_proxy unix//run/fcgiwrap.socket {9+ transport fastcgi {10+ env SCRIPT_FILENAME /usr/lib/git-core/git-http-backend11+ env GIT_PROJECT_ROOT /srv/git12+ env PATH_INFO {path}13+ }14+ }15+ }16+ @bare path_regexp bare ^/[A-Za-z0-9_-][^/]*/[^/]+\.git(/.*)?$17+ handle @bare {18+ respond 404719 }820 # Sites are served from their own origin below, away from the forge.921 @sites path /sites/* /previews/*@@ -32,9 +32,9 @@ id git >/dev/null 2>&1 || adduser --system --group --shell /bin/sh --home "$root
3232 mkdir -p "$root"3333 chown git:git "$root"3434 chmod 750 "$root"35-# Caddy reads pages, site content, and bare repos (for HTTPS clones) through the git group.35+# Caddy reads pages and site content through the git group.3636 usermod -aG git caddy37-# Debian's fcgiwrap (smart HTTP) runs as www-data and needs to read the repos.37+# Debian's fcgiwrap (git http-backend, for HTTPS clones) runs as www-data and needs to read the repos.3838 usermod -aG git www-data3939 4040 (cd "$app" && npm ci --omit=dev --no-audit --no-fund --silent)@@ -0,0 +1,55 @@
1+# HTTPS clones: Caddy hands git's paths (smart and dumb) to git2+# http-backend, which serves only repos with git-daemon-export-ok, and3+# serves nothing else from inside a bare repo. A private repo gets the 4044+# a missing one gets.5+. "$(dirname "$0")/../lib.sh"6+user dami7+repo --private dami/secret8+repo dami/open9+for r in secret open; do10+ clone dami "dami/$r" "$T/w/$r"11+ at "$T/w/$r" commit "first"12+ ok push_as dami "$T/w/$r" origin main13+done14+15+# The Caddyfile, as on a host with fcgiwrap.16+mkdir "$T/bin"17+printf '#!/bin/sh\n' >"$T/bin/fcgiwrap"18+chmod +x "$T/bin/fcgiwrap"19+PATH="$T/bin:$PATH" forge server caddyfile git.test >"$T/Caddyfile" 2>"$T_LOG" || fail "caddyfile"20+lacks "$T/Caddyfile" 'GIT_HTTP_EXPORT_ALL'21+lacks "$T/Caddyfile" "root \\* $FORGE_ROOT\$"22+has "$T/Caddyfile" 'SCRIPT_FILENAME .*/git-http-backend$'23+has "$T/Caddyfile" 'respond 404'24+eq "$(grep -c 'file_server' "$T/Caddyfile")" 125+if command -v caddy >/dev/null 2>&1; then ok caddy adapt --config "$T/Caddyfile" --adapter caddyfile; fi26+re=$(sed -n 's/^ @git path_regexp git //p' "$T/Caddyfile")27+[ -n "$re" ] || fail "no @git route"28+h40=0123456789abcdef0123456789abcdef0123456729+for p in info/refs git-upload-pack HEAD objects/info/packs "objects/01/${h40#01}" "objects/pack/pack-$h40.pack" "objects/pack/pack-$h40.idx"; do30+ printf '/dami/open.git/%s\n' "$p" | grep -Eq "$re" || fail "@git doesn't route $p"31+done32+for p in config description forge/reviews/1 forge/readers hooks/pre-receive git-receive-pack objects/info/commit-graphs/x refs/heads/main packed-refs; do33+ if printf '/dami/open.git/%s\n' "$p" | grep -Eq "$re"; then fail "@git routes $p"; fi34+done35+36+# git http-backend itself, as fcgiwrap runs it.37+cgi() {38+ env -u GIT_HTTP_EXPORT_ALL GIT_PROJECT_ROOT="$FORGE_ROOT" PATH_INFO="$1" REQUEST_METHOD=GET QUERY_STRING="${2:-}" \39+ "$(git --exec-path)/git-http-backend" </dev/null >"$T_LOG" 2>/dev/null || true40+}41+cgi /dami/open.git/info/refs service=git-upload-pack42+has "$T_LOG" 'application/x-git-upload-pack-advertisement'43+has "$T_LOG" 'refs/heads/main'44+cgi /dami/secret.git/info/refs service=git-upload-pack45+has "$T_LOG" '^Status: 404'46+lacks "$T_LOG" 'refs/heads'47+denied=$(output)48+cgi /dami/nope.git/info/refs service=git-upload-pack49+eq "$denied" "$(output)"50+cgi /dami/open.git/HEAD51+has "$T_LOG" 'ref: refs/heads/main'52+cgi /dami/secret.git/HEAD53+has "$T_LOG" '^Status: 404'54+cgi /dami/secret.git/info/refs55+has "$T_LOG" '^Status: 404'