fix(gateway): ignore non-client packets (#6086)

On the gateway, the only packets we are interested in receiving on the
TUN device are the ones destined for clients. To achieve this, we
specifically set routes for the reserved IP ranges on our interface.

Multicast packets as such as MLDV2 get sent to all packets and cause
unnecessary noise in our logs. Thus, as a defense-in-depth measure, we
drop all packets outside of the IP ranges reserved for our clients.
This commit is contained in:
Thomas Eizinger
2024-07-30 07:34:36 +01:00
committed by GitHub
parent be15afdabd
commit c6b576d1b1
3 changed files with 35 additions and 9 deletions

View File

@@ -9,6 +9,7 @@ use connlib_shared::messages::{
Offer, RelayId, ResourceId,
};
use connlib_shared::{DomainName, Error, Result, StaticSecret};
use ip_network::{Ipv4Network, Ipv6Network};
use ip_packet::{IpPacket, MutableIpPacket};
use secrecy::{ExposeSecret as _, Secret};
use snownet::{RelaySocket, ServerNode};
@@ -17,6 +18,16 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::time::{Duration, Instant};
use tun::Tun;
pub const IPV4_PEERS: Ipv4Network = match Ipv4Network::new(Ipv4Addr::new(100, 64, 0, 0), 11) {
Ok(n) => n,
Err(_) => unreachable!(),
};
pub const IPV6_PEERS: Ipv6Network =
match Ipv6Network::new(Ipv6Addr::new(0xfd00, 0x2021, 0x1111, 0, 0, 0, 0, 0), 107) {
Ok(n) => n,
Err(_) => unreachable!(),
};
const EXPIRE_RESOURCES_INTERVAL: Duration = Duration::from_secs(1);
impl GatewayTunnel {
@@ -152,6 +163,10 @@ impl GatewayState {
) -> Option<snownet::Transmit<'s>> {
let dst = packet.destination();
if !is_client(dst) {
return None;
}
let Some(peer) = self.peers.peer_by_ip_mut(dst) else {
tracing::warn!(%dst, "Couldn't find connection by IP");
@@ -406,3 +421,20 @@ impl GatewayState {
self.node.update_relays(to_remove, &to_add, now);
}
}
fn is_client(dst: IpAddr) -> bool {
match dst {
IpAddr::V4(v4) => IPV4_PEERS.contains(v4),
IpAddr::V6(v6) => IPV6_PEERS.contains(v6),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mldv2_routers_are_not_clients() {
assert!(!is_client("ff02::16".parse().unwrap()))
}
}

View File

@@ -52,7 +52,7 @@ pub type GatewayTunnel = Tunnel<GatewayState>;
pub type ClientTunnel = Tunnel<ClientState>;
pub use client::{ClientState, Request};
pub use gateway::GatewayState;
pub use gateway::{GatewayState, IPV4_PEERS, IPV6_PEERS};
/// [`Tunnel`] glues together connlib's [`Io`] component and the respective (pure) state of a client or gateway.
///

View File

@@ -4,11 +4,10 @@ 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_tunnel::GatewayTunnel;
use firezone_tunnel::{GatewayTunnel, IPV4_PEERS, IPV6_PEERS};
use futures::channel::mpsc;
use futures::{future, StreamExt, TryFutureExt};
use ip_network::{Ipv4Network, Ipv6Network};
use phoenix_channel::PhoenixChannel;
use secrecy::{Secret, SecretString};
use std::convert::Infallible;
@@ -25,8 +24,6 @@ mod eventloop;
mod messages;
const ID_PATH: &str = "/var/lib/firezone/gateway_id";
const PEERS_IPV4: &str = "100.64.0.0/11";
const PEERS_IPV6: &str = "fd00:2021:1111::/107";
#[tokio::main]
async fn main() {
@@ -143,10 +140,7 @@ async fn update_device_task(
}
if let Err(e) = tun_device
.set_routes(
vec![PEERS_IPV4.parse::<Ipv4Network>().unwrap()],
vec![PEERS_IPV6.parse::<Ipv6Network>().unwrap()],
)
.set_routes(vec![IPV4_PEERS], vec![IPV6_PEERS])
.await
{
tracing::warn!("Failed to set routes: {e:#}");