Background
Sibling to #271 (SELinux on Fedora): rustnet has runtime Landlock confinement on Linux 5.13+ but lacks an out-of-the-box MAC profile on Ubuntu/Debian. AppArmor is the right vehicle there — Ubuntu enforces it by default, and a profile installed by the .deb would tighten what rustnet can do even before Landlock applies.
Why we need this on top of Landlock: Landlock's network-restriction primitive only covers TCP — UDP and raw sockets pass straight through. So even with our existing sandbox, a compromised rustnet could exfiltrate over UDP (DNS, QUIC, anything). AppArmor's network inet dgram rules give us real UDP egress control, which Landlock cannot provide today.
Scope
Add debian/apparmor/usr.bin.rustnet (or debian/rustnet.apparmor consumed by dh_apparmor) and wire it into the Debian package so the profile is installed to /etc/apparmor.d/usr.bin.rustnet and loaded on install.
The profile should:
- Capabilities:
capability net_raw, capability bpf, capability perfmon (and capability sys_admin for older kernels)
- DNS reverse-lookup egress (PTR queries): rustnet does PTR lookups on captured IPs via libc — allow UDP+TCP to the system resolver via
abstractions/nameservice and network inet dgram / network inet stream scoped to port 53 (or use peer-address rules to scope to the resolver in /etc/resolv.conf). Must work with both systemd-resolved's stub (127.0.0.53:53) and external resolvers
- Reads (config & runtime):
/proc/**
/usr/share/rustnet-monitor/services (port-name database installed by the .deb)
/etc/resolv.conf, /etc/nsswitch.conf, /etc/hosts, and the nameservice abstraction
- The binary itself, dynamic linker, system libs (use
abstractions/base)
- User config:
owner @{HOME}/.config/rustnet/config.yml r, and ./config.yml r,
- Reads (GeoIP databases — rustnet probes several paths):
/usr/share/GeoIP/** r,
/usr/local/share/GeoIP/** r,
/var/lib/GeoIP/** r,
owner @{HOME}/.local/share/rustnet/geoip/** r,
./GeoLite2-*.mmdb r, (cwd lookup — used during development; consider commenting it out by default)
- Writes (logging and capture export — paths chosen by the user):
- Default log directory:
./logs/ rw, and ./logs/rustnet_*.log rw, (rustnet creates logs/ in cwd today)
- User-chosen
--json-log path: owner @{HOME}/** rwk, (allow anywhere the user already owns) plus a system-wide tunable @{RUSTNET_LOG_DIR} defaulting to /var/log/rustnet/
- User-chosen
--pcap-export path: same handling, and remember the .connections.jsonl sidecar that rustnet writes next to the PCAP — the rule must cover both
- Document in SECURITY.md that to write logs outside
$HOME, users either edit the profile's tunable or use aa-complain for the session
- Network — deny everything except the DNS resolver (this is the whole point — Landlock can't block UDP):
- Deny
network inet stream and network inet dgram by default
- Whitelist only the DNS resolver via abstractions / explicit
peer=(addr=127.0.0.53) etc.
- Exec: allow
/usr/bin/wl-copy Px, (Wayland clipboard helper for the c key); deny everything else
- Ship in
complain mode initially; flip to enforce after a release of field testing
Acceptance criteria
The PR must include evidence of real-world end-to-end testing — not just "it builds". Include in the PR description: Ubuntu version + kernel tested on, commands run, and relevant dmesg / journalctl -k excerpts plus screenshots. Synthetic CI checks alone are not sufficient.
References
Background
Sibling to #271 (SELinux on Fedora): rustnet has runtime Landlock confinement on Linux 5.13+ but lacks an out-of-the-box MAC profile on Ubuntu/Debian. AppArmor is the right vehicle there — Ubuntu enforces it by default, and a profile installed by the
.debwould tighten what rustnet can do even before Landlock applies.Why we need this on top of Landlock: Landlock's network-restriction primitive only covers TCP — UDP and raw sockets pass straight through. So even with our existing sandbox, a compromised rustnet could exfiltrate over UDP (DNS, QUIC, anything). AppArmor's
network inet dgramrules give us real UDP egress control, which Landlock cannot provide today.Scope
Add
debian/apparmor/usr.bin.rustnet(ordebian/rustnet.apparmorconsumed bydh_apparmor) and wire it into the Debian package so the profile is installed to/etc/apparmor.d/usr.bin.rustnetand loaded on install.The profile should:
capability net_raw,capability bpf,capability perfmon(andcapability sys_adminfor older kernels)abstractions/nameserviceandnetwork inet dgram/network inet streamscoped to port 53 (or use peer-address rules to scope to the resolver in/etc/resolv.conf). Must work with both systemd-resolved's stub (127.0.0.53:53) and external resolvers/proc/**/usr/share/rustnet-monitor/services(port-name database installed by the .deb)/etc/resolv.conf,/etc/nsswitch.conf,/etc/hosts, and thenameserviceabstractionabstractions/base)owner @{HOME}/.config/rustnet/config.yml r,and./config.yml r,/usr/share/GeoIP/** r,/usr/local/share/GeoIP/** r,/var/lib/GeoIP/** r,owner @{HOME}/.local/share/rustnet/geoip/** r,./GeoLite2-*.mmdb r,(cwd lookup — used during development; consider commenting it out by default)./logs/ rw,and./logs/rustnet_*.log rw,(rustnet createslogs/in cwd today)--json-logpath:owner @{HOME}/** rwk,(allow anywhere the user already owns) plus a system-wide tunable@{RUSTNET_LOG_DIR}defaulting to/var/log/rustnet/--pcap-exportpath: same handling, and remember the.connections.jsonlsidecar that rustnet writes next to the PCAP — the rule must cover both$HOME, users either edit the profile's tunable or useaa-complainfor the sessionnetwork inet streamandnetwork inet dgramby defaultpeer=(addr=127.0.0.53)etc./usr/bin/wl-copy Px,(Wayland clipboard helper for theckey); deny everything elsecomplainmode initially; flip toenforceafter a release of field testingAcceptance criteria
The PR must include evidence of real-world end-to-end testing — not just "it builds". Include in the PR description: Ubuntu version + kernel tested on, commands run, and relevant
dmesg/journalctl -kexcerpts plus screenshots. Synthetic CI checks alone are not sufficient..debinstalls/etc/apparmor.d/usr.bin.rustnetand reloads AppArmor on install/upgradeaa-statuson a real Ubuntu install (25.10 or 26.04) shows the profile loaded — include outputcomplainmode produces no DENIED entries injournalctl -k -g apparmor— include outputGeoLite2-City.mmdbat/usr/share/GeoIP/(orgeoipupdate-installed), captured connections show country info in the UI — include screenshot127.0.0.53:53) and an external resolver. Include screenshotrustnet --log-level debugwrites to./logs/rustnet_*.logwithout DENIED entries — include log path + apparmor log excerptrustnet --pcap-export /tmp/cap.pcapwrites bothcap.pcapandcap.pcap.connections.jsonl— includels -laof both filesaa-exec -p rustnet -- nc -u <ip> 1234(or equivalent), include the DENIED entry as proofapparmor_parser -Q /etc/apparmor.d/usr.bin.rustnetsyntax check in CI on a Ubuntu runnerReferences
debian/postinst(lines 14-16)wl-copyman dh_apparmor