use anyhow::{anyhow, Result};
use std::fs;
const CAP_NET_ADMIN: u64 = 12;
const CAP_NET_ADMIN_HINT: &str = "CAP_NET_ADMIN is not held — boringtun cannot open /dev/net/tun. \
Fix with one of:\n \
setcap cap_net_admin+ep $(which innisfree)\n \
systemd: AmbientCapabilities=CAP_NET_ADMIN in the unit file";
pub fn require_cap_net_admin() -> Result<()> {
if has_cap_net_admin()? {
Ok(())
} else {
Err(anyhow!(CAP_NET_ADMIN_HINT))
}
}
pub fn platform_is_supported() -> Result<bool> {
let mut ok = true;
if has_cap_net_admin()? {
tracing::info!("CAP_NET_ADMIN is held — local Wireguard interface can be created");
} else {
tracing::warn!("{}", CAP_NET_ADMIN_HINT);
ok = false;
}
if std::env::var("DIGITALOCEAN_API_TOKEN").is_ok() {
tracing::info!("DIGITALOCEAN_API_TOKEN is set");
} else {
tracing::warn!("DIGITALOCEAN_API_TOKEN is not set — server provisioning will fail");
ok = false;
}
Ok(ok)
}
fn has_cap_net_admin() -> Result<bool> {
let status = fs::read_to_string("/proc/self/status")?;
for line in status.lines() {
if let Some(rest) = line.strip_prefix("CapEff:\t") {
let bits = u64::from_str_radix(rest.trim(), 16)?;
return Ok(bits & (1u64 << CAP_NET_ADMIN) != 0);
}
}
Ok(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cap_check_does_not_panic() {
let _ = has_cap_net_admin().unwrap();
}
#[test]
fn require_matches_check() {
let held = has_cap_net_admin().unwrap();
assert_eq!(held, require_cap_net_admin().is_ok());
}
}