@@ -2080,6 +2080,44 @@ def disable_network(uid=None, gid=None):
with open("/proc/self/gid_map", "w") as f:
f.write("%s %s 1" % (gid, gid))
+def landlock_restrict_network():
+ """Block TCP bind/connect using Landlock LSM (ABI v4+, kernel 6.7+).
+ Gracefully skipped on older kernels. Stacks with disable_network()."""
+
+ NR_landlock_create_ruleset = 444
+ NR_landlock_restrict_self = 446
+
+ LANDLOCK_ACCESS_NET_BIND_TCP = 0x1
+ LANDLOCK_ACCESS_NET_CONNECT_TCP = 0x2
+
+ LANDLOCK_CREATE_RULESET_VERSION = 1
+
+ PR_SET_NO_NEW_PRIVS = 38
+
+ libc = ctypes.CDLL('libc.so.6')
+
+ libc.prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
+ abi = libc.syscall(NR_landlock_create_ruleset,
+ 0, 0,
+ LANDLOCK_CREATE_RULESET_VERSION)
+ if abi < 4:
+ logger.debug("System doesn't support disabling network via landlock")
+ return
+
+ net_access = LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP
+ attr = struct.pack("QQ", 0, net_access)
+ buf = ctypes.create_string_buffer(attr)
+ fd = libc.syscall(NR_landlock_create_ruleset, buf, len(attr), 0)
+ if fd < 0:
+ logger.debug("Failed to create landlock ruleset for network restriction")
+ return
+
+ r = libc.syscall(NR_landlock_restrict_self, fd, 0)
+ if r < 0:
+ logger.debug("Failed to landlock restrict_self")
+
+ os.close(fd)
+
def export_proxies(d):
from bb.fetch import get_fetcher_environment
""" export common proxies variables from datastore to environment """
Add landlock_restrict_network() which blocks TCP bind/connect using Landlock LSM (ABI v4+, kernel 6.7+). Designed to stack with the existing disable_network() namespace isolation, covering the case where disable_network() is skipped for non-local UIDs. unpriv landlock requires PR_NO_NEW_PRIVS. Any setcap/uid:ed binaries will silently execute without additional privileges for tasks. Gracefully fails on older kernels (ABI < 4). Signed-off-by: David Nyström <david.nystrom@est.tech> --- lib/bb/utils.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)