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

summaryrefslogtreecommitdiffhomepage
path: root/ops/git/nginx
diff options
context:
space:
mode:
authorLeitwolf2026-11-13 05:06:08 +0300
committerGitHub2026-11-13 05:06:08 +0300
commitd82258f9f3bb23a0f59d79b984ff527942efc0f0 (patch)
treeda8b19d9dbe959d85ab60b044295b3c1afb8cc3a /ops/git/nginx
parent189b15ca0909033a40626bc8f600686e561b9cbb (diff)
parentf980fee24c5302173fa00ab758b7c54213d2bb7a (diff)
downloadprogmaweb-content/catalog-without-tala.tar.gz
Merge branch 'main' into content/catalog-without-talacontent/catalog-without-tala
Diffstat (limited to 'ops/git/nginx')
-rw-r--r--ops/git/nginx/git.conf113
1 files changed, 113 insertions, 0 deletions
diff --git a/ops/git/nginx/git.conf b/ops/git/nginx/git.conf
new file mode 100644
index 0000000..71c5b75
--- /dev/null
+++ b/ops/git/nginx/git.conf
@@ -0,0 +1,113 @@
+# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1
+
+# https://git.progmasoft.com: cgit over read-only mirrors, and clones over smart HTTP with
+# git-http-backend (upload-pack only). Installed as /etc/nginx/conf.d/git.conf.
+
+limit_req_zone $binary_remote_addr zone=git_pages:10m rate=10r/s;
+limit_req_zone $binary_remote_addr zone=git_clones:10m rate=1r/s;
+limit_conn_zone $binary_remote_addr zone=git_connections:10m;
+
+server {
+ listen 80;
+ listen [::]:80;
+ server_name git.progmasoft.com;
+
+ location ^~ /.well-known/acme-challenge/ {
+ root /var/lib/letsencrypt;
+ default_type text/plain;
+ }
+
+ location / {
+ return 308 https://git.progmasoft.com$request_uri;
+ }
+}
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ http2 on;
+ server_name git.progmasoft.com;
+
+ ssl_certificate /etc/letsencrypt/live/progmasoft.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/progmasoft.com/privkey.pem;
+ include /etc/letsencrypt/options-ssl-nginx.conf;
+
+ # Nothing is uploaded here: a clone sends only the list of what it wants.
+ client_max_body_size 1m;
+ limit_conn git_connections 8;
+
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-Frame-Options "DENY" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
+ # cgit writes no scripts; its diffstat bars set their width in style attributes.
+ add_header Content-Security-Policy "default-src 'none'; style-src-elem 'self'; style-src-attr 'unsafe-inline'; img-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'" always;
+
+ location ^~ /static/ {
+ alias /srv/git/www/static/;
+ expires 1d;
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Content-Security-Policy "default-src 'none'; style-src 'self'" always;
+ }
+
+ location = /robots.txt {
+ default_type text/plain;
+ return 200 "User-agent: *\nDisallow: /*/snapshot/\nDisallow: /*/log/\nDisallow: /*/diff/\nDisallow: /*/patch/\n";
+ }
+
+ # Pushing is never possible: no receive-pack, and the dumb protocol is off.
+ location ~ ^/[A-Za-z0-9._-]+/git-receive-pack$ {
+ return 403;
+ }
+
+ location ~ ^/(?<git_repo>[A-Za-z0-9._-]+?)(?:\.git)?/(?<git_path>info/refs|git-upload-pack)$ {
+ set $git_service "";
+ if ($git_path = "info/refs") {
+ set $git_service $arg_service;
+ }
+ if ($git_path = "git-upload-pack") {
+ set $git_service "git-upload-pack";
+ }
+ if ($git_service != "git-upload-pack") {
+ return 403;
+ }
+ limit_req zone=git_clones burst=10 nodelay;
+
+ fastcgi_pass unix:/run/fcgiwrap/fcgiwrap-cgit.sock;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME /usr/libexec/git-core/git-http-backend;
+ fastcgi_param GIT_PROJECT_ROOT /srv/git/mirrors;
+ # 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.
+ fastcgi_param GIT_CONFIG_COUNT 1;
+ fastcgi_param GIT_CONFIG_KEY_0 safe.directory;
+ fastcgi_param GIT_CONFIG_VALUE_0 /srv/git/mirrors/*;
+ fastcgi_read_timeout 300s;
+ # Nginx buffers the pack (to a file when it is large), so a slow reader never stalls
+ # git-http-backend behind fcgiwrap.
+ fastcgi_buffering on;
+ fastcgi_max_temp_file_size 2048m;
+ }
+
+ location / {
+ limit_req zone=git_pages burst=40 nodelay;
+ # The menus of cgit (branch, diff options) submit themselves from inline handlers, which the
+ # policy above blocks; their buttons ("switch", "reload") do the same without a script.
+ sub_filter "" "";
+ sub_filter_once off;
+
+ fastcgi_pass unix:/run/fcgiwrap/fcgiwrap-cgit.sock;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME /var/www/cgi-bin/cgit;
+ fastcgi_param CGIT_CONFIG /etc/cgitrc;
+ fastcgi_param PATH_INFO $uri;
+ fastcgi_param QUERY_STRING $args;
+ fastcgi_param HTTP_HOST $server_name;
+ fastcgi_read_timeout 60s;
+ }
+}