blob: c2a478f0436500937c9895cefdaa8a147d0cd6c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# 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.
#
# Paths assumed below, edit them to match your install.
#   cgit CGI binary   /usr/lib/cgit/cgit.cgi
#   static assets     /usr/share/cgit  (cgit.css cgit.js cgit.png favicon.ico robots.txt)
#   cgit config       /etc/cgitrc
#   public URL        https://git.example.org/  (cgit at the domain root)
#
# cgit is one CGI executable. It learns the repository and the page from
# PATH_INFO and reads page options such as h= and id= from QUERY_STRING. cgit
# builds its own link base from SCRIPT_NAME. The five static assets are served
# straight off disk and must never be routed through cgit.


# A plain assignment rather than "+=", since this config stands on its own
# and there is no distro base list to append to. Adding mod_accesslog here
# is what the access log below needs.
server.modules = (
    "mod_alias",
    "mod_setenv",
    "mod_cgi",
    "mod_accesslog",
)


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"
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


# 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. 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",
    ".png" => "image/png",
    ".ico" => "image/vnd.microsoft.icon",
    ".txt" => "text/plain",
)


# 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" )

    # Site-wide security headers are set here so they also cover the static
    # assets lighttpd serves. cgit itself sends only the headers the server
    # cannot supply. Those are Status, Content-Type, Content-Length and
    # Content-Disposition on downloads, Location on redirects, a no-store
    # Cache-Control on unauthenticated responses, the auth filter's Set-Cookie,
    # and on raw repository bytes a nosniff of its own next to the stricter
    # policy "default-src 'none'". Everything else, this policy included, is
    # the server's job.
    #
    # The add in add-response-header is load bearing. It appends a second
    # copy next to what cgit emitted, so a raw page carries both policies and
    # the browser enforces the stricter one, while the doubled nosniff line
    # is harmless. The set-response-header directive would instead replace
    # what cgit sent, swapping the strict policy on raw repository content
    # for this looser site one. Never switch add to set.
    #
    # form-action self covers the login form, the only form cgit renders. If
    # you enable the gravatar or libravatar avatar filter, add its host to
    # img-src. A head-include or repo.head-content that injects a <style>
    # block needs 'unsafe-inline' added to style-src.
    #
    # Permissions-Policy refuses the browser features a git viewer never asks
    # for, camera and location among them, for everything served here,
    # repository files included. The opener policy cuts any window.opener
    # link between cgit and pages that open it, and the resource policy stops
    # other origins from embedding what cgit serves, a hotlinked raw file for
    # example. git clients are not browsers and ignore both, so clone and
    # snapshot downloads keep working. The stricter
    # Cross-Origin-Embedder-Policy is deliberately absent because it would
    # break the avatar filters mentioned above.
    setenv.add-response-header = (
        "Content-Security-Policy" => "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'; form-action 'self'",
        "X-Content-Type-Options" => "nosniff",
        "Referrer-Policy" => "no-referrer",
        "Permissions-Policy" => "accelerometer=(), autoplay=(), camera=(), display-capture=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), usb=()",
        "Cross-Origin-Opener-Policy" => "same-origin",
        "Cross-Origin-Resource-Policy" => "same-origin"
    )
    # Two years of forced HTTPS. Enable this together with the TLS socket at
    # the end of this file and only once you serve HTTPS exclusively, since
    # it is hard to undo once browsers have seen it.
    #setenv.add-response-header += ( "Strict-Transport-Security" => "max-age=63072000; includeSubDomains" )

    # Register the cgit binary as a CGI program. The empty value means the
    # file is itself the program rather than input to an interpreter.
    cgi.assign = ( "cgit.cgi" => "" )

    # Routing. lighttpd's alias.url is first-match in declaration order, not
    # longest prefix, so the five static entries must come before the / entry.
    # If / came first it would swallow every request and recent lighttpd
    # refuses to start. The static entries are served off disk and the / entry
    # hands everything else to cgit.
    #
    # The trailing slash on cgit.cgi/ is load bearing. lighttpd builds the
    # physical path by stripping the matched key off the front of the URL and
    # appending the rest to the value. For the key / the remainder carries no
    # leading slash, so without the trailing slash a request for
    # /linux/tree/kernel/sched.c glues onto the binary name as
    # /usr/lib/cgit/cgit.cgilinux/tree/... and 404s. The trailing slash
    # restores the separator, giving cgit SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi
    # and PATH_INFO /linux/tree/kernel/sched.c.
    alias.url = (
        "/cgit.css"    => "/usr/share/cgit/cgit.css",
        "/cgit.js"     => "/usr/share/cgit/cgit.js",
        "/cgit.png"    => "/usr/share/cgit/cgit.png",
        "/favicon.ico" => "/usr/share/cgit/favicon.ico",
        "/robots.txt"  => "/usr/share/cgit/robots.txt",
        "/"            => "/usr/lib/cgit/cgit.cgi/",
    )

    # Served at / the SCRIPT_NAME is empty and cgit derives its link base
    # correctly. For a sub-path install use a key without a trailing slash
    # mapped to the binary without a trailing slash, for example
    #     "/git" => "/usr/lib/cgit/cgit.cgi"
    # so /git/linux/tree resolves to /usr/lib/cgit/cgit.cgi/linux/tree, giving
    # SCRIPT_NAME /git and PATH_INFO /linux/tree. cgit auto-detects the prefix.
    # If links come out wrong, pin it in cgitrc with virtual-root=/git.
}


# 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" {
#    ssl.engine  = "enable"
#    ssl.pemfile = "/etc/lighttpd/certs/git.example.org.crt"
#    ssl.privkey = "/etc/lighttpd/certs/git.example.org.key"
#    ssl.ca-file = "/etc/lighttpd/certs/git.example.org.chain.pem"
#    ssl.openssl.ssl-conf-cmd = ( "MinProtocol" => "TLSv1.2" )
#}
#
# Redirect plain HTTP to HTTPS, scoped to the port 80 socket. Needs
# mod_redirect.
#server.modules += ( "mod_redirect" )
#$SERVER["socket"] == ":80" {
#    $HTTP["host"] == "git.example.org" {
#        url.redirect = ( "^/(.*)" => "https://git.example.org/$1" )
#    }
#}