@@ -174,6 +174,8 @@ use crate::l7::tls::{
174174} ;
175175use crate :: opa:: OpaEngine ;
176176use crate :: policy:: { NetworkMode , NetworkPolicy , ProxyPolicy , SandboxPolicy } ;
177+ #[ cfg( target_os = "linux" ) ]
178+ use crate :: proxy:: LoopbackProxyHandle ;
177179use crate :: proxy:: ProxyHandle ;
178180#[ cfg( target_os = "linux" ) ]
179181use crate :: sandbox:: linux:: netns:: NetworkNamespace ;
@@ -571,8 +573,10 @@ pub async fn run_sandbox(
571573 // the entrypoint process's /proc/net/tcp for identity binding.
572574 let entrypoint_pid = Arc :: new ( AtomicU32 :: new ( 0 ) ) ;
573575
574- let ( _proxy, denial_rx, bypass_denial_tx) = if matches ! ( policy. network. mode, NetworkMode :: Proxy )
575- {
576+ let ( _proxy, loopback_proxy, denial_rx, bypass_denial_tx) = if matches ! (
577+ policy. network. mode,
578+ NetworkMode :: Proxy
579+ ) {
576580 let proxy_policy = policy. network . proxy . as_ref ( ) . ok_or_else ( || {
577581 miette:: miette!( "Network mode is set to proxy but no proxy configuration was provided" )
578582 } ) ?;
@@ -617,21 +621,66 @@ pub async fn run_sandbox(
617621 let proxy_handle = ProxyHandle :: start_with_bind_addr (
618622 proxy_policy,
619623 bind_addr,
620- engine,
621- cache,
624+ engine. clone ( ) ,
625+ cache. clone ( ) ,
622626 entrypoint_pid. clone ( ) ,
623- tls_state,
624- inference_ctx,
627+ tls_state. clone ( ) ,
628+ inference_ctx. clone ( ) ,
625629 Some ( provider_credentials. clone ( ) ) ,
626630 Some ( policy_local_ctx. clone ( ) ) ,
627- denial_tx,
631+ denial_tx. clone ( ) ,
628632 )
629633 . await ?;
630- ( Some ( proxy_handle) , denial_rx, bypass_denial_tx)
634+
635+ #[ cfg( target_os = "linux" ) ]
636+ let loopback_proxy_handle = if let ( Some ( ns) , Some ( _upstream_addr) ) =
637+ ( netns. as_ref ( ) , bind_addr)
638+ {
639+ let Some ( netns_fd) = ns. ns_fd ( ) else {
640+ return Err ( miette:: miette!(
641+ "Managed loopback proxy requires a sandbox network namespace file descriptor"
642+ ) ) ;
643+ } ;
644+ let port = proxy_policy. http_addr . map_or ( 3128 , |addr| addr. port ( ) ) ;
645+ let listen_addr: SocketAddr = ( [ 127 , 0 , 0 , 1 ] , port) . into ( ) ;
646+ Some ( LoopbackProxyHandle :: start_in_netns (
647+ netns_fd,
648+ listen_addr,
649+ engine,
650+ cache,
651+ entrypoint_pid. clone ( ) ,
652+ tls_state,
653+ inference_ctx,
654+ Some ( provider_credentials. clone ( ) ) ,
655+ Some ( policy_local_ctx. clone ( ) ) ,
656+ denial_tx,
657+ ) ?)
658+ } else {
659+ None
660+ } ;
661+
662+ #[ cfg( not( target_os = "linux" ) ) ]
663+ let loopback_proxy_handle: Option < ( ) > = None ;
664+
665+ (
666+ Some ( proxy_handle) ,
667+ loopback_proxy_handle,
668+ denial_rx,
669+ bypass_denial_tx,
670+ )
631671 } else {
632- ( None , None , None )
672+ ( None , None , None , None )
633673 } ;
634674
675+ #[ cfg( target_os = "linux" ) ]
676+ let loopback_proxy_url = loopback_proxy. as_ref ( ) . map ( LoopbackProxyHandle :: proxy_url) ;
677+
678+ #[ cfg( not( target_os = "linux" ) ) ]
679+ let _ = & loopback_proxy;
680+
681+ #[ cfg( not( target_os = "linux" ) ) ]
682+ let loopback_proxy_url: Option < String > = None ;
683+
635684 // Spawn bypass detection monitor (Linux only, proxy mode only).
636685 // Reads /dev/kmsg for nftables log entries and emits structured
637686 // tracing events for direct connection attempts that bypass the proxy.
@@ -758,6 +807,7 @@ pub async fn run_sandbox(
758807 let policy_clone = policy. clone ( ) ;
759808 let workdir_clone = workdir. clone ( ) ;
760809 let proxy_url = ssh_proxy_url;
810+ let loopback_proxy_url = loopback_proxy_url. clone ( ) ;
761811 let netns_fd = ssh_netns_fd;
762812 let ca_paths = ca_file_paths. clone ( ) ;
763813 let provider_credentials_clone = provider_credentials. clone ( ) ;
@@ -772,6 +822,7 @@ pub async fn run_sandbox(
772822 workdir_clone,
773823 netns_fd,
774824 proxy_url,
825+ loopback_proxy_url,
775826 ca_paths,
776827 provider_credentials_clone,
777828 )
@@ -838,6 +889,7 @@ pub async fn run_sandbox(
838889 interactive,
839890 & policy,
840891 netns. as_ref ( ) ,
892+ loopback_proxy_url. as_deref ( ) ,
841893 ca_file_paths. as_ref ( ) ,
842894 & provider_env,
843895 ) ?;
@@ -849,6 +901,7 @@ pub async fn run_sandbox(
849901 workdir. as_deref ( ) ,
850902 interactive,
851903 & policy,
904+ loopback_proxy_url. as_deref ( ) ,
852905 ca_file_paths. as_ref ( ) ,
853906 & provider_env,
854907 ) ?;
0 commit comments