Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Validate the configuration and harden what a deployment creates

ed1812123cd9e9df0f34fdaf0d2511a340c2aaa6 · Keenan Nemetz · 13d ago · parent 13cff33f

Validate the configuration and harden what a deployment creates

Two ways an instance used to run in a state it should have refused.

The configuration was never checked. `dev_login` was guarded only by a loopback
listener, so a proxy that rewrites Host to 127.0.0.1 inherited a credential-free
session; open registration could be enabled on a plain-HTTP origin WebAuthn would
never accept; a public_url could carry a path or credentials; a size could be
negative. Config.Validate now runs before anything is opened and reports every
problem at once, and dev_login requires an explicit dev_only opt-in next to a
loopback listener with no public address. The per-request check no longer trusts
Host at all: it must name the configured listener, and a request carrying a
forwarding header gets no session.

The files were never checked either. directories were created 0755 and the audit
log 0644, and a file keeps the mode it was created with, so an upgraded instance
stayed exposed. internal/secure creates secrets at 0600 in directories at 0700,
tightens what an existing install left, keeps group access for a shared backup
group, and refuses to start when a mode cannot be changed. Every audit writer and
the account backup go through it. The service unit drops all capabilities,
sets UMask=0027, disables core dumps, hides other processes and devices, and
restricts address families; docs/operations.md now states the production
baseline.

packaging/systemd/vole.service +36−3

@@ -12,6 +12,12 @@ ExecStart=/usr/bin/vole serve --config /etc/vole/vole.toml
12 12 Restart=on-failure
13 13 RestartSec=2s
14 14
15 # Everything the service creates is group-readable at most: 0027 gives the
16 # service account rw, the vole group r, and everybody else nothing. Vole creates
17 # its own secrets at 0600 regardless, but a umask is what covers the files git,
18 # sqlite and the ACME client create on its behalf.
19 UMask=0027
20
15 21 # The instance lives in /var/lib/vole. systemd creates and owns it, and with
16 22 # ProtectSystem=strict it is the only writable directory the service gets.
17 23 StateDirectory=vole
@@ -21,23 +27,50 @@ ConfigurationDirectory=vole
21 27 # Vole runs git for every push and read, so a busy instance has many children.
22 28 LimitNOFILE=65536
23 29
24 # The SSH listener is 2222 in the packaged config. To serve SSH or automatic
25 # HTTPS on a port below 1024, add AmbientCapabilities=CAP_NET_BIND_SERVICE;
26 # nothing else needs privilege.
30 # A core dump of this process is a copy of the SSH host key, the roster and any
31 # private key in memory.
32 LimitCORE=0
33
34 # No capabilities at all. The SSH listener is 2222 in the packaged config; to
35 # serve SSH or automatic HTTPS on a port below 1024, a drop-in must re-grant the
36 # one capability that needs, which means adding it here as well:
37 #
38 # [Service]
39 # CapabilityBoundingSet=CAP_NET_BIND_SERVICE
40 # AmbientCapabilities=CAP_NET_BIND_SERVICE
41 CapabilityBoundingSet=
42
27 43 NoNewPrivileges=true
28 44 PrivateTmp=true
45 PrivateDevices=true
29 46 ProtectSystem=strict
30 47 ProtectHome=true
31 48 ProtectClock=true
32 49 ProtectHostname=true
33 50 ProtectKernelTunables=true
34 51 ProtectKernelModules=true
52 ProtectKernelLogs=true
35 53 ProtectControlGroups=true
36 54 RestrictNamespaces=true
37 55 RestrictRealtime=true
38 56 RestrictSUIDSGID=true
39 57 LockPersonality=true
40 58 SystemCallArchitectures=native
59 RemoveIPC=true
60
61 # AF_NETLINK is not a network client: libc reads interface and address changes
62 # through it. Everything the forge needs to reach is unix sockets, TCP and UDP.
63 RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
64
65 # Other processes' command lines, environment and file descriptors are not this
66 # service's business, and a git child does not need to see them.
67 ProtectProc=invisible
68 ProcSubset=pid
69
70 # Go does not generate code at run time. The container runtime a CI runner needs
71 # runs in the runner's own unit, not here, so nothing in this process wants a
72 # writable-and-executable mapping.
73 MemoryDenyWriteExecute=true
41 74
42 75 [Install]
43 76 WantedBy=multi-user.target