diff options
Diffstat (limited to '')
| -rw-r--r-- | custom/servers/nginx.conf | 327 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 file changed, 200 insertions, 127 deletions
diff --git a/custom/servers/nginx.conf b/custom/servers/nginx.conf index 76ac260..3b0b7ef 100644 --- a/custom/servers/nginx.conf +++ b/custom/servers/nginx.conf @@ -1,8 +1,10 @@ # nginx configuration for cgit. # -# nginx cannot run CGI programs itself, so a small bridge called fcgiwrap -# runs the cgit.cgi binary and speaks FastCGI to nginx. Starting fcgiwrap is -# covered in the notes at the end of this file. +# This is a complete nginx.conf rather than a snippet for conf.d or +# sites-enabled, so nothing here is included from elsewhere. Install it and +# reload, or point nginx straight at it to try it out. +# nginx -t -c /path/to/nginx.conf # check the syntax +# nginx -c /path/to/nginx.conf # run it # # Paths assumed below, edit them to match your install. # cgit CGI binary /usr/lib/cgit/cgit.cgi @@ -16,166 +18,237 @@ # base from SCRIPT_NAME. The five static assets are served straight off disk # and must never be routed through cgit. Passing PATH_INFO through is the # single most important part of the config below. +# +# nginx cannot run CGI programs itself, so a small bridge called fcgiwrap runs +# the cgit.cgi binary and speaks FastCGI to nginx. Nothing below works until +# that socket exists. On Debian and Ubuntu the packaged systemd socket +# provides /run/fcgiwrap.socket, so enabling it is enough. +# apt install fcgiwrap +# systemctl enable --now fcgiwrap.socket +# The socket must be readable by nginx's user. The packaged unit runs fcgiwrap +# as www-data, which nginx also uses on those systems. Without systemd you can +# run +# spawn-fcgi -s /run/fcgiwrap.socket -M 660 -- /usr/sbin/fcgiwrap +# or run fcgiwrap over TCP and point fastcgi_pass at 127.0.0.1:9000. -# --- Optional HTTP to HTTPS redirect ---------------------------------------- -# Delete this whole server block if you serve plain HTTP only. -server { - listen 80; - listen [::]:80; - server_name git.example.org; +# The user nginx drops to after binding the ports. It is www-data on Debian +# and Ubuntu, nginx on RHEL and Fedora, and http on Arch and Alpine. It has to +# match whatever owns the fcgiwrap socket. +user www-data; +worker_processes auto; +pid /run/nginx.pid; - # ACME http-01 challenge files, if you use certbot in webroot mode. - location ^~ /.well-known/acme-challenge/ { - root /var/www/html; - } +# Startup and worker errors. Per-site request logs are set in the vhost. +error_log /var/log/nginx/error.log warn; - # Everything else moves to HTTPS. - location / { - return 301 https://$host$request_uri; - } +events { + worker_connections 1024; } -# --- Main site -------------------------------------------------------------- -# Written for TLS on 443. For a quick plain-HTTP test, change the two listen -# lines to port 80, delete the redirect block above, and delete the four ssl -# lines below. Everything else stays the same. -server { - listen 443 ssl; - listen [::]:443 ssl; - http2 on; - server_name git.example.org; +http { + # nginx's compiled-in type table knows only text/html, and the usual + # "include mime.types" would pull in a second file. Exactly five static + # files are served off disk, so their types are declared here instead and + # this config keeps standing on its own. Everything else nginx returns + # comes from cgit, which sets its own Content-Type. + types { + text/css css; + text/javascript js; + image/png png; + image/vnd.microsoft.icon ico; + text/plain txt; + } + default_type application/octet-stream; + + sendfile on; + tcp_nopush on; + keepalive_timeout 65; - ssl_certificate /etc/letsencrypt/live/git.example.org/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/git.example.org/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; + # Drop the version number from the Server header and from error pages. + server_tokens off; - # --- Security headers --------------------------------------------------- - # These sit here, not in cgit, because they must also cover the static - # assets nginx serves directly. cgit loads only its own /cgit.js and uses - # inline style on the diffstat bars, so script-src stays self while - # style-src allows inline. always applies them to error responses too. If - # you enable the gravatar or libravatar avatar filter, add its host to - # img-src, for example https://www.gravatar.com or https://seccdn.libravatar.org. - add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'" always; - add_header X-Content-Type-Options "nosniff" always; - add_header Referrer-Policy "no-referrer" always; - # Enable only once you serve HTTPS exclusively, since it is hard to undo. - #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; + # cgit pages are large and highly compressible, so this is the cheapest + # speedup available. text/html is always compressed and cannot be listed. + # Snapshot tarballs are deliberately absent, since they arrive compressed + # already and running them through gzip again only burns CPU. + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_min_length 1024; + gzip_types text/css text/javascript text/plain application/atom+xml; - # The document root is the directory that holds the static assets. cgit - # emits absolute links to /cgit.css and /cgit.png by default, so those - # files must resolve at the root of the URL space. Pointing root at the - # asset directory makes /cgit.css map to /usr/share/cgit/cgit.css. - root /usr/share/cgit; - # Upload cap for large form posts. Snapshots are generated rather than - # uploaded, so this does not limit them. - client_max_body_size 64m; + # Bounce plain HTTP up to HTTPS. Delete this whole server block if you + # serve plain HTTP only. + server { + listen 80; + listen [::]:80; + server_name git.example.org; - access_log /var/log/nginx/cgit.access.log; - error_log /var/log/nginx/cgit.error.log; + # ACME http-01 challenge files, if you use certbot in webroot mode. + location ^~ /.well-known/acme-challenge/ { + root /var/www/html; + } - # --- Static assets, served directly ------------------------------------- - # Match the assets by their exact root-level names, never by bare - # extension. cgit routes on PATH_INFO and a repository can hold files - # ending in .css or .png, so /myrepo/tree/style.css and /myrepo/plain/ - # logo.png are real cgit URLs. A broad extension match would capture - # those, look for them on disk, and return 404 before cgit could render - # them. Anchoring the regex at the start of the path matches /cgit.css but - # not /myrepo/tree/cgit.css, so it can never shadow a repository file. An - # nginx regex location is matched before the prefix location below, so - # these assets win for their exact URLs and cgit wins for the rest. - location ~ ^/(cgit\.css|cgit\.js|cgit\.png|favicon\.ico|robots\.txt)$ { - expires 30d; - access_log off; - try_files $uri =404; + # Everything else moves to HTTPS. + location / { + return 301 https://$host$request_uri; + } } - # --- cgit, the catch-all ------------------------------------------------ - # Everything that is not a static asset above is a cgit URL, the repo - # index, a repository, a page within a repository, a snapshot, a feed. - location / { - # nginx's standard FastCGI parameters, some of which are overridden - # below. A later fastcgi_param wins, so include order does not matter. - include fastcgi_params; - # The program fcgiwrap runs. It must be the cgit binary itself, not - # $document_root$fastcgi_script_name, which would try to run a repo - # path and is the usual cause of a failed request. - fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; + # The site itself, written for TLS on 443. For a quick plain-HTTP test, + # change the two listen lines to port 80, delete the redirect block above, + # and delete the http2 and ssl lines below. Everything else stays as it is. + server { + listen 443 ssl; + listen [::]:443 ssl; + # nginx 1.25.1 and newer. On older builds delete this and write the + # listen lines as "listen 443 ssl http2;" instead. + http2 on; + server_name git.example.org; - # cgit builds its link base, the virtual root, from SCRIPT_NAME. The - # stock parameters set SCRIPT_NAME to the whole request path, which - # would make cgit prepend that path to every link. Served at the - # domain root the script has no prefix, so force SCRIPT_NAME empty and - # cgit uses / as its base. A sub-path install sets it instead, see the - # end of this file. - fastcgi_param SCRIPT_NAME ""; + ssl_certificate /etc/letsencrypt/live/git.example.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/git.example.org/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + # Let the client pick, which is the modern advice once the ancient + # protocol versions are already excluded above. + ssl_prefer_server_ciphers off; + # Resumption, so a browser paging through a repository is not made to + # redo a full handshake on every connection. + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; - # How cgit learns the repository and page. At the domain root the - # whole request path is the PATH_INFO. - fastcgi_param PATH_INFO $uri; + # Security headers sit here, not in cgit, because they must also cover + # the static assets nginx serves directly. cgit loads only its own + # /cgit.js and uses inline style on the diffstat bars, so script-src + # stays self while style-src allows inline. always applies them to + # error responses too. If you enable the gravatar or libravatar avatar + # filter, add its host to img-src, for example https://www.gravatar.com + # or https://seccdn.libravatar.org. + add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer" always; + # Enable only once you serve HTTPS exclusively, since it is hard to undo. + #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; - # Page options such as h=branch, id=sha and the snapshot format. - fastcgi_param QUERY_STRING $query_string; + # The document root is the directory that holds the static assets. cgit + # emits absolute links to /cgit.css and /cgit.png by default, so those + # files must resolve at the root of the URL space. Pointing root at the + # asset directory makes /cgit.css map to /usr/share/cgit/cgit.css. + root /usr/share/cgit; - # Where the static assets live, kept consistent with root above. - fastcgi_param DOCUMENT_ROOT $document_root; + # The only request body cgit ever reads is the auth-filter login form, + # and it stops after 4096 bytes. Nothing else here accepts an upload, + # so keep the cap far below the nginx default of 1m. + client_max_body_size 64k; - # The browser's Host header, so cgit builds clone URLs against the - # name the visitor used rather than server_name. - fastcgi_param HTTP_HOST $http_host; + access_log /var/log/nginx/cgit.access.log combined; + error_log /var/log/nginx/cgit.error.log; - # Which config cgit reads. It checks CGIT_CONFIG and falls back to the - # compiled-in /etc/cgitrc. Setting it makes the location explicit and - # lets you move cgitrc without recompiling. - fastcgi_param CGIT_CONFIG /etc/cgitrc; + # Match the assets by their exact root-level names, never by bare + # extension. cgit routes on PATH_INFO and a repository can hold files + # ending in .css or .png, so /myrepo/tree/style.css and /myrepo/plain/ + # logo.png are real cgit URLs. A broad extension match would capture + # those, look for them on disk, and return 404 before cgit could render + # them. Anchoring the regex at the start of the path matches /cgit.css + # but not /myrepo/tree/cgit.css, so it can never shadow a repository + # file. An nginx regex location is matched before the prefix location + # below, so these assets win for their exact URLs and cgit wins for the + # rest. + location ~ ^/(cgit\.css|cgit\.js|cgit\.png|favicon\.ico|robots\.txt)$ { + expires 30d; + access_log off; + try_files $uri =404; + } - # The real scheme, so cgit builds correct https clone URLs. - fastcgi_param HTTPS $https if_not_empty; + # Everything that is not a static asset above is a cgit URL, the repo + # index, a repository, a page within a repository, a snapshot, a feed. + location / { + # The CGI environment. This is normally "include fastcgi_params", + # which would be a second file, so the list is written out here. + # Of all of it cgit itself reads only CGIT_CONFIG, PATH_INFO, + # QUERY_STRING, SCRIPT_NAME, REQUEST_METHOD, CONTENT_LENGTH, + # HTTP_HOST, HTTPS, SERVER_NAME, SERVER_PORT, HTTP_COOKIE and + # HTTP_REFERER. The cookie and referer arrive on their own, since + # nginx forwards request headers as HTTP_* without being asked. - # Hand off to the fcgiwrap socket. See the notes for how to create it. - # A TCP fcgiwrap would use for example 127.0.0.1:9000 here. - fastcgi_pass unix:/run/fcgiwrap.socket; + # The program fcgiwrap runs. It must be the cgit binary itself, not + # $document_root$fastcgi_script_name, which would try to run a repo + # path and is the usual cause of a failed request. + fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; - # Large outputs such as snapshot tarballs and blame on big files can - # take a while, so give cgit room and stream rather than buffer. - fastcgi_read_timeout 300s; - fastcgi_buffering off; - } -} + # cgit builds its link base, the virtual root, from SCRIPT_NAME. + # The stock parameters set SCRIPT_NAME to the whole request path, + # which would make cgit prepend that path to every link. Served at + # the domain root the script has no prefix, so force SCRIPT_NAME + # empty and cgit uses / as its base. A sub-path install sets it + # instead, see the end of this file. + fastcgi_param SCRIPT_NAME ""; + # How cgit learns the repository and page. At the domain root the + # whole request path is the PATH_INFO. + fastcgi_param PATH_INFO $uri; -# --- Notes, starting fcgiwrap ----------------------------------------------- -# cgit is a CGI binary and fcgiwrap is the CGI to FastCGI bridge nginx talks -# to. On Debian and Ubuntu the packaged systemd socket provides -# /run/fcgiwrap.socket, so enabling it is enough. -# apt install fcgiwrap -# systemctl enable --now fcgiwrap.socket -# The socket must be readable by nginx's user. The packaged unit runs fcgiwrap -# as www-data, which nginx also uses on those systems. Without systemd you can -# run -# spawn-fcgi -s /run/fcgiwrap.socket -M 660 -- /usr/sbin/fcgiwrap -# or run fcgiwrap over TCP and point fastcgi_pass at 127.0.0.1:9000. + # Page options such as h=branch, id=sha and the snapshot format. + fastcgi_param QUERY_STRING $query_string; + + # Which config cgit reads. It checks CGIT_CONFIG and falls back to + # the compiled-in /etc/cgitrc. Setting it makes the location + # explicit and lets you move cgitrc without recompiling. + fastcgi_param CGIT_CONFIG /etc/cgitrc; + + # The browser's Host header, so cgit builds clone URLs against the + # name the visitor used rather than server_name. + fastcgi_param HTTP_HOST $http_host; + + # The real scheme, so cgit builds correct https clone URLs. + fastcgi_param HTTPS $https if_not_empty; + + # The routine remainder, needed by fcgiwrap and by the login form. + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param CONTENT_TYPE $content_type; + fastcgi_param CONTENT_LENGTH $content_length; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param DOCUMENT_URI $document_uri; + fastcgi_param DOCUMENT_ROOT $document_root; + fastcgi_param SERVER_PROTOCOL $server_protocol; + fastcgi_param REQUEST_SCHEME $scheme; + fastcgi_param GATEWAY_INTERFACE CGI/1.1; + fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + fastcgi_param REMOTE_ADDR $remote_addr; + fastcgi_param REMOTE_PORT $remote_port; + fastcgi_param SERVER_ADDR $server_addr; + fastcgi_param SERVER_PORT $server_port; + fastcgi_param SERVER_NAME $server_name; + + # Hand off to the fcgiwrap socket. A TCP fcgiwrap would use for + # example 127.0.0.1:9000 here. + fastcgi_pass unix:/run/fcgiwrap.socket; + + # Large outputs such as snapshot tarballs and blame on big files + # can take a while, so give cgit room and stream rather than buffer. + fastcgi_read_timeout 300s; + fastcgi_buffering off; + } + } +} -# --- Alternative, serving cgit under a sub-path ----------------------------- # To serve cgit at https://git.example.org/cgit/ instead of the root, split -# the URL so SCRIPT_NAME is the prefix and PATH_INFO is the rest. +# the URL so SCRIPT_NAME is the prefix and PATH_INFO is the rest. Keep every +# other fastcgi_param from the location above. # # location /cgit/ { -# include fastcgi_params; # fastcgi_split_path_info ^(/cgit)(/.*)$; # fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; # fastcgi_param SCRIPT_NAME $fastcgi_script_name; # fastcgi_param PATH_INFO $fastcgi_path_info; -# fastcgi_param QUERY_STRING $query_string; -# fastcgi_param HTTP_HOST $http_host; -# fastcgi_param CGIT_CONFIG /etc/cgitrc; -# fastcgi_param HTTPS $https if_not_empty; +# ... # fastcgi_pass unix:/run/fcgiwrap.socket; # } # |
