fix(gateway): prevent routing loops (#6096)

In some weird conditions there might be routing loops in the gateway
too, so this fixes it and it doesn't do any harm.

Could be the cause behind [these
logs](https://github.com/firezone/firezone/issues/6067#issuecomment-2259081958)
This commit is contained in:
Gabi
2024-07-30 19:29:38 -03:00
committed by GitHub
parent 64d2d89542
commit 5841f297a5
6 changed files with 42 additions and 25 deletions

2
rust/Cargo.lock generated
View File

@@ -1791,8 +1791,10 @@ dependencies = [
"libc",
"netlink-packet-core",
"netlink-packet-route",
"nix 0.28.0",
"ring",
"rtnetlink",
"socket-factory",
"tokio",
"tracing",
"tracing-log",

View File

@@ -25,7 +25,9 @@ tokio = { workspace = true, features = ["macros"] }
libc = "0.2"
netlink-packet-core = { version = "0.7", default-features = false }
netlink-packet-route = { version = "0.19", default-features = false }
nix = { version = "0.28.0", features = ["socket"] }
rtnetlink = { workspace = true }
socket-factory = { workspace = true }
zbus = "4.4" # Can't use `zbus`'s `tokio` feature here, or it will break toast popups all the way over in `gui-client`.
[target.'cfg(windows)'.dependencies]

View File

@@ -1,3 +1,9 @@
use std::{io, net::SocketAddr};
use crate::FIREZONE_MARK;
use nix::sys::socket::{setsockopt, sockopt};
use socket_factory::{TcpSocket, UdpSocket};
const FIREZONE_DNS_CONTROL: &str = "FIREZONE_DNS_CONTROL";
#[derive(Clone, Copy, Debug)]
@@ -29,3 +35,15 @@ impl DnsControlMethod {
}
}
}
pub fn tcp_socket_factory(socket_addr: &SocketAddr) -> io::Result<TcpSocket> {
let socket = socket_factory::tcp(socket_addr)?;
setsockopt(&socket, sockopt::Mark, &FIREZONE_MARK)?;
Ok(socket)
}
pub fn udp_socket_factory(socket_addr: &SocketAddr) -> io::Result<UdpSocket> {
let socket = socket_factory::udp(socket_addr)?;
setsockopt(&socket, sockopt::Mark, &FIREZONE_MARK)?;
Ok(socket)
}

View File

@@ -180,9 +180,13 @@ impl ClientTunnel {
}
impl GatewayTunnel {
pub fn new(private_key: StaticSecret) -> std::io::Result<Self> {
pub fn new(
private_key: StaticSecret,
tcp_socket_factory: Arc<dyn SocketFactory<TcpSocket>>,
udp_socket_factory: Arc<dyn SocketFactory<UdpSocket>>,
) -> std::io::Result<Self> {
Ok(Self {
io: Io::new(Arc::new(socket_factory::tcp), Arc::new(socket_factory::udp))?,
io: Io::new(tcp_socket_factory, udp_socket_factory)?,
role_state: GatewayState::new(private_key, rand::random()),
write_buf: Box::new([0u8; DEFAULT_MTU + 20 + 16]),
ip4_read_buf: Box::new([0u8; MAX_UDP_SIZE]),

View File

@@ -3,7 +3,10 @@ use anyhow::{Context, Result};
use backoff::ExponentialBackoffBuilder;
use clap::Parser;
use connlib_shared::{get_user_agent, keypair, messages::Interface, LoginUrl, StaticSecret};
use firezone_bin_shared::{setup_global_subscriber, TunDeviceManager};
use firezone_bin_shared::{
linux::{tcp_socket_factory, udp_socket_factory},
setup_global_subscriber, TunDeviceManager,
};
use firezone_tunnel::{GatewayTunnel, IPV4_PEERS, IPV6_PEERS};
use futures::channel::mpsc;
@@ -98,7 +101,11 @@ async fn get_firezone_id(env_id: Option<String>) -> Result<String> {
}
async fn run(login: LoginUrl, private_key: StaticSecret) -> Result<Infallible> {
let mut tunnel = GatewayTunnel::new(private_key)?;
let mut tunnel = GatewayTunnel::new(
private_key,
Arc::new(tcp_socket_factory),
Arc::new(udp_socket_factory),
)?;
let portal = PhoenixChannel::connect(
Secret::new(login),
get_user_agent(None, env!("CARGO_PKG_VERSION")),
@@ -107,7 +114,7 @@ async fn run(login: LoginUrl, private_key: StaticSecret) -> Result<Infallible> {
ExponentialBackoffBuilder::default()
.with_max_elapsed_time(None)
.build(),
Arc::new(socket_factory::tcp),
Arc::new(tcp_socket_factory),
)?;
let (sender, receiver) = mpsc::channel::<Interface>(10);

View File

@@ -2,36 +2,20 @@
use super::TOKEN_ENV_KEY;
use anyhow::{bail, Result};
use firezone_bin_shared::{BUNDLE_ID, FIREZONE_MARK};
use nix::sys::socket::{setsockopt, sockopt};
use socket_factory::{TcpSocket, UdpSocket};
use std::{
io,
net::SocketAddr,
path::{Path, PathBuf},
};
use firezone_bin_shared::BUNDLE_ID;
use std::path::{Path, PathBuf};
// The Client currently must run as root to control DNS
// Root group and user are used to check file ownership on the token
const ROOT_GROUP: u32 = 0;
const ROOT_USER: u32 = 0;
pub(crate) fn tcp_socket_factory(socket_addr: &SocketAddr) -> io::Result<TcpSocket> {
let socket = socket_factory::tcp(socket_addr)?;
setsockopt(&socket, sockopt::Mark, &FIREZONE_MARK)?;
Ok(socket)
}
pub(crate) fn udp_socket_factory(socket_addr: &SocketAddr) -> io::Result<UdpSocket> {
let socket = socket_factory::udp(socket_addr)?;
setsockopt(&socket, sockopt::Mark, &FIREZONE_MARK)?;
Ok(socket)
}
pub(crate) fn default_token_path() -> PathBuf {
PathBuf::from("/etc").join(BUNDLE_ID).join("token")
}
pub use firezone_bin_shared::linux::{tcp_socket_factory, udp_socket_factory};
pub(crate) fn check_token_permissions(path: &Path) -> Result<()> {
let Ok(stat) = nix::sys::stat::fstatat(None, path, nix::fcntl::AtFlags::empty()) else {
// File doesn't exist or can't be read