Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/ops
diff options
context:
space:
mode:
authorLeitwolf112026-11-13 01:57:15 +0300
committerLeitwolf112026-11-13 01:57:15 +0300
commit05d42b1a4ca923ee7726c35c261bfc75d32ddea3 (patch)
treeb7f2ddce7130aca707d9b9a5d9844078230812e5 /ops
parent83e0ad4586d90bf02c3a5bfeb1ab92fd90e51703 (diff)
downloadprogmaweb-ops/git-mirrors.tar.gz
Serve only listed mirrors and report a failed default-branch lookupops/git-mirrors
- git-http-backend no longer exports every directory under the mirror root: GIT_HTTP_EXPORT_ALL is gone, git-mirror marks each listed mirror with git-daemon-export-ok, and a mirror taken off the list loses the mark and is deleted on the next run. Tried live: an unlisted bare repository answered 404 and was removed by the next run. - git-mirror checks git ls-remote before it reads the default branch and fails the run when the lookup fails. - The README explains the /usr/bin spelling of the fcgiwrap label (an equivalence of /usr/sbin), the stock label of /srv/*/www, and where the DNS record is kept.
Diffstat (limited to 'ops')
-rw-r--r--ops/README.md10
-rw-r--r--ops/git/git-mirror.sh26
-rw-r--r--ops/git/nginx/git.conf2
3 files changed, 32 insertions, 6 deletions
diff --git a/ops/README.md b/ops/README.md
index 9022227..285b2ba 100644
--- a/ops/README.md
+++ b/ops/README.md
@@ -320,7 +320,9 @@ Packages: `git-core`, and `cgit` and `fcgiwrap` from EPEL; `checkpolicy` builds
- **Mirrors.** `git-mirror` fetches only branches and tags from `https://github.com/Progmasoft/<name>.git` (never the
pull-request refs), prunes what GitHub removed, follows its default branch, and repacks into one pack with a bitmap
when something changed, so a clone starts sending at once. It needs no credentials. A new repository needs a line in
- `repositories` and a `repo.url` block in `cgitrc`.
+ `repositories` and a `repo.url` block in `cgitrc`. git-http-backend serves only the mirrors `git-mirror` marks with
+ `git-daemon-export-ok`; a mirror taken off `repositories` loses the mark and is deleted on the next run, so a
+ repository that becomes private or archived stops being cloned within ten minutes.
- **Read only.** Nginx answers 403 to `git-receive-pack` and to the dumb protocol; git-http-backend gets no user; the
mirrors belong to `gitmirror` and are read as `cgit`, trusted through `safe.directory` for that backend only.
- **SELinux.** `/srv/git/mirrors` is `git_sys_content_t` and `/var/cache/cgit` `git_rw_content_t` (package default).
@@ -331,14 +333,18 @@ Packages: `git-core`, and `cgit` and `fcgiwrap` from EPEL; `checkpolicy` builds
```sh
semanage fcontext -a -t git_sys_content_t '/srv/git/mirrors(/.*)?'
semanage fcontext -a -t bin_t /usr/local/libexec/git-mirror
+ # /usr/sbin is an equivalence of /usr/bin, so semanage takes the /usr/bin spelling; restorecon on
+ # /usr/sbin/fcgiwrap then gives it httpd_exec_t (check with ls -Z).
semanage fcontext -a -t httpd_exec_t /usr/bin/fcgiwrap
semanage fcontext -a -t git_script_exec_t /usr/libexec/git-core/git-http-backend
restorecon -RF /srv/git /usr/local/libexec/git-mirror /usr/sbin/fcgiwrap /usr/libexec/git-core/git-http-backend
```
+ `/srv/git/www/static` needs no rule of its own: the stock policy labels `/srv/*/www` `httpd_sys_content_t`.
- **No Cloudflare proxy.** `git.progmasoft.com` is a DNS-only A record: through the proxy, a clone of a large repository
(catch3, about 25 MB) never reached the client, while the origin served it in under a minute. The address of the server
- is public through `mail.progmasoft.com` anyway; Nginx limits requests and connections per address itself.
+ is public through `mail.progmasoft.com` anyway; Nginx limits requests and connections per address itself. The record
+ is in `cloudflarectl/config.progmasoft.json`.
- **Look.** No README is rendered (cgit would include its HTML unfiltered) and no script runs: the content security
policy allows none, and Nginx removes the inline `onchange` handlers of cgit's menus, whose buttons do the same.
- **Certificate.** The certificate of `progmasoft.com` names `git.progmasoft.com` (webroot, `--expand`).
diff --git a/ops/git/git-mirror.sh b/ops/git/git-mirror.sh
index 25bed02..a36f978 100644
--- a/ops/git/git-mirror.sh
+++ b/ops/git/git-mirror.sh
@@ -40,9 +40,16 @@ for name in $names; do
git -C "$repository" repack -a -d -b -q
touch "$repository/info/web/repacked"
fi
- # HEAD follows the default branch of GitHub.
- head=$(git -C "$repository" ls-remote --symref origin HEAD | sed -n 's|^ref: refs/heads/\([^[:space:]]*\)[[:space:]]*HEAD$|\1|p')
- [ -n "$head" ] && git -C "$repository" symbolic-ref HEAD "refs/heads/$head"
+ # HEAD follows the default branch of GitHub; a failed lookup fails the run.
+ if advertised=$(git -C "$repository" ls-remote --symref origin HEAD); then
+ head=$(printf '%s\n' "$advertised" | sed -n 's|^ref: refs/heads/\([^[:space:]]*\)[[:space:]]*HEAD$|\1|p')
+ [ -n "$head" ] && git -C "$repository" symbolic-ref HEAD "refs/heads/$head"
+ else
+ echo "git-mirror: the default branch of $name could not be read" >&2
+ status=1
+ fi
+ # git-http-backend serves only repositories with this file.
+ touch "$repository/git-daemon-export-ok"
# cgit shows the age of the last change of a repository from this file.
git -C "$repository" for-each-ref --sort=-committerdate --count=1 \
--format='%(committerdate:iso8601)' refs/heads > "$repository/info/web/last-modified"
@@ -51,4 +58,17 @@ for name in $names; do
status=1
fi
done
+# A repository no longer listed stops being served at once and is removed.
+for repository in "$root"/*.git; do
+ [ -d "$repository" ] || continue
+ name=$(basename "$repository" .git)
+ case " $(printf '%s ' $names)" in
+ *" $name "*) ;;
+ *)
+ rm -f "$repository/git-daemon-export-ok"
+ rm -rf -- "$repository"
+ echo "git-mirror: removed $name, which is no longer listed" >&2
+ ;;
+ esac
+done
exit "$status"
diff --git a/ops/git/nginx/git.conf b/ops/git/nginx/git.conf
index c11dce4..71c5b75 100644
--- a/ops/git/nginx/git.conf
+++ b/ops/git/nginx/git.conf
@@ -80,7 +80,7 @@ server {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/libexec/git-core/git-http-backend;
fastcgi_param GIT_PROJECT_ROOT /srv/git/mirrors;
- fastcgi_param GIT_HTTP_EXPORT_ALL "";
+ # No GIT_HTTP_EXPORT_ALL: only mirrors git-mirror marks with git-daemon-export-ok are served.
fastcgi_param PATH_INFO /$git_repo.git/$git_path;
fastcgi_param REMOTE_USER "";
# The mirrors belong to gitmirror and are read as cgit; only this backend trusts them.