Fossil SCM

fossil-scm / www / server / openbsd / fastcgi.md
Source Blame History 302 lines
62ec410… wyoung 1 # Serving via httpd on OpenBSD
62ec410… wyoung 2
62ec410… wyoung 3 [`httpd`][httpd] is the default web server that is included in the base
62ec410… wyoung 4 install on OpenBSD. It's minimal and lightweight but secure and capable,
62ec410… wyoung 5 and provides a clean interface for setting up a Fossil server using
62ec410… wyoung 6 FastCGI.
62ec410… wyoung 7
62ec410… wyoung 8 This article will detail the steps required to setup a TLS-enabled
62ec410… wyoung 9 `httpd` configuration that serves multiple Fossil repositories out of
62ec410… wyoung 10 a single directory within a chroot, and allow `ssh` access to create
62ec410… wyoung 11 new repositories remotely.
62ec410… wyoung 12
62ec410… wyoung 13 **NOTE:** The following instructions assume an OpenBSD 6.7 installation.
62ec410… wyoung 14
62ec410… wyoung 15 [httpd]: https://www.openbsd.org/papers/httpd-asiabsdcon2015.pdf
62ec410… wyoung 16
93cee1f… wyoung 17 ## <a id="fslinstall"></a>Install Fossil
62ec410… wyoung 18
62ec410… wyoung 19 Use the OpenBSD package manager `pkg_add` to install Fossil, making sure
62ec410… wyoung 20 to select the statically linked binary.
62ec410… wyoung 21
62ec410… wyoung 22 ```console
8a1ba49… wyoung 23 $ doas pkg_add fossil
8a1ba49… wyoung 24 quirks-3.325 signed on 2020-06-12T06:24:53Z
8a1ba49… wyoung 25 Ambiguous: choose package for fossil
8a1ba49… wyoung 26 0: <None>
8a1ba49… wyoung 27 1: fossil-2.10v0
8a1ba49… wyoung 28 2: fossil-2.10v0-static
8a1ba49… wyoung 29 Your choice: 2
8a1ba49… wyoung 30 fossil-2.10v0-static: ok
62ec410… wyoung 31 ```
62ec410… wyoung 32
62ec410… wyoung 33 This installs Fossil into the chroot. To facilitate local use, create a
62ec410… wyoung 34 symbolic link of the fossil executable into `/usr/local/bin`.
62ec410… wyoung 35
62ec410… wyoung 36 ```console
8a1ba49… wyoung 37 $ doas ln -s /var/www/bin/fossil /usr/local/bin/fossil
62ec410… wyoung 38 ```
62ec410… wyoung 39
62ec410… wyoung 40 As a privileged user, create the file `/var/www/cgi-bin/scm` with the
62ec410… wyoung 41 following contents to make the CGI script that `httpd` will execute in
62ec410… wyoung 42 response to `fsl.domain.tld` requests; all paths are relative to the
62ec410… wyoung 43 `/var/www` chroot.
62ec410… wyoung 44
62ec410… wyoung 45 ```sh
8a1ba49… wyoung 46 #!/bin/fossil
8a1ba49… wyoung 47 directory: /htdocs/fsl.domain.tld
8a1ba49… wyoung 48 notfound: https://domain.tld
8a1ba49… wyoung 49 repolist
8a1ba49… wyoung 50 errorlog: /logs/fossil.log
62ec410… wyoung 51 ```
62ec410… wyoung 52
62ec410… wyoung 53 The `directory` directive instructs Fossil to serve all repositories
62ec410… wyoung 54 found in `/var/www/htdocs/fsl.domain.tld`, while `errorlog` sets logging
62ec410… wyoung 55 to be saved to `/var/www/logs/fossil.log`; create the repository
62ec410… wyoung 56 directory and log file—making the latter owned by the `www` user, and
62ec410… wyoung 57 the script executable.
62ec410… wyoung 58
62ec410… wyoung 59 ```console
8a1ba49… wyoung 60 $ doas mkdir /var/www/htdocs/fsl.domain.tld
8a1ba49… wyoung 61 $ doas touch /var/www/logs/fossil.log
8a1ba49… wyoung 62 $ doas chown www /var/www/logs/fossil.log
8a1ba49… wyoung 63 $ doas chmod 660 /var/www/logs/fossil.log
8a1ba49… wyoung 64 $ doas chmod 755 /var/www/cgi-bin/scm
62ec410… wyoung 65 ```
62ec410… wyoung 66
93cee1f… wyoung 67 ## <a id="chroot"></a>Setup chroot
62ec410… wyoung 68
62ec410… wyoung 69 Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible
62ec410… wyoung 70 from within the chroot, so need to be constructed; `/var`, however, is
62ec410… wyoung 71 mounted with the `nodev` option. Rather than removing this default
4b240ec… jamsek 72 setting, create a small memory filesystem and then mount it on to
4b240ec… jamsek 73 `/var/www/dev` with [`mount_mfs(8)`][mfs] so that the `random` and
e755561… danield 74 `null` device files can be created. In order to avoid necessitating a
4b240ec… jamsek 75 startup script to recreate the device files at boot, create a template
4b240ec… jamsek 76 of the needed ``/dev`` tree to automatically populate the memory
4b240ec… jamsek 77 filesystem.
62ec410… wyoung 78
62ec410… wyoung 79 ```console
8a1ba49… wyoung 80 $ doas mkdir /var/www/dev
8a1ba49… wyoung 81 $ doas install -d -g daemon /template/dev
8a1ba49… wyoung 82 $ cd /template/dev
8a1ba49… wyoung 83 $ doas /dev/MAKEDEV urandom
8a1ba49… wyoung 84 $ doas mknod -m 666 null c 2 2
8a1ba49… wyoung 85 $ doas mount_mfs -s 1M -P /template/dev /dev/sd0b /var/www/dev
8a1ba49… wyoung 86 $ ls -l
8a1ba49… wyoung 87 total 0
8a1ba49… wyoung 88 crw-rw-rw- 1 root daemon 2, 2 Jun 20 08:56 null
8a1ba49… wyoung 89 lrwxr-xr-x 1 root daemon 7 Jun 18 06:30 random@ -> urandom
8a1ba49… wyoung 90 crw-r--r-- 1 root wheel 45, 0 Jun 18 06:30 urandom
62ec410… wyoung 91 ```
62ec410… wyoung 92
62ec410… wyoung 93 [mfs]: https://man.openbsd.org/mount_mfs.8
62ec410… wyoung 94
62ec410… wyoung 95 To make the mountable memory filesystem permanent, open `/etc/fstab` as
62ec410… wyoung 96 a privileged user and add the following line to automate creation of the
62ec410… wyoung 97 filesystem at startup:
62ec410… wyoung 98
62ec410… wyoung 99 ```console
8a1ba49… wyoung 100 swap /var/www/dev mfs rw,-s=1048576,-P=/template/dev 0 0
4b240ec… jamsek 101 ```
62ec410… wyoung 102
62ec410… wyoung 103 The same user that executes the fossil binary must have writable access
62ec410… wyoung 104 to the repository directory that resides within the chroot; on OpenBSD
62ec410… wyoung 105 this is `www`. In addition, grant repository directory ownership to the
62ec410… wyoung 106 user who will push to, pull from, and create repositories.
62ec410… wyoung 107
62ec410… wyoung 108 ```console
8a1ba49… wyoung 109 $ doas chown -R user:www /var/www/htdocs/fsl.domain.tld
8a1ba49… wyoung 110 $ doas chmod 770 /var/www/htdocs/fsl.domain.tld
62ec410… wyoung 111 ```
62ec410… wyoung 112
93cee1f… wyoung 113 ## <a id="httpdconfig"></a>Configure httpd
62ec410… wyoung 114
62ec410… wyoung 115 On OpenBSD, [httpd.conf(5)][httpd] is the configuration file for
62ec410… wyoung 116 `httpd`. To setup the server to serve all Fossil repositores within the
62ec410… wyoung 117 directory specified in the CGI script, and automatically redirect
62ec410… wyoung 118 standard HTTP requests to HTTPS—apart from [Let's Encrypt][LE]
62ec410… wyoung 119 challenges issued in response to [acme-client(1)][acme] certificate
62ec410… wyoung 120 requests—create `/etc/httpd.conf` as a privileged user with the
62ec410… wyoung 121 following contents.
62ec410… wyoung 122
62ec410… wyoung 123 [LE]: https://letsencrypt.org
62ec410… wyoung 124 [acme]: https://man.openbsd.org/acme-client.1
62ec410… wyoung 125 [httpd.conf(5)]: https://man.openbsd.org/httpd.conf.5
62ec410… wyoung 126
62ec410… wyoung 127 ```apache
8a1ba49… wyoung 128 server "fsl.domain.tld" {
8a1ba49… wyoung 129 listen on * port http
8a1ba49… wyoung 130 root "/htdocs/fsl.domain.tld"
8a1ba49… wyoung 131 location "/.well-known/acme-challenge/*" {
8a1ba49… wyoung 132 root "/acme"
8a1ba49… wyoung 133 request strip 2
8a1ba49… wyoung 134 }
8a1ba49… wyoung 135 location * {
8a1ba49… wyoung 136 block return 301 "https://$HTTP_HOST$REQUEST_URI"
8a1ba49… wyoung 137 }
8a1ba49… wyoung 138 location "/*" {
8a1ba49… wyoung 139 fastcgi { param SCRIPT_FILENAME "/cgi-bin/scm" }
8a1ba49… wyoung 140 }
8a1ba49… wyoung 141 }
8a1ba49… wyoung 142
8a1ba49… wyoung 143 server "fsl.domain.tld" {
8a1ba49… wyoung 144 listen on * tls port https
8a1ba49… wyoung 145 root "/htdocs/fsl.domain.tld"
8a1ba49… wyoung 146 tls {
8a1ba49… wyoung 147 certificate "/etc/ssl/domain.tld.fullchain.pem"
8a1ba49… wyoung 148 key "/etc/ssl/private/domain.tld.key"
8a1ba49… wyoung 149 }
8a1ba49… wyoung 150 hsts {
8a1ba49… wyoung 151 max-age 15768000
8a1ba49… wyoung 152 preload
8a1ba49… wyoung 153 subdomains
8a1ba49… wyoung 154 }
8a1ba49… wyoung 155 connection max request body 104857600
8a1ba49… wyoung 156 location "/*" {
8a1ba49… wyoung 157 fastcgi { param SCRIPT_FILENAME "/cgi-bin/scm" }
8a1ba49… wyoung 158 }
8a1ba49… wyoung 159 location "/.well-known/acme-challenge/*" {
8a1ba49… wyoung 160 root "/acme"
8a1ba49… wyoung 161 request strip 2
8a1ba49… wyoung 162 }
8a1ba49… wyoung 163 }
62ec410… wyoung 164 ```
62ec410… wyoung 165
7252f7a… wyoung 166 [The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server
7252f7a… wyoung 167 is 1 MiB. Fossil chunks its sync protocol such that this is not
7252f7a… wyoung 168 normally a problem, but when sending [unversioned content][uv], it uses
7252f7a… wyoung 169 a single message for the entire file. Therefore, if you will be storing
7252f7a… wyoung 170 files larger than this limit as unversioned content, you need to raise
7252f7a… wyoung 171 the limit as we’ve done above with the “`connection max request body`”
7252f7a… wyoung 172 setting, raising the limit to 100 MiB.
7252f7a… wyoung 173
7252f7a… wyoung 174 [dlim]: https://man.openbsd.org/httpd.conf.5#connection
7252f7a… wyoung 175 [uv]: ../../unvers.wiki
7252f7a… wyoung 176
a3be0b8… drh 177 **NOTE:** If not already in possession of an HTTPS certificate, comment
62ec410… wyoung 178 out the `https` server block and proceed to securing a free
62ec410… wyoung 179 [Let's Encrypt Certificate](#letsencrypt); otherwise skip to
62ec410… wyoung 180 [Start `httpd`](#starthttpd).
62ec410… wyoung 181
7252f7a… wyoung 182
93cee1f… wyoung 183 ## <a id="letsencrypt"></a>Let's Encrypt Certificate
62ec410… wyoung 184
62ec410… wyoung 185 In order for `httpd` to serve HTTPS, secure a free certificate from
62ec410… wyoung 186 Let's Encrypt using `acme-client`. Before issuing the request, however,
62ec410… wyoung 187 ensure you have a zone record for the subdomain with your registrar or
62ec410… wyoung 188 nameserver. Then open `/etc/acme-client.conf` as a privileged user to
62ec410… wyoung 189 configure the request.
62ec410… wyoung 190
62ec410… wyoung 191 ```dosini
8a1ba49… wyoung 192 authority letsencrypt {
8a1ba49… wyoung 193 api url "https://acme-v02.api.letsencrypt.org/directory"
8a1ba49… wyoung 194 account key "/etc/acme/letsencrypt-privkey.pem"
8a1ba49… wyoung 195 }
8a1ba49… wyoung 196
8a1ba49… wyoung 197 authority letsencrypt-staging {
8a1ba49… wyoung 198 api url "https://acme-staging.api.letsencrypt.org/directory"
8a1ba49… wyoung 199 account key "/etc/acme/letsencrypt-staging-privkey.pem"
8a1ba49… wyoung 200 }
8a1ba49… wyoung 201
8a1ba49… wyoung 202 domain domain.tld {
8a1ba49… wyoung 203 alternative names { www.domain.tld fsl.domain.tld }
8a1ba49… wyoung 204 domain key "/etc/ssl/private/domain.tld.key"
8a1ba49… wyoung 205 domain certificate "/etc/ssl/domain.tld.crt"
8a1ba49… wyoung 206 domain full chain certificate "/etc/ssl/domain.tld.fullchain.pem"
8a1ba49… wyoung 207 sign with letsencrypt
8a1ba49… wyoung 208 }
62ec410… wyoung 209 ```
62ec410… wyoung 210
62ec410… wyoung 211 Start `httpd` with the new configuration file, and issue the certificate
62ec410… wyoung 212 request.
62ec410… wyoung 213
62ec410… wyoung 214 ```console
8a1ba49… wyoung 215 $ doas rcctl start httpd
8a1ba49… wyoung 216 $ doas acme-client -vv domain.tld
8a1ba49… wyoung 217 acme-client: /etc/acme/letsencrypt-privkey.pem: account key exists (not creating)
8a1ba49… wyoung 218 acme-client: /etc/acme/letsencrypt-privkey.pem: loaded RSA account key
8a1ba49… wyoung 219 acme-client: /etc/ssl/private/domain.tld.key: generated RSA domain key
8a1ba49… wyoung 220 acme-client: https://acme-v01.api.letsencrypt.org/directory: directories
8a1ba49… wyoung 221 acme-client: acme-v01.api.letsencrypt.org: DNS: 172.65.32.248
8a1ba49… wyoung 222 ...
8a1ba49… wyoung 223 N(Q????Z???j?j?>W#????b???? H????eb??T??*? DNosz(???n{L}???D???4[?B] (1174 bytes)
8a1ba49… wyoung 224 acme-client: /etc/ssl/domain.tld.crt: created
8a1ba49… wyoung 225 acme-client: /etc/ssl/domain.tld.fullchain.pem: created
62ec410… wyoung 226 ```
62ec410… wyoung 227
62ec410… wyoung 228 A successful result will output the public certificate, full chain of
62ec410… wyoung 229 trust, and private key into the `/etc/ssl` directory as specified in
62ec410… wyoung 230 `acme-client.conf`.
62ec410… wyoung 231
62ec410… wyoung 232 ```console
8a1ba49… wyoung 233 $ doas ls -lR /etc/ssl
8a1ba49… wyoung 234 -r--r--r-- 1 root wheel 2.3K Mar 2 01:31:03 2018 domain.tld.crt
8a1ba49… wyoung 235 -r--r--r-- 1 root wheel 3.9K Mar 2 01:31:03 2018 domain.tld.fullchain.pem
62ec410… wyoung 236
8a1ba49… wyoung 237 /etc/ssl/private:
8a1ba49… wyoung 238 -r-------- 1 root wheel 3.2K Mar 2 01:31:03 2018 domain.tld.key
62ec410… wyoung 239 ```
62ec410… wyoung 240
62ec410… wyoung 241 Make sure to reopen `/etc/httpd.conf` to uncomment the second server
62ec410… wyoung 242 block responsible for serving HTTPS requests before proceeding.
62ec410… wyoung 243
93cee1f… wyoung 244 ## <a id="starthttpd"></a>Start `httpd`
62ec410… wyoung 245
62ec410… wyoung 246 With `httpd` configured to serve Fossil repositories out of
62ec410… wyoung 247 `/var/www/htdocs/fsl.domain.tld`, and the certificates and key in place,
62ec410… wyoung 248 enable and start `slowcgi`—OpenBSD's FastCGI wrapper server that will
62ec410… wyoung 249 execute the above Fossil CGI script—before checking that the syntax of
62ec410… wyoung 250 the `httpd.conf` configuration file is correct, and (re)starting the
62ec410… wyoung 251 server (if still running from requesting a Let's Encrypt certificate).
62ec410… wyoung 252
62ec410… wyoung 253 ```console
8a1ba49… wyoung 254 $ doas rcctl enable slowcgi
8a1ba49… wyoung 255 $ doas rcctl start slowcgi
8a1ba49… wyoung 256 slowcgi(ok)
8a1ba49… wyoung 257 $ doas httpd -vnf /etc/httpd.conf
8a1ba49… wyoung 258 configuration OK
8a1ba49… wyoung 259 $ doas rcctl start httpd
8a1ba49… wyoung 260 httpd(ok)
62ec410… wyoung 261 ```
62ec410… wyoung 262
93cee1f… wyoung 263 ## <a id="clientconfig"></a>Configure Client
62ec410… wyoung 264
62ec410… wyoung 265 To facilitate creating new repositories and pushing them to the server,
62ec410… wyoung 266 add the following function to your `~/.cshrc` or `~/.zprofile` or the
62ec410… wyoung 267 config file for whichever shell you are using on your development box.
62ec410… wyoung 268
62ec410… wyoung 269 ```sh
8a1ba49… wyoung 270 finit() {
8a1ba49… wyoung 271 fossil init $1.fossil && \
8a1ba49… wyoung 272 chmod 664 $1.fossil && \
8a1ba49… wyoung 273 fossil open $1.fossil && \
8a1ba49… wyoung 274 fossil user password $USER $PASSWD && \
8a1ba49… wyoung 275 fossil remote-url https://$USER:[email protected]/$1 && \
8a1ba49… wyoung 276 rsync --perms $1.fossil [email protected]:/var/www/htdocs/fsl.domain.tld/ >/dev/null && \
8a1ba49… wyoung 277 chmod 644 $1.fossil && \
8a1ba49… wyoung 278 fossil ui
8a1ba49… wyoung 279 }
62ec410… wyoung 280 ```
62ec410… wyoung 281
62ec410… wyoung 282 This enables a new repository to be made with `finit repo`, which will
62ec410… wyoung 283 create the fossil repository file `repo.fossil` in the current working
62ec410… wyoung 284 directory; by default, the repository user is set to the environment
62ec410… wyoung 285 variable `$USER`. It then opens the repository and sets the user
62ec410… wyoung 286 password to the `$PASSWD` environment variable (which you can either set
62ec410… wyoung 287 with `export PASSWD 'password'` on the command line or add to a
62ec410… wyoung 288 *secured* shell environment file), and the `remote-url` to
62ec410… wyoung 289 `https://fsl.domain.tld/repo` with the credentials of `$USER` who is
62ec410… wyoung 290 authenticated with `$PASSWD`. Finally, it `rsync`'s the file to the
62ec410… wyoung 291 server before opening the local repository in your browser where you can
62ec410… wyoung 292 adjust settings such as anonymous user access, and set pertinent
62ec410… wyoung 293 repository details. Thereafter, you can add files with `fossil add`, and
62ec410… wyoung 294 commit with `fossil ci -m 'commit message'` where Fossil, by default,
62ec410… wyoung 295 will push to the `remote-url`. It's suggested you read the
62ec410… wyoung 296 [Fossil documentation][documentation]; with a sane and consistent
62ec410… wyoung 297 development model, the system is much more efficient and cohesive than
62ec410… wyoung 298 `git`—so the learning curve is not steep at all.
62ec410… wyoung 299
62ec410… wyoung 300 [documentation]: https://fossil-scm.org/home/doc/trunk/www/permutedindex.html
62ec410… wyoung 301
62ec410… wyoung 302 *[Return to the top-level Fossil server article.](../)*

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button