Skip to content

packaging(deb): ship an AppArmor profile that confines rustnet #272

@domcyrus

Description

@domcyrus

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.

  • .deb installs /etc/apparmor.d/usr.bin.rustnet and reloads AppArmor on install/upgrade
  • aa-status on a real Ubuntu install (25.10 or 26.04) shows the profile loaded — include output
  • Real 5+ minute capture session in complain mode produces no DENIED entries in journalctl -k -g apparmor — include output
  • GeoIP works on real system: with a GeoLite2-City.mmdb at /usr/share/GeoIP/ (or geoipupdate-installed), captured connections show country info in the UI — include screenshot
  • DNS PTR lookups work on real system: captured IPs resolve to hostnames in the UI — tested both with systemd-resolved (127.0.0.53:53) and an external resolver. Include screenshot
  • Logging works on real system: rustnet --log-level debug writes to ./logs/rustnet_*.log without DENIED entries — include log path + apparmor log excerpt
  • PCAP export works on real system: rustnet --pcap-export /tmp/cap.pcap writes both cap.pcap and cap.pcap.connections.jsonl — include ls -la of both files
  • Non-DNS UDP egress blocked in enforce mode on real system: flip the profile to enforce, run aa-exec -p rustnet -- nc -u <ip> 1234 (or equivalent), include the DENIED entry as proof
  • Documented in SECURITY.md and INSTALL.md
  • Profile passes apparmor_parser -Q /etc/apparmor.d/usr.bin.rustnet syntax check in CI on a Ubuntu runner

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is neededpackagingDistribution packaging (deb, rpm, AUR, etc.)securitySecurity-related changes (sandboxing, MAC, privileges)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions