Background
rustnet runs with file capabilities (cap_net_raw,cap_bpf,cap_perfmon on kernels ≥5.8) and does passive packet capture only. At runtime it already drops capabilities after libpcap initializes and applies a Landlock sandbox (see SECURITY.md and src/network/platform/linux/sandbox/landlock.rs) for filesystem confinement.
Landlock has a critical gap on the network side: its network-restriction primitive only covers TCP bind/connect — UDP and raw sockets pass straight through. That means even with our existing sandbox in place, a compromised rustnet could exfiltrate over DNS, QUIC, or any other UDP-based channel. We need a MAC profile (SELinux on Fedora, AppArmor on Ubuntu in a sibling issue) to actually deny non-DNS UDP egress and exec of other binaries — things Landlock cannot enforce. This narrows the blast radius of any RCE/parser bug in a tool that handles untrusted packet data.
Scope
Add a selinux/ directory containing a .te/.fc/.if policy module (or a single .cil file — author's preference) for rustnet, plus packaging hooks in rpm/rustnet.spec to install and load it on Fedora 42+.
The policy should:
- Define a
rustnet_t domain entered when /usr/bin/rustnet is executed
- Allow capabilities:
cap_net_raw, cap_bpf, cap_perfmon (and cap_sys_admin fallback for older kernels); raw socket access for packet capture
- Allow DNS reverse-lookup egress (rustnet does PTR queries on captured IPs via libc, so we need this — but only to the configured resolver, not arbitrary destinations):
- Use the
sysnet_dns_name_resolve() / corenet_udp_sendrecv_dns_port interfaces so UDP+TCP to port 53 reaches the system resolver
- Reads of
/etc/resolv.conf, /etc/nsswitch.conf, /etc/hosts
- This intentionally permits local stub resolver (
127.0.0.53:53 on systemd-resolved hosts) and external resolvers configured by the user — but nothing else on UDP/TCP
- Allow reads (config & runtime):
/proc/* (process enumeration for socket→PID mapping)
/usr/share/rustnet/services (port-name database installed by the RPM)
- User config:
~/.config/rustnet/config.yml (and $XDG_CONFIG_HOME variants) plus ./config.yml
- Allow reads (GeoIP databases — multiple locations rustnet probes in order):
/usr/share/GeoIP/** (canonical, populated by geoipupdate)
/usr/local/share/GeoIP/**
/var/lib/GeoIP/**
~/.local/share/rustnet/geoip/** (XDG_DATA_HOME fallback)
./GeoLite2-*.mmdb (current directory — used during development; consider gating behind a boolean)
- Allow writes (logging and capture export — paths chosen by the user):
- Default log directory created by
--log-level: ./logs/rustnet_*.log (rustnet creates logs/ in cwd today)
- Arbitrary path passed to
--json-log <FILE>
- Arbitrary path passed to
--pcap-export <FILE> plus the sidecar <FILE>.connections.jsonl
- Strategy: label these as
rustnet_log_t and allow rustnet_t to create/append to that type; for user-chosen paths, allow writes under user_home_t via a boolean (rustnet_can_write_user_home, default off) or require users to point output at /var/log/rustnet/ (preferred — add a tmpfiles.d snippet creating it with the right label)
- Deny by default (this is the point of the policy — Landlock can't do this):
- Outbound TCP
connect other than to the DNS resolver port
- Outbound UDP
sendto other than to the DNS resolver port (Landlock cannot gate UDP at all)
- Reading arbitrary
$HOME/** outside the config/GeoIP/log paths above
- Executing other binaries
- Loading kernel modules
- Be
permissive on first ship so it logs AVCs without breaking users; flip to enforcing in a follow-up after audit2allow review
Acceptance criteria
The PR must include evidence of real-world end-to-end testing — not just "it builds". Include in the PR description: distro + kernel version tested on, the commands run, and the relevant output / screenshots / ausearch excerpts. Synthetic CI checks alone are not sufficient.
References
Background
rustnet runs with file capabilities (
cap_net_raw,cap_bpf,cap_perfmonon kernels ≥5.8) and does passive packet capture only. At runtime it already drops capabilities after libpcap initializes and applies a Landlock sandbox (see SECURITY.md andsrc/network/platform/linux/sandbox/landlock.rs) for filesystem confinement.Landlock has a critical gap on the network side: its network-restriction primitive only covers TCP bind/connect — UDP and raw sockets pass straight through. That means even with our existing sandbox in place, a compromised rustnet could exfiltrate over DNS, QUIC, or any other UDP-based channel. We need a MAC profile (SELinux on Fedora, AppArmor on Ubuntu in a sibling issue) to actually deny non-DNS UDP egress and exec of other binaries — things Landlock cannot enforce. This narrows the blast radius of any RCE/parser bug in a tool that handles untrusted packet data.
Scope
Add a
selinux/directory containing a.te/.fc/.ifpolicy module (or a single.cilfile — author's preference) for rustnet, plus packaging hooks inrpm/rustnet.specto install and load it on Fedora 42+.The policy should:
rustnet_tdomain entered when/usr/bin/rustnetis executedcap_net_raw,cap_bpf,cap_perfmon(andcap_sys_adminfallback for older kernels); raw socket access for packet capturesysnet_dns_name_resolve()/corenet_udp_sendrecv_dns_portinterfaces so UDP+TCP to port 53 reaches the system resolver/etc/resolv.conf,/etc/nsswitch.conf,/etc/hosts127.0.0.53:53on systemd-resolved hosts) and external resolvers configured by the user — but nothing else on UDP/TCP/proc/*(process enumeration for socket→PID mapping)/usr/share/rustnet/services(port-name database installed by the RPM)~/.config/rustnet/config.yml(and$XDG_CONFIG_HOMEvariants) plus./config.yml/usr/share/GeoIP/**(canonical, populated bygeoipupdate)/usr/local/share/GeoIP/**/var/lib/GeoIP/**~/.local/share/rustnet/geoip/**(XDG_DATA_HOME fallback)./GeoLite2-*.mmdb(current directory — used during development; consider gating behind a boolean)--log-level:./logs/rustnet_*.log(rustnet createslogs/in cwd today)--json-log <FILE>--pcap-export <FILE>plus the sidecar<FILE>.connections.jsonlrustnet_log_tand allowrustnet_tto create/append to that type; for user-chosen paths, allow writes underuser_home_tvia a boolean (rustnet_can_write_user_home, default off) or require users to point output at/var/log/rustnet/(preferred — add atmpfiles.dsnippet creating it with the right label)connectother than to the DNS resolver portsendtoother than to the DNS resolver port (Landlock cannot gate UDP at all)$HOME/**outside the config/GeoIP/log paths abovepermissiveon first ship so it logs AVCs without breaking users; flip to enforcing in a follow-up afteraudit2allowreviewAcceptance criteria
The PR must include evidence of real-world end-to-end testing — not just "it builds". Include in the PR description: distro + kernel version tested on, the commands run, and the relevant output / screenshots /
ausearchexcerpts. Synthetic CI checks alone are not sufficient.make -f /usr/share/selinux/devel/Makefilesestatus+semodule -l | grep rustnetoutputrustnet --helpand a real capture session (5+ min, real traffic) run cleanly inpermissivemode — includeausearch -m avc -ts recentoutput showing no AVCsGeoLite2-City.mmdbplaced in/usr/share/GeoIP/(or installed viageoipupdate), captured connections show country info in the UI — include screenshot127.0.0.53:53) and with an external resolver. Include screenshotrustnet --log-level debugwrites to./logs/rustnet_*.log(or/var/log/rustnet/) without AVCs — include the log path +ausearchoutputrustnet --pcap-export /tmp/cap.pcapwrites bothcap.pcapandcap.pcap.connections.jsonl— includels -lZoutput showing the SELinux labelsnc -u <ip> 1234from inside therustnet_tdomain (e.g.runcon -t rustnet_t -- nc -u ...), include the resulting AVC denial as proofsemanage permissive -d rustnet_tReferences
rpm/rustnet.spec(lines 60-70)socket_connectfor TCP only; no UDP coverage as of kernel 6.x): https://docs.kernel.org/userspace-api/landlock.html