zlayer-overlay 0.14.2

Encrypted overlay networking for containers using boringtun userspace WireGuard
Documentation
//! Cheap TUN / `CAP_NET_ADMIN` capability probes.
//!
//! Single source of truth for the two syscall-level probes both the zlayer
//! daemon (`zlayer-agent`) and the unprivileged edge client use to decide
//! whether overlay networking is viable:
//!
//! - [`probe_tun_device_available`] — can `/dev/net/tun` be opened r/w?
//! - [`probe_has_cap_net_admin`] — is `CAP_NET_ADMIN` in the effective set?
//!
//! Both are intentionally cheap and non-destructive — a single `open(2)` whose
//! fd is dropped immediately, or one read of `/proc/self/status`. Neither
//! allocates any kernel resource (no TUN interface is created, no capability is
//! changed), so they are safe to call repeatedly. Non-Linux targets report a
//! fixed `false`, since the kernel features these probes target are Linux-only.

/// `true` if `CAP_NET_ADMIN` is present in the process's *effective* set
/// (the `CapEff:` line of `/proc/self/status`). Linux only; always `false` on
/// other targets.
#[cfg(target_os = "linux")]
#[must_use]
pub fn probe_has_cap_net_admin() -> bool {
    // CAP_NET_ADMIN is bit 12 in the Linux capability bitmask.
    // We need it in the EFFECTIVE set (`CapEff`), not just the bounding set
    // (`CapBnd`). A regular user process has full CapBnd by default but empty
    // CapPrm/CapEff — checking PR_CAPBSET_READ gives a false positive that
    // makes the daemon think it can create TUN/WG interfaces when it cannot.
    const CAP_NET_ADMIN_BIT: u64 = 1 << 12;
    let Ok(status) = std::fs::read_to_string("/proc/self/status") else {
        return false;
    };
    for line in status.lines() {
        if let Some(hex) = line.strip_prefix("CapEff:") {
            let trimmed = hex.trim();
            if let Ok(eff) = u64::from_str_radix(trimmed, 16) {
                return eff & CAP_NET_ADMIN_BIT != 0;
            }
            return false;
        }
    }
    false
}

#[cfg(not(target_os = "linux"))]
#[must_use]
pub fn probe_has_cap_net_admin() -> bool {
    false
}

/// `true` if `/dev/net/tun` can be opened r/w in non-blocking mode without
/// error. The fd is dropped immediately (no TUN interface is allocated). Linux
/// only; always `false` on other targets.
#[cfg(target_os = "linux")]
#[must_use]
pub fn probe_tun_device_available() -> bool {
    use std::os::unix::fs::OpenOptionsExt;

    // Opening /dev/net/tun without any ioctls is benign and does not allocate
    // a TUN interface. The fd is dropped immediately when this scope ends.
    // Any open error — missing device, no perms, kernel module not loaded,
    // FD exhaustion — means we can't actually use TUN. Treat as unavailable.
    std::fs::OpenOptions::new()
        .read(true)
        .write(true)
        .custom_flags(libc::O_NONBLOCK)
        .open("/dev/net/tun")
        .is_ok()
}

#[cfg(not(target_os = "linux"))]
#[must_use]
pub fn probe_tun_device_available() -> bool {
    false
}

#[cfg(test)]
mod tests {
    #[test]
    fn tun_probe_is_total_and_does_not_panic() {
        // The concrete result depends on the host, but the call must be total.
        let _ = super::probe_tun_device_available();
    }

    #[test]
    fn cap_net_admin_probe_is_total_and_does_not_panic() {
        let _ = super::probe_has_cap_net_admin();
    }

    #[cfg(target_os = "linux")]
    #[test]
    fn probe_has_cap_net_admin_matches_cap_eff() {
        // Just confirm the probe agrees with what /proc/self/status reports.
        // The actual capability state depends on how the test is run (regular
        // user vs root vs setcap'd binary), but the probe MUST agree with the
        // CapEff line — that's the whole point of the bug fix.
        let status = std::fs::read_to_string("/proc/self/status").unwrap();
        let cap_eff_line = status
            .lines()
            .find(|l| l.starts_with("CapEff:"))
            .expect("CapEff: present in /proc/self/status");
        let hex = cap_eff_line.trim_start_matches("CapEff:").trim();
        let eff: u64 = u64::from_str_radix(hex, 16).unwrap();
        let expected = (eff & (1u64 << 12)) != 0;
        assert_eq!(super::probe_has_cap_net_admin(), expected);
    }
}