diff options
| author | Bryce Kwon <bryce@brycekwon.com> | |
|---|---|---|
| committer | Bryce Kwon <bryce@brycekwon.com> | |
| commit | ||
| parent | ||
| tree | ||
| download | ||
Make the server configs complete standalone files
| -rw-r--r-- | custom/servers/apache.conf | 212 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/servers/lighttpd.conf | 60 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/servers/nginx.conf | 327 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3 files changed, 395 insertions, 204 deletions
diff --git a/custom/servers/apache.conf b/custom/servers/apache.conf index d042dd9..7ff3bab 100644 --- a/custom/servers/apache.conf +++ b/custom/servers/apache.conf @@ -1,9 +1,15 @@ # Apache httpd 2.4 configuration for cgit. # +# This is a complete httpd.conf rather than a vhost snippet, so nothing here +# is included from elsewhere and no distro base config is assumed. Check it +# and run it with +# httpd -t -f /path/to/apache.conf # check the syntax +# httpd -f /path/to/apache.conf # run it +# To use it as an ordinary vhost file instead, drop everything above the +# virtual hosts and let your distro's httpd.conf supply it. +# # Apache runs the cgit.cgi binary directly through mod_cgid, so no FastCGI -# bridge is needed. Drop this file in your vhost directory, for example -# /etc/apache2/sites-available/cgit.conf on Debian and Ubuntu or -# /etc/httpd/conf.d/cgit.conf on RHEL and Fedora, then enable it and reload. +# bridge is needed. # # Paths assumed below, edit them to match your install. # cgit CGI binary /usr/lib/cgit/cgit.cgi @@ -21,26 +27,134 @@ # routes from shadowing each other. -# --- Required modules ------------------------------------------------------- +# ServerRoot is what every relative path below resolves against, including the +# module paths. It is /etc/httpd on RHEL and Fedora and /etc/apache2 on Debian +# and Ubuntu, where the modules live in /usr/lib/apache2/modules and the +# LoadModule lines need that absolute path instead of the relative one. +ServerRoot /etc/httpd +PidFile /var/run/httpd.pid + +# Where Apache puts its runtime scratch, the mutexes and the SSL session +# cache. It is /var/run/httpd on RHEL and Fedora and /var/run/apache2 on +# Debian and Ubuntu. The directory has to exist and be writable before Apache +# starts, which is normally the packaging's job. +DefaultRuntimeDir /var/run/httpd + +Listen 80 +Listen 443 + +# Set globally so Apache does not have to guess a name at startup, which it +# warns about. Each vhost overrides it with its own. +ServerName git.example.org + +# Drop the version number from the Server header and from error pages. +ServerTokens Prod +ServerSignature Off + + # mod_cgid suits the threaded MPMs that ship by default. Use mod_cgi instead -# only on the old prefork MPM. mod_alias provides Alias and ScriptAlias and -# mod_env provides SetEnv. On Debian and Ubuntu run a2enmod cgid alias env +# only on the old prefork MPM. mod_alias provides Alias and ScriptAlias, +# mod_env provides SetEnv, and the rest are the core pieces a standalone +# config cannot do without. On Debian and Ubuntu run +# a2enmod cgid alias env headers expires ssl # rather than editing these lines. The guards make double-loading harmless. -<IfModule !mod_cgid.c> - LoadModule cgid_module modules/mod_cgid.so +<IfModule !mpm_event_module> + LoadModule mpm_event_module modules/mod_mpm_event.so +</IfModule> +<IfModule !unixd_module> + LoadModule unixd_module modules/mod_unixd.so +</IfModule> +<IfModule !authz_core_module> + LoadModule authz_core_module modules/mod_authz_core.so </IfModule> -<IfModule !mod_alias.c> - LoadModule alias_module modules/mod_alias.so +<IfModule !log_config_module> + LoadModule log_config_module modules/mod_log_config.so </IfModule> -<IfModule !mod_env.c> - LoadModule env_module modules/mod_env.so +<IfModule !mime_module> + LoadModule mime_module modules/mod_mime.so </IfModule> -<IfModule !mod_headers.c> - LoadModule headers_module modules/mod_headers.so +<IfModule !alias_module> + LoadModule alias_module modules/mod_alias.so </IfModule> +<IfModule !cgid_module> + LoadModule cgid_module modules/mod_cgid.so +</IfModule> +<IfModule !env_module> + LoadModule env_module modules/mod_env.so +</IfModule> +<IfModule !headers_module> + LoadModule headers_module modules/mod_headers.so +</IfModule> +<IfModule !expires_module> + LoadModule expires_module modules/mod_expires.so +</IfModule> +# TLS. Delete these two along with the HTTPS vhost to run plain HTTP only. +# mod_socache_shmcb backs the SSL session cache and mod_ssl expects it. +<IfModule !socache_shmcb_module> + LoadModule socache_shmcb_module modules/mod_socache_shmcb.so +</IfModule> +<IfModule !ssl_module> + LoadModule ssl_module modules/mod_ssl.so +</IfModule> + + +# The user Apache drops to after binding the ports. It is apache on RHEL and +# Fedora, www-data on Debian and Ubuntu, and http on Arch. +User apache +Group apache + + +# The combined format comes from the distro config rather than from Apache +# itself, so a standalone config has to define it before any CustomLog uses it. +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +ErrorLog /var/log/apache2/error.log +LogLevel warn + + +# The usual "TypesConfig conf/mime.types" would pull in a second file. Exactly +# five static files are served off disk, so their types are declared here +# instead. Everything else Apache returns comes from cgit, which sets its own +# Content-Type. +AddType text/css .css +AddType text/javascript .js +AddType image/png .png +AddType image/vnd.microsoft.icon .ico +AddType text/plain .txt + + +# Deny the whole filesystem, then open only the two directories cgit needs. +# Without this a misplaced Alias could expose anything readable on the host. +<Directory /> + AllowOverride None + Require all denied +</Directory> + +# The static asset directory, read only. +<Directory "/usr/share/cgit"> + Options None + AllowOverride None + Require all granted + + # These assets rarely change, so let browsers cache them. + <IfModule mod_expires.c> + ExpiresActive On + ExpiresDefault "access plus 30 days" + </IfModule> +</Directory> + +# The cgit binary. +<Directory "/usr/lib/cgit"> + # Allow CGI execution here. ScriptAlias implies it, stating it makes the + # intent clear. + Options +ExecCGI + # Run cgit.cgi as a CGI even if it is ever reached through a plain Alias + # rather than ScriptAlias. + SetHandler cgi-script + AllowOverride None + Require all granted +</Directory> -# --- Plain HTTP virtual host ------------------------------------------------ # This vhost only bounces plain HTTP up to HTTPS. It serves no cgit itself, # every cgit directive lives in the HTTPS vhost below. To run without TLS for # now, convert the HTTPS vhost to port 80 and delete this whole block rather @@ -56,39 +170,44 @@ </VirtualHost> -# --- HTTPS virtual host, this one serves cgit ------------------------------- -# To run without TLS for now, change this opening line to <VirtualHost *:80>, -# delete the three SSL lines, and delete the port 80 vhost above so there is -# only one vhost. Everything else stays the same. +# The vhost that serves cgit. To run without TLS for now, change this opening +# line to port 80, delete the SSL lines, and delete the vhost above so there +# is only one. Everything else stays as it is. <VirtualHost *:443> ServerName git.example.org ErrorLog /var/log/apache2/cgit_ssl_error.log CustomLog /var/log/apache2/cgit_ssl_access.log combined - # TLS needs mod_ssl (a2enmod ssl). Point these at your certificate. + # Point these at your certificate. SSLEngine on SSLCertificateFile /etc/ssl/certs/git.example.org.crt SSLCertificateKeyFile /etc/ssl/private/git.example.org.key + # Subtractive rather than naming the versions to keep, since a mod_ssl + # built before TLS 1.3 rejects the +TLSv1.3 token outright and refuses to + # start. This form enables 1.3 wherever it exists. + SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 # Which config cgit reads. It falls back to the compiled-in /etc/cgitrc, # the same path used here, but setting it makes the location explicit and # lets you point at a per-vhost file later. SetEnv CGIT_CONFIG /etc/cgitrc - # --- Security headers (needs mod_headers, a2enmod headers) -------------- - # Set here, not in cgit, so they also cover the static assets Apache - # serves. script-src stays self because cgit loads only its own cgit.js, - # and style-src allows inline for the diffstat bars. If you enable the - # gravatar or libravatar avatar filter, add its host to img-src, for - # example https://www.gravatar.com. + # 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. + LimitRequestBody 65536 + + # Security headers are set here, not in cgit, so they also cover the static + # assets Apache serves. script-src stays self because cgit loads only its + # own cgit.js, and style-src allows inline for the diffstat bars. If you + # enable the gravatar or libravatar avatar filter, add its host to img-src, + # for example https://www.gravatar.com. Header always set 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'" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "no-referrer" # Enable only once you serve HTTPS exclusively, since it is hard to undo. #Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains" - # --- Static assets, served directly by Apache --------------------------- # These five files are the only things served off disk. Each Alias maps # one URL to one file. Because they come before the ScriptAlias below, a # request for /cgit.css is answered from disk and never reaches cgit. @@ -101,22 +220,6 @@ Alias /favicon.ico /usr/share/cgit/favicon.ico Alias /robots.txt /usr/share/cgit/robots.txt - # Apache 2.4 denies filesystem access by default, so open the asset - # directory for reading. - <Directory "/usr/share/cgit"> - Options None - AllowOverride None - Require all granted - - # Optional. These assets rarely change, so let browsers cache them. - # Needs mod_expires (a2enmod expires). Safe to delete this block. - <IfModule mod_expires.c> - ExpiresActive On - ExpiresDefault "access plus 30 days" - </IfModule> - </Directory> - - # --- cgit, the catch-all ------------------------------------------------ # ScriptAlias maps a URL prefix to a path, marks it executable, and # forwards the rest of the URL as PATH_INFO. Mapping / makes cgit the # handler for every URL the static Aliases above did not already claim. @@ -128,21 +231,20 @@ # h=next. cgit derives its link base from SCRIPT_NAME, which at the domain # root is / and needs no tuning. For a sub-path install see the note below. ScriptAlias / /usr/lib/cgit/cgit.cgi/ - - <Directory "/usr/lib/cgit"> - # Allow CGI execution here. ScriptAlias implies it, stating it makes - # the intent clear. - Options +ExecCGI - # Run cgit.cgi as a CGI even if it is ever reached through a plain - # Alias rather than ScriptAlias. - SetHandler cgi-script - AllowOverride None - Require all granted - </Directory> </VirtualHost> -# --- Sub-path install, only if cgit is not at the domain root --------------- +# The SSL session cache is a global mod_ssl setting, so it sits outside the +# vhosts. Resumption keeps a browser paging through a repository from redoing +# a full handshake on every connection. Delete along with the HTTPS vhost if +# you serve plain HTTP. +<IfModule mod_ssl.c> + # Relative, so it lands in DefaultRuntimeDir and follows it across distros. + SSLSessionCache "shmcb:ssl_scache(512000)" + SSLSessionCacheTimeout 300 +</IfModule> + + # To serve cgit at https://git.example.org/cgit/ instead of the root, change # the ScriptAlias to # ScriptAlias /cgit/ /usr/lib/cgit/cgit.cgi/ diff --git a/custom/servers/lighttpd.conf b/custom/servers/lighttpd.conf index 3111ed0..6ae87e0 100644 --- a/custom/servers/lighttpd.conf +++ b/custom/servers/lighttpd.conf @@ -1,5 +1,11 @@ # lighttpd configuration for cgit. # +# This is a complete lighttpd.conf rather than a snippet for conf-enabled, so +# nothing here is included from elsewhere and no distro base config is +# assumed. Check it and run it with +# lighttpd -tt -f /path/to/lighttpd.conf # check the syntax and modules +# lighttpd -D -f /path/to/lighttpd.conf # run it in the foreground +# # lighttpd runs the cgit.cgi binary directly through mod_cgi, so no FastCGI # bridge is needed. This is cgit's classic reference deployment, mod_cgi with # mod_alias and mod_setenv. @@ -16,29 +22,40 @@ # straight off disk and must never be routed through cgit. -# --- Modules ---------------------------------------------------------------- -# Append the three modules cgit needs so the distro's base config is kept. -# mod_alias maps URL paths onto files, mod_setenv injects CGIT_CONFIG, and -# mod_cgi runs cgit.cgi. -server.modules += ( "mod_alias", "mod_setenv", "mod_cgi" ) +# A plain assignment rather than "+=", since this config stands on its own and +# there is no distro base list to append to. mod_alias maps URL paths onto +# files, mod_setenv injects CGIT_CONFIG and the response headers, and mod_cgi +# runs cgit.cgi. Adding mod_accesslog here is what the access log below needs. +server.modules = ( + "mod_alias", + "mod_setenv", + "mod_cgi", + "mod_accesslog", +) -# --- Server basics ---------------------------------------------------------- server.port = 80 server.username = "http" # Debian and Ubuntu use www-data server.groupname = "http" server.document-root = "/usr/share/cgit" # a valid docroot must exist, the # alias rules below do the routing +server.pid-file = "/run/lighttpd.pid" server.errorlog = "/var/log/lighttpd/error.log" -# Access logging needs mod_accesslog. Load it and uncomment to enable. -#server.modules += ( "mod_accesslog" ) -#accesslog.filename = "/var/log/lighttpd/access.log" +accesslog.filename = "/var/log/lighttpd/access.log" + +# Drop the version number from the Server header and from error pages. +server.tag = "lighttpd" + +# 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. The value is in +# kilobytes and the default of 0 means unlimited. +server.max-request-size = 64 -# --- MIME types for the static assets --------------------------------------- # mod_alias serves the assets off disk, so lighttpd must know their content # types. Without this the stylesheet is sent as application/octet-stream and -# the browser ignores it. +# the browser ignores it. Only these five files are served off disk, so this +# short table is the whole of it and no external mime file is needed. mimetype.assign = ( ".css" => "text/css", ".js" => "text/javascript", @@ -48,20 +65,19 @@ mimetype.assign = ( ) -# --- Virtual host, git.example.org ------------------------------------------ -# A top-level conditional, so it matches on both the port 80 socket and the -# optional TLS socket at the end of this file. +# The vhost, as a top-level conditional so it matches on both the port 80 +# socket and the optional TLS socket at the end of this file. $HTTP["host"] == "git.example.org" { # Which config cgit reads. It falls back to the compiled-in /etc/cgitrc, # the same path used here, but setting it makes the location explicit. setenv.add-environment = ( "CGIT_CONFIG" => "/etc/cgitrc" ) - # --- Security headers --------------------------------------------------- - # Set here, not in cgit, so they also cover the static assets lighttpd - # serves. script-src stays self because cgit loads only its own cgit.js, - # and style-src allows inline for the diffstat bars. If you enable the - # gravatar or libravatar avatar filter, add its host to img-src. + # Security headers are set here, not in cgit, so they also cover the + # static assets lighttpd serves. script-src stays self because cgit loads + # only its own cgit.js, and style-src allows inline for the diffstat bars. + # If you enable the gravatar or libravatar avatar filter, add its host to + # img-src. setenv.add-response-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'", "X-Content-Type-Options" => "nosniff", @@ -109,9 +125,9 @@ $HTTP["host"] == "git.example.org" { } -# --- Optional HTTPS on 443 -------------------------------------------------- -# Uncomment this whole block to enable TLS. The host block above is socket -# independent, so it serves cgit over this socket too once the crypto is set. +# Uncomment this whole block to enable TLS on 443. The host block above is +# socket independent, so it serves cgit over this socket too once the crypto +# is set. #server.modules += ( "mod_openssl" ) # #$SERVER["socket"] == ":443" { 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; # } # |
