|
1
|
# Serving via systemd on Debian and Ubuntu |
|
2
|
|
|
3
|
[`systemd`][sdhome] is the service management framework in all major |
|
4
|
in-support versions of Linux. There are multiple ways to run Fossil |
|
5
|
under `systemd`. |
|
6
|
|
|
7
|
[sdhome]: https://www.freedesktop.org/wiki/Software/systemd/ |
|
8
|
[wpa]: https://en.wikipedia.org/wiki/Systemd#Adoption |
|
9
|
|
|
10
|
|
|
11
|
## Containerized Service |
|
12
|
|
|
13
|
Two of the methods for running [containerized Fossil][cntdoc] integrate |
|
14
|
with `systemd`, potentially obviating the more direct methods below: |
|
15
|
|
|
16
|
* If you take [the Podman method][podman] of running containerized |
|
17
|
Fossil, it opens the `podman generate systemd` option for you, as |
|
18
|
exemplified in [the `fslsrv` script][fslsrv] used on this author’s |
|
19
|
public Fossil-based web site. That script pulls its container images |
|
20
|
from [my Docker Hub repo][dhrepo] to avoid the need for my public |
|
21
|
Fossil server to have build tools and a copy of the Fossil source |
|
22
|
tree. You’re welcome to use my images as-is, or you may use these |
|
23
|
tools to bounce custom builds up through a separate container image |
|
24
|
repo you manage. |
|
25
|
|
|
26
|
* If you’re willing to give up [a lot of features][nsweak] relative to |
|
27
|
Podman, and you’re willing to tolerate a lot more manual |
|
28
|
administrivia, [the nspawn method][nspawn] has a lot less overhead, |
|
29
|
being a direct feature of `systemd` itself. |
|
30
|
|
|
31
|
Both of these options provide [better security][cntsec] than running |
|
32
|
Fossil directly under `systemd`, among [other benefits][cntdoc]. |
|
33
|
|
|
34
|
[cntdoc]: ../../containers.md |
|
35
|
[cntsec]: ../../containers.md#security |
|
36
|
[dhrepo]: https://hub.docker.com/r/tangentsoft/fossil |
|
37
|
[fslsrv]: https://tangentsoft.com/fossil/dir?name=bin |
|
38
|
[nspawn]: ../../containers.md#nspawn |
|
39
|
[nsweak]: ../../containers.md#nspawn-weaknesses |
|
40
|
[podman]: ../../containers.md#podman |
|
41
|
|
|
42
|
|
|
43
|
## User Service |
|
44
|
|
|
45
|
A fun thing you can easily do with `systemd` that you can’t directly do |
|
46
|
with older technologies like `inetd` and `xinetd` is to set a server up |
|
47
|
as a “user” service. |
|
48
|
|
|
49
|
You can’t listen on TCP port 80 with this method due to security |
|
50
|
restrictions on TCP ports in every OS where `systemd` runs, but you can |
|
51
|
create a listener socket on a high-numbered (≥ 1024) TCP port, |
|
52
|
suitable for sharing a Fossil repo to a workgroup on a private LAN. |
|
53
|
|
|
54
|
To do this, write the following in |
|
55
|
`~/.local/share/systemd/user/fossil.service`: |
|
56
|
|
|
57
|
> ```dosini |
|
58
|
[Unit] |
|
59
|
Description=Fossil user server |
|
60
|
After=network-online.target |
|
61
|
|
|
62
|
[Service] |
|
63
|
WorkingDirectory=/home/fossil/museum |
|
64
|
ExecStart=/home/fossil/bin/fossil server --port 9000 repo.fossil |
|
65
|
Restart=always |
|
66
|
RestartSec=3 |
|
67
|
|
|
68
|
[Install] |
|
69
|
WantedBy=multi-user.target |
|
70
|
``` |
|
71
|
|
|
72
|
Unlike with `inetd` and `xinetd`, we don’t need to tell `systemd` which |
|
73
|
user and group to run this service as, because we’ve installed it |
|
74
|
under the account we’re logged into, which `systemd` will use as the |
|
75
|
service’s owner. |
|
76
|
|
|
77
|
The result is essentially [the standalone server method](../any/none.md) |
|
78
|
coupled with an intelligent service manager that will start it |
|
79
|
automatically in the background on system boot, perform automatic |
|
80
|
service restarts with back-off logic, and more, making this much more |
|
81
|
robust than the by-hand launches of `fossil` in the platform-independent |
|
82
|
Fossil server instructions. The service will stay up until we |
|
83
|
explicitly tell it to shut down. |
|
84
|
|
|
85
|
This scheme couples well with [the generic SCGI instructions][scgi] as |
|
86
|
it requires a way to run the underlying repository server in the |
|
87
|
background. Given that its service port is then proxied by SCGI, it |
|
88
|
follows that it doesn’t need to run as a system service. A user service |
|
89
|
works perfectly well for this. |
|
90
|
|
|
91
|
Because we’ve set this up as a user service, the commands you give to |
|
92
|
manipulate the service vary somewhat from the sort you’re more likely to |
|
93
|
find online: |
|
94
|
|
|
95
|
$ systemctl --user daemon-reload |
|
96
|
$ systemctl --user enable fossil |
|
97
|
$ systemctl --user start fossil |
|
98
|
$ systemctl --user status fossil -l |
|
99
|
$ systemctl --user stop fossil |
|
100
|
|
|
101
|
That is, we don’t need to talk to `systemd` with `sudo` privileges, but |
|
102
|
we do need to tell it to look at the user configuration rather than the |
|
103
|
system-level configuration. |
|
104
|
|
|
105
|
This scheme isolates the permissions needed by the Fossil server, which |
|
106
|
reduces the amount of damage it can do if there is ever a |
|
107
|
remotely-triggerable security flaw found in Fossil. |
|
108
|
|
|
109
|
On some `systemd` based OSes, user services only run while that user is |
|
110
|
logged in interactively. This is common on systems aiming to provide |
|
111
|
desktop environments, where this is the behavior you often want. To |
|
112
|
allow background services to continue to run after logout, say: |
|
113
|
|
|
114
|
$ sudo loginctl enable-linger $USER |
|
115
|
|
|
116
|
You can paste the command just like that into your terminal, since |
|
117
|
`$USER` will expand to your login name. |
|
118
|
|
|
119
|
[scgi]: ../any/scgi.md |
|
120
|
|
|
121
|
|
|
122
|
|
|
123
|
### System Service Alternative |
|
124
|
|
|
125
|
There are some common reasons that you’d have good cause to install |
|
126
|
Fossil as a system-level service rather than the prior user-level one: |
|
127
|
|
|
128
|
* You’re using [the new `fossil server --cert` feature][sslsrv] to get |
|
129
|
TLS service and want it to listen directly on port 443, rather than be |
|
130
|
proxied, as one had to do before Fossil got the ability to act as a |
|
131
|
TLS server itself. That requires root privileges, so you can’t run |
|
132
|
it as a user-level service. |
|
133
|
|
|
134
|
* You’re proxying Fossil with [nginx](./nginx.md) or similar, allowing |
|
135
|
it to bind to high-numbered ports, but because it starts as a system |
|
136
|
service, you can’t get Fossil into the same dependency chain to |
|
137
|
ensure things start up and shut down in the proper order unless it |
|
138
|
*also* runs as a system service. |
|
139
|
|
|
140
|
* You want to make use of Fossil’s [chroot jail feature][cjail], which |
|
141
|
requires the server to start as root. |
|
142
|
|
|
143
|
There are just a small set of changes required: |
|
144
|
|
|
145
|
1. Install the unit file to one of the persistent system-level unit |
|
146
|
file directories. Typically, these are: |
|
147
|
|
|
148
|
/etc/systemd/system |
|
149
|
/lib/systemd/system |
|
150
|
|
|
151
|
2. Add `User` and `Group` directives to the `[Service]` section so |
|
152
|
Fossil runs as a normal user, preferably one with access only to |
|
153
|
the Fossil repo files, rather than running as `root`. |
|
154
|
|
|
155
|
[sslsrv]: ../../ssl-server.md |
|
156
|
[cjail]: ../../chroot.md |
|
157
|
|
|
158
|
|
|
159
|
## Socket Activation |
|
160
|
|
|
161
|
Another useful method to serve a Fossil repo via `systemd` is via a |
|
162
|
socket listener, which `systemd` calls “[socket activation][sa],” |
|
163
|
roughly equivalent to [the ancient `inetd` method](../any/inetd.md). |
|
164
|
It’s more complicated, but it has some nice properties. |
|
165
|
|
|
166
|
We first need to define the privileged socket listener by writing |
|
167
|
`/etc/systemd/system/fossil.socket`: |
|
168
|
|
|
169
|
> ```dosini |
|
170
|
[Unit] |
|
171
|
Description=Fossil socket |
|
172
|
|
|
173
|
[Socket] |
|
174
|
Accept=yes |
|
175
|
ListenStream=80 |
|
176
|
NoDelay=true |
|
177
|
|
|
178
|
[Install] |
|
179
|
WantedBy=sockets.target |
|
180
|
``` |
|
181
|
|
|
182
|
Note the change of configuration directory from the `~/.local` directory |
|
183
|
to the system level. We need to start this socket listener at the root |
|
184
|
level because of the low-numbered TCP port restriction we brought up |
|
185
|
above. |
|
186
|
|
|
187
|
This configuration says more or less the same thing as the socket part |
|
188
|
of an `inetd` entry [exemplified elsewhere in this |
|
189
|
documentation](../any/inetd.md). |
|
190
|
|
|
191
|
Next, create the service definition file in that same directory as |
|
192
|
`[email protected]`: |
|
193
|
|
|
194
|
> ```dosini |
|
195
|
[Unit] |
|
196
|
Description=Fossil socket server |
|
197
|
After=network-online.target |
|
198
|
|
|
199
|
[Service] |
|
200
|
WorkingDirectory=/home/fossil/museum |
|
201
|
ExecStart=/home/fossil/bin/fossil http repo.fossil |
|
202
|
StandardInput=socket |
|
203
|
|
|
204
|
[Install] |
|
205
|
WantedBy=multi-user.target |
|
206
|
``` |
|
207
|
|
|
208
|
Notice that we haven’t told `systemd` which user and group to run Fossil |
|
209
|
under. Since this is a system-level service definition, that means it |
|
210
|
will run as root, which then causes Fossil to [automatically drop into a |
|
211
|
`chroot(2)` jail](../../chroot.md) rooted at the `WorkingDirectory` |
|
212
|
we’ve configured above, shortly after each `fossil http` call starts. |
|
213
|
|
|
214
|
The `Restart*` directives we had in the user service configuration above |
|
215
|
are unnecessary for this method, since Fossil isn’t supposed to remain |
|
216
|
running under it. Each HTTP hit starts one Fossil instance, which |
|
217
|
handles that single client’s request and then immediately shuts down. |
|
218
|
|
|
219
|
Next, you need to tell `systemd` to reload its system-level |
|
220
|
configuration files and enable the listening socket: |
|
221
|
|
|
222
|
$ sudo systemctl daemon-reload |
|
223
|
$ sudo systemctl enable fossil.socket |
|
224
|
|
|
225
|
And now you can manipulate the socket listener: |
|
226
|
|
|
227
|
$ sudo systemctl start fossil.socket |
|
228
|
$ sudo systemctl status -l fossil.socket |
|
229
|
$ sudo systemctl stop fossil.socket |
|
230
|
|
|
231
|
Notice that we’re working with the *socket*, not the *service*. The fact |
|
232
|
that we’ve given them the same base name and marked the service as an |
|
233
|
instantiated service with the “`@`” notation allows `systemd` to |
|
234
|
automatically start an instance of the service each time a hit comes in |
|
235
|
on the socket that `systemd` is monitoring on Fossil’s behalf. To see |
|
236
|
this service instantiation at work, visit a long-running Fossil page |
|
237
|
(e.g. `/tarball`) and then give a command like this: |
|
238
|
|
|
239
|
$ sudo systemctl --full | grep fossil |
|
240
|
|
|
241
|
This will show information about the `fossil` socket and service |
|
242
|
instances, which should show your `/tarball` hit handler, if it’s still |
|
243
|
running: |
|
244
|
|
|
245
|
[email protected]:80-127.0.0.1:38304.service |
|
246
|
|
|
247
|
You can feed that service instance description to a `systemctl kill` |
|
248
|
command to stop that single instance without restarting the whole |
|
249
|
`fossil` service, for example. |
|
250
|
|
|
251
|
In all of this, realize that we’re able to manipulate a single socket |
|
252
|
listener or single service instance at a time, rather than reload the |
|
253
|
whole externally-facing network configuration as with the far more |
|
254
|
primitive `inetd` service. |
|
255
|
|
|
256
|
[sa]: http://0pointer.de/blog/projects/socket-activation.html |
|
257
|
|
|
258
|
|
|
259
|
*[Return to the top-level Fossil server article.](../)* |
|
260
|
|