deploy/cloud-init.yaml
242 lines · 7840 bytes
1#cloud-config
2# gitbay VPS bootstrap (Ubuntu 24.04).
3#
4# What this does on first boot:
5# - moves the host's admin sshd to port 2222 (gitbay's embedded SSH
6# listener owns port 22) — CONNECT ON 2222 AFTER FIRST BOOT
7# - creates the unprivileged gitbay user and directory layout
8# - installs /etc/gitbay/config.toml, the systemd unit (with
9# CAP_NET_BIND_SERVICE so ports 22/80/443 work without root), and a
10# nightly backup timer
11# - opens ufw for 22, 80, 443, 2222
12#
13# It does NOT install the gitbayd binary (it is not hosted anywhere yet);
14# scp it to /usr/local/bin/gitbayd afterward and `systemctl start gitbayd`.
15
16package_update: true
17packages:
18 - git
19 - ufw
20 - unattended-upgrades
21 - fail2ban
22 # The CI runner shares this host and the suite drives them; without them
23 # the LFS and signature tests skip themselves and CI goes green having
24 # tested less.
25 - git-lfs
26 - gnupg
27
28write_files:
29 # Admin sshd on 2222. Ubuntu 24.04 socket-activates sshd, so the port
30 # must change in BOTH sshd_config and the socket unit.
31 - path: /etc/ssh/sshd_config.d/60-gitbay-port.conf
32 content: |
33 Port 2222
34 PasswordAuthentication no
35 # Throttle unauthenticated connection floods on the admin sshd
36 # (gitbayd's own port 22 is throttled by limits.ssh_auth_rate).
37 MaxStartups 10:30:60
38 MaxAuthTries 3
39 LoginGraceTime 20
40
41 # OS security patches applied automatically; reboot at 04:30 if needed.
42 - path: /etc/apt/apt.conf.d/51gitbay-unattended
43 content: |
44 Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security"; };
45 Unattended-Upgrade::Automatic-Reboot "true";
46 Unattended-Upgrade::Automatic-Reboot-Time "04:30";
47 APT::Periodic::Update-Package-Lists "1";
48 APT::Periodic::Unattended-Upgrade "1";
49
50 # fail2ban watches the admin sshd for auth failures.
51 - path: /etc/fail2ban/jail.d/gitbay.conf
52 content: |
53 [sshd]
54 enabled = true
55 port = 2222
56 backend = systemd
57 maxretry = 5
58 bantime = 1h
59
60 # Heartbeat: post disk/service/cert status to a webhook if one is set in
61 # /etc/gitbay/monitor.url. Silent when the file is absent.
62 - path: /usr/local/bin/gitbay-monitor.sh
63 permissions: "0755"
64 content: |
65 #!/bin/sh
66 set -eu
67 url_file=/etc/gitbay/monitor.url
68 [ -f "$url_file" ] || exit 0
69 url=$(cat "$url_file")
70 disk=$(df -P /var/lib/gitbay | awk 'NR==2{print $5}')
71 svc=$(systemctl is-active gitbayd || true)
72 # Days until the ACME cert expires, if autocert cached one.
73 cert=/var/lib/gitbay/autocert
74 exp="n/a"
75 if [ -d "$cert" ]; then
76 f=$(ls -1 "$cert" 2>/dev/null | grep -v acme_account | head -1 || true)
77 [ -n "$f" ] && exp=$(openssl x509 -enddate -noout -in "$cert/$f" 2>/dev/null | cut -d= -f2 || echo n/a)
78 fi
79 alert=""
80 [ "$svc" != "active" ] && alert="gitbayd is $svc; "
81 pct=$(echo "$disk" | tr -d '%')
82 [ "$pct" -ge 85 ] && alert="${alert}disk ${disk}; "
83 body=$(printf '{"disk":"%s","service":"%s","cert_expires":"%s","alert":"%s"}' "$disk" "$svc" "$exp" "$alert")
84 curl -fsS -m 10 -H 'Content-Type: application/json' -d "$body" "$url" >/dev/null 2>&1 || true
85
86 - path: /etc/systemd/system/gitbay-monitor.service
87 content: |
88 [Unit]
89 Description=gitbay host heartbeat
90 [Service]
91 Type=oneshot
92 ExecStart=/usr/local/bin/gitbay-monitor.sh
93
94 - path: /etc/systemd/system/gitbay-monitor.timer
95 content: |
96 [Unit]
97 Description=gitbay host heartbeat
98 [Timer]
99 OnCalendar=*-*-* *:00:00 UTC
100 Persistent=true
101 [Install]
102 WantedBy=timers.target
103 - path: /etc/systemd/system/ssh.socket.d/override.conf
104 content: |
105 [Socket]
106 ListenStream=
107 ListenStream=2222
108
109 - path: /etc/gitbay/config.toml
110 permissions: "0640"
111 content: |
112 [server]
113 root = "/var/lib/gitbay"
114 site_url = "https://gitbay.org"
115
116 [ssh]
117 mode = "embedded"
118 port = 22
119
120 [http]
121 addr = ":443"
122 tls = "acme"
123 acme_email = "hello@gitbay.org"
124 acme_http_addr = ":80"
125
126 [web]
127 mode = "view_only"
128
129 [registration]
130 mode = "closed"
131
132 - path: /etc/systemd/system/gitbayd.service
133 content: |
134 [Unit]
135 Description=gitbay forge daemon
136 After=network-online.target
137 Wants=network-online.target
138
139 [Service]
140 User=gitbay
141 Group=gitbay
142 ExecStart=/usr/local/bin/gitbayd --config /etc/gitbay/config.toml serve
143 Restart=on-failure
144 RestartSec=3
145
146 # Bind 22/80/443 without root; no privilege escalation afterward.
147 AmbientCapabilities=CAP_NET_BIND_SERVICE
148 CapabilityBoundingSet=CAP_NET_BIND_SERVICE
149 NoNewPrivileges=yes
150 ProtectSystem=strict
151 ProtectHome=yes
152 ReadWritePaths=/var/lib/gitbay /var/backups/gitbay
153 PrivateTmp=yes
154 ProtectKernelTunables=yes
155 ProtectKernelModules=yes
156 ProtectControlGroups=yes
157 ProtectHostname=yes
158 ProtectClock=yes
159 ProtectKernelLogs=yes
160 RestrictSUIDSGID=yes
161 RestrictNamespaces=yes
162 RestrictRealtime=yes
163 LockPersonality=yes
164 MemoryDenyWriteExecute=yes
165 PrivateDevices=yes
166 # IPv4/IPv6 for listeners and outbound git/ssh; UNIX for the hook socket.
167 RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
168 # Allow only ordinary service syscalls; the daemon spawns git and ssh,
169 # so keep @process/@exec available (both are within @system-service).
170 SystemCallFilter=@system-service
171 SystemCallErrorNumber=EPERM
172 SystemCallArchitectures=native
173
174 [Install]
175 WantedBy=multi-user.target
176
177 - path: /usr/local/bin/gitbay-backup.sh
178 permissions: "0755"
179 content: |
180 #!/bin/sh
181 # Nightly consistent backup; keeps the last 7 locally.
182 # To ship offsite, add an rclone/s3 upload of $out here.
183 set -eu
184 dir=/var/backups/gitbay
185 out="$dir/gitbay-$(date -u +%Y%m%d-%H%M%S).tar.gz"
186 /usr/local/bin/gitbayd --config /etc/gitbay/config.toml admin backup --out "$out"
187 ls -1t "$dir"/gitbay-*.tar.gz | tail -n +8 | xargs -r rm --
188
189 - path: /etc/systemd/system/gitbay-backup.service
190 content: |
191 [Unit]
192 Description=gitbay nightly backup
193 [Service]
194 Type=oneshot
195 User=gitbay
196 ExecStart=/usr/local/bin/gitbay-backup.sh
197
198 - path: /etc/systemd/system/gitbay-backup.timer
199 content: |
200 [Unit]
201 Description=gitbay nightly backup
202 [Timer]
203 OnCalendar=*-*-* 09:00:00 UTC
204 RandomizedDelaySec=15m
205 Persistent=true
206 [Install]
207 WantedBy=timers.target
208
209 - path: /etc/systemd/system/gitbay-gc.service
210 content: |
211 [Unit]
212 Description=gitbay weekly repository maintenance
213 [Service]
214 Type=oneshot
215 User=gitbay
216 ExecStart=/usr/local/bin/gitbayd --config /etc/gitbay/config.toml admin gc
217
218 - path: /etc/systemd/system/gitbay-gc.timer
219 content: |
220 [Unit]
221 Description=gitbay weekly repository maintenance
222 [Timer]
223 OnCalendar=Sun *-*-* 07:00:00 UTC
224 RandomizedDelaySec=30m
225 Persistent=true
226 [Install]
227 WantedBy=timers.target
228
229runcmd:
230 - adduser --system --group --home /var/lib/gitbay --shell /usr/sbin/nologin gitbay
231 - install -d -o gitbay -g gitbay -m 750 /var/lib/gitbay /var/backups/gitbay
232 - chgrp gitbay /etc/gitbay/config.toml /etc/gitbay
233 - ufw allow 22/tcp
234 - ufw allow 80/tcp
235 - ufw allow 443/tcp
236 - ufw allow 2222/tcp
237 - ufw --force enable
238 - systemctl daemon-reload
239 - systemctl restart ssh.socket || systemctl restart ssh
240 - systemctl enable gitbayd gitbay-backup.timer gitbay-gc.timer gitbay-monitor.timer
241 - systemctl start gitbay-backup.timer gitbay-gc.timer gitbay-monitor.timer
242 - systemctl enable --now unattended-upgrades fail2ban