Codex Handbook
network-proxy/src/connect_policy.rs 157 lines
use crate::policy::is_non_public_ip;use crate::state::NetworkProxyState;use rama_core::Service;use rama_core::error::BoxError;use rama_core::error::ErrorExt as _;use rama_core::error::OpaqueError;use rama_core::extensions::ExtensionsMut;use rama_net::address::ProxyAddress;use rama_net::client::EstablishedClientConnection;use rama_net::transport::TryRefIntoTransportContext;use rama_tcp::TcpStream;use rama_tcp::client::TcpStreamConnector;use rama_tcp::client::service::TcpConnector;use std::io;use std::net::SocketAddr;use std::sync::Arc;#[derive(Clone)]pub(crate) struct TargetCheckedTcpConnector {    policy: TargetPolicy,}impl TargetCheckedTcpConnector {    pub(crate) fn new(state: Arc<NetworkProxyState>) -> Self {        Self {            policy: TargetPolicy::State(state),        }    }    pub(crate) fn from_allow_local_binding(allow_local_binding: bool) -> Self {        Self {            policy: TargetPolicy::Config {                allow_local_binding,            },        }    }}impl<Input> Service<Input> for TargetCheckedTcpConnectorwhere    Input: TryRefIntoTransportContext + Send + ExtensionsMut + 'static,    Input::Error: Into<BoxError> + Send + Sync + 'static,{    type Output = EstablishedClientConnection<TcpStream, Input>;    type Error = BoxError;    async fn serve(&self, input: Input) -> Result<Self::Output, Self::Error> {        if input.extensions().get::<ProxyAddress>().is_some() {            return TcpConnector::new().serve(input).await;        }        TcpConnector::new()            .with_connector(TargetCheckedStreamConnector {                policy: self.policy.clone(),            })            .serve(input)            .await    }}#[derive(Clone)]struct TargetCheckedStreamConnector {    policy: TargetPolicy,}impl TcpStreamConnector for TargetCheckedStreamConnector {    type Error = BoxError;    async fn connect(&self, addr: SocketAddr) -> Result<TcpStream, Self::Error> {        if !self.policy.allow_local_binding().await? && is_non_public_ip(addr.ip()) {            return Err(io::Error::new(                io::ErrorKind::PermissionDenied,                "network target rejected by policy",            )            .into());        }        tokio::net::TcpStream::connect(addr)            .await            .map(TcpStream::from)            .map_err(Into::into)    }}#[derive(Clone)]enum TargetPolicy {    Config { allow_local_binding: bool },    State(Arc<NetworkProxyState>),}impl TargetPolicy {    async fn allow_local_binding(&self) -> Result<bool, BoxError> {        match self {            Self::Config {                allow_local_binding,            } => Ok(*allow_local_binding),            Self::State(state) => state.allow_local_binding().await.map_err(|err| {                let err: BoxError = err.into();                OpaqueError::from_boxed(err)                    .context("read network proxy config")                    .into_boxed()            }),        }    }}#[cfg(test)]mod tests {    use super::*;    use crate::config::NetworkProxySettings;    use crate::state::network_proxy_state_for_policy;    use rama_net::address::HostWithPort;    use std::net::Ipv4Addr;    use tokio::net::TcpListener;    #[tokio::test(flavor = "current_thread")]    async fn direct_connector_rejects_non_public_target_when_local_binding_disabled() {        let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0))            .await            .expect("bind local listener");        let target = listener.local_addr().expect("local addr");        let connector = TargetCheckedTcpConnector::new(Arc::new(network_proxy_state_for_policy(            NetworkProxySettings::default(),        )));        let request: rama_tcp::client::Request =            rama_tcp::client::Request::new(HostWithPort::from(target));        let err = Service::serve(&connector, request)            .await            .expect_err("local target should be rejected");        assert!(            format!("{err:?}").contains("network target rejected by policy"),            "unexpected error: {err:?}"        );    }    #[tokio::test(flavor = "current_thread")]    async fn direct_connector_allows_non_public_target_when_local_binding_enabled() {        let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0))            .await            .expect("bind local listener");        let target = listener.local_addr().expect("local addr");        let connector = TargetCheckedTcpConnector::new(Arc::new(network_proxy_state_for_policy(            NetworkProxySettings {                allow_local_binding: true,                ..NetworkProxySettings::default()            },        )));        let request: rama_tcp::client::Request =            rama_tcp::client::Request::new(HostWithPort::from(target));        let result = Service::serve(&connector, request).await;        assert!(result.is_ok(), "local target should be allowed: {result:?}");    }}