deploy/gitbay-runner-builds.nft

v1.43.0
gitbay/deploy/gitbay-runner-builds.nft history · blame · raw

94 lines · 4067 bytes

 1#!/usr/sbin/nft -f
 2# Egress for CI builds by trust (#260). Installed as
 3# /etc/gitbay-runner/builds.nft by `make deploy-runner` and loaded by the
 4# runner unit's ExecStartPre (gitbay-runner.override.conf), after the
 5# cgroups it names exist.
 6#
 7# gitbay-runner-egress.nft cannot tell a build from its runner: both run
 8# as ci-runner. This table can. The runner starts every podman process
 9# for a build inside builds/trusted/build-<id> or
10# builds/untrusted/build-<id> under its service cgroup, and pasta,
11# which podman starts, inherits that cgroup; so every socket pasta opens
12# for a build carries it. The runner itself, its clone and its log
13# stream run in <service>/runner and never match here.
14#
15# nftables resolves a cgroup path to the cgroup's id when the table
16# loads. The runner's service cgroup is new on every start, so the drop-in
17# creates builds/trusted and builds/untrusted and loads this file on
18# every start, and the runner never recreates them. A table loaded
19# against an earlier start's cgroups matches nothing, and builds then
20# get only the uid table.
21#
22# The uid table still applies to builds; a packet must pass both. This
23# table only takes away. Both hook output with policy accept, so their
24# relative order does not matter.
25#
26# Trusted builds (a branch of the repository, with its secrets):
27#   internet          open, any port.
28#   host, public      22, 80 and 443: the forge at GITBAY_SSH
29#                     (169.254.1.2, which pasta translates to the public
30#                     address). hutch and orgo publish over 22.
31#   host, loopback    53 only: the host's resolver, where pasta forwards
32#                     a build's DNS when the host's nameserver is a
33#                     loopback address.
34#   private ranges    closed (RFC 1918, CGNAT, link-local, ULA).
35# Untrusted builds (a fork's merge request head, no secrets):
36#   internet          80 and 443 over TCP, and 53: enough to fetch
37#                     modules and packages, not to send mail or reach
38#                     ssh elsewhere.
39#   host              loopback 53 only. No forge: an untrusted build has
40#                     no key to use there, and a failing login from it
41#                     would count against the host's public address.
42#   private ranges    closed.
43# -isolation none builds run in the runner's own cgroup and get only the
44# uid table; such a runner must not take -untrusted.
45#
46# A host whose /etc/resolv.conf names a nameserver in a private range
47# needs that address let through here, or builds resolve nothing.
48#
49# The first line creates the table if it is missing, so the delete never
50# fails; the file then replaces it in one transaction.
51
52table inet gitbay_builds
53delete table inet gitbay_builds
54
55table inet gitbay_builds {
56	set private4 {
57		type ipv4_addr
58		flags interval
59		elements = { 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16 }
60	}
61
62	set private6 {
63		type ipv6_addr
64		flags interval
65		elements = { fc00::/7, fe80::/10 }
66	}
67
68	chain output {
69		type filter hook output priority filter; policy accept;
70		socket cgroupv2 level 4 "system.slice/gitbay-runner.service/builds/trusted" jump trusted
71		socket cgroupv2 level 4 "system.slice/gitbay-runner.service/builds/untrusted" jump untrusted
72	}
73
74	chain trusted {
75		oifname "lo" ip daddr 127.0.0.0/8 meta l4proto { tcp, udp } th dport 53 return
76		oifname "lo" ip6 daddr ::1 meta l4proto { tcp, udp } th dport 53 return
77		oifname "lo" ip daddr != 127.0.0.0/8 tcp dport { 22, 80, 443 } return
78		oifname "lo" ip6 daddr != ::1 tcp dport { 22, 80, 443 } return
79		oifname "lo" counter reject
80		ip daddr @private4 counter reject
81		ip6 daddr @private6 counter reject
82	}
83
84	chain untrusted {
85		oifname "lo" ip daddr 127.0.0.0/8 meta l4proto { tcp, udp } th dport 53 return
86		oifname "lo" ip6 daddr ::1 meta l4proto { tcp, udp } th dport 53 return
87		oifname "lo" counter reject
88		ip daddr @private4 counter reject
89		ip6 daddr @private6 counter reject
90		meta l4proto { tcp, udp } th dport 53 return
91		tcp dport { 80, 443 } return
92		counter reject
93	}
94}