@@ -4547,6 +4547,7 @@ async fn prepare_docker_boundary_files(
45474547 let tls = generate_sandbox_tls_material ( session_id)
45484548 . map_err ( |error| Status :: internal ( format ! ( "generate Docker boundary TLS: {error}" ) ) ) ?;
45494549 let verification_keys = gateway_verification_keys ( & launch_authentication. verification_keys ) ?;
4550+ let host_gateway_ip = resolve_docker_supervisor_host_address ( docker, sandbox, config) . await ?;
45504551 let provisioning = isolation:: DockerBoundarySpec {
45514552 boundary_id : sandbox. id . clone ( ) ,
45524553 generation : launch_authentication
@@ -4575,7 +4576,7 @@ async fn prepare_docker_boundary_files(
45754576 // host-networked supervisor. This is normally loopback, but container
45764577 // CI reaches the gateway and host fixtures through the job
45774578 // container's bridge address.
4578- host_gateway_ip : docker_supervisor_host_address ( & config . supervisor_grpc_endpoint ) ,
4579+ host_gateway_ip,
45794580 workload_identity : workload_identity. clone ( ) ,
45804581 child_env : docker_child_environment ( sandbox) ,
45814582 }
@@ -5038,6 +5039,168 @@ fn docker_supervisor_host_address(grpc_endpoint: &str) -> Option<IpAddr> {
50385039 }
50395040}
50405041
5042+ fn docker_host_gateway_resolver_body (
5043+ sandbox : & DriverSandbox ,
5044+ config : & DockerDriverRuntimeConfig ,
5045+ ) -> ContainerCreateBody {
5046+ ContainerCreateBody {
5047+ image : Some ( config. supervisor_image_id . clone ( ) ) ,
5048+ user : Some ( format ! ( "{SUPERVISOR_UID}:{SUPERVISOR_GID}" ) ) ,
5049+ entrypoint : Some ( vec ! [ SUPERVISOR_IMAGE_CONTROL_BINARY_PATH . to_string( ) ] ) ,
5050+ cmd : Some ( vec ! [ "--help" . to_string( ) ] ) ,
5051+ labels : Some ( docker_auxiliary_container_labels (
5052+ sandbox,
5053+ config,
5054+ LABEL_ISOLATION_ROLE_STAGING ,
5055+ ) ) ,
5056+ host_config : Some ( HostConfig {
5057+ network_mode : Some ( "none" . to_string ( ) ) ,
5058+ extra_hosts : Some ( vec ! [ format!( "{HOST_OPEN_SHELL_INTERNAL}:host-gateway" ) ] ) ,
5059+ cap_drop : Some ( vec ! [ "ALL" . to_string( ) ] ) ,
5060+ security_opt : Some ( vec ! [ "no-new-privileges:true" . to_string( ) ] ) ,
5061+ readonly_rootfs : Some ( true ) ,
5062+ ..Default :: default ( )
5063+ } ) ,
5064+ ..Default :: default ( )
5065+ }
5066+ }
5067+
5068+ fn parse_docker_host_gateway_hosts ( contents : & [ u8 ] ) -> CoreResult < IpAddr > {
5069+ let text = std:: str:: from_utf8 ( contents) . map_err ( |error| {
5070+ Error :: config ( format ! (
5071+ "Docker host gateway hosts file is not UTF-8: {error}"
5072+ ) )
5073+ } ) ?;
5074+ let mut addresses = Vec :: new ( ) ;
5075+ for line in text. lines ( ) {
5076+ let fields = line
5077+ . split_once ( '#' )
5078+ . map_or ( line, |( entry, _) | entry)
5079+ . split_whitespace ( )
5080+ . collect :: < Vec < _ > > ( ) ;
5081+ if fields. len ( ) < 2 || !fields[ 1 ..] . contains ( & HOST_OPEN_SHELL_INTERNAL ) {
5082+ continue ;
5083+ }
5084+ let address = fields[ 0 ] . parse :: < IpAddr > ( ) . map_err ( |error| {
5085+ Error :: config ( format ! (
5086+ "Docker host gateway hosts entry has an invalid address: {error}"
5087+ ) )
5088+ } ) ?;
5089+ if !addresses. contains ( & address) {
5090+ addresses. push ( address) ;
5091+ }
5092+ }
5093+
5094+ let ipv4 = addresses
5095+ . iter ( )
5096+ . filter ( |address| address. is_ipv4 ( ) )
5097+ . copied ( )
5098+ . collect :: < Vec < _ > > ( ) ;
5099+ let address = match ipv4. as_slice ( ) {
5100+ [ address] => * address,
5101+ [ ] if addresses. len ( ) == 1 => addresses[ 0 ] ,
5102+ [ ] => {
5103+ return Err ( Error :: config (
5104+ "Docker host gateway resolver did not return one concrete address" ,
5105+ ) ) ;
5106+ }
5107+ _ => {
5108+ return Err ( Error :: config (
5109+ "Docker host gateway resolver returned ambiguous IPv4 addresses" ,
5110+ ) ) ;
5111+ }
5112+ } ;
5113+ let normalized = match address {
5114+ IpAddr :: V6 ( value) => value. to_ipv4_mapped ( ) . map_or ( address, IpAddr :: V4 ) ,
5115+ IpAddr :: V4 ( _) => address,
5116+ } ;
5117+ if address. is_unspecified ( )
5118+ || address. is_multicast ( )
5119+ || normalized == IpAddr :: V4 ( Ipv4Addr :: BROADCAST )
5120+ || normalized == IpAddr :: V4 ( Ipv4Addr :: new ( 169 , 254 , 169 , 254 ) )
5121+ {
5122+ return Err ( Error :: config ( format ! (
5123+ "Docker host gateway resolver returned unsafe address '{address}'"
5124+ ) ) ) ;
5125+ }
5126+ Ok ( address)
5127+ }
5128+
5129+ async fn resolve_docker_supervisor_host_address (
5130+ docker : & Docker ,
5131+ sandbox : & DriverSandbox ,
5132+ config : & DockerDriverRuntimeConfig ,
5133+ ) -> Result < Option < IpAddr > , Status > {
5134+ if let Some ( address) = docker_supervisor_host_address ( & config. supervisor_grpc_endpoint ) {
5135+ return Ok ( Some ( address) ) ;
5136+ }
5137+ let endpoint = Url :: parse ( & config. supervisor_grpc_endpoint ) . map_err ( |error| {
5138+ Status :: failed_precondition ( format ! ( "parse Docker supervisor endpoint: {error}" ) )
5139+ } ) ?;
5140+ if !matches ! ( endpoint. host( ) , Some ( url:: Host :: Domain ( name) ) if name. eq_ignore_ascii_case( HOST_DOCKER_INTERNAL ) )
5141+ {
5142+ return Ok ( None ) ;
5143+ }
5144+
5145+ let resolver_name = temp_extract_container_name ( ) ;
5146+ let created = docker
5147+ . create_container (
5148+ Some (
5149+ CreateContainerOptionsBuilder :: default ( )
5150+ . name ( resolver_name. as_str ( ) )
5151+ . build ( ) ,
5152+ ) ,
5153+ docker_host_gateway_resolver_body ( sandbox, config) ,
5154+ )
5155+ . await
5156+ . map_err ( |error| {
5157+ Status :: internal ( format ! ( "create Docker host gateway resolver: {error}" ) )
5158+ } ) ?;
5159+
5160+ let result = async {
5161+ docker
5162+ . start_container ( & created. id , None )
5163+ . await
5164+ . map_err ( |error| {
5165+ Status :: internal ( format ! ( "start Docker host gateway resolver: {error}" ) )
5166+ } ) ?;
5167+ let mut wait = docker. wait_container (
5168+ & created. id ,
5169+ None :: < bollard:: query_parameters:: WaitContainerOptions > ,
5170+ ) ;
5171+ let status = tokio:: time:: timeout ( Duration :: from_secs ( 10 ) , wait. next ( ) )
5172+ . await
5173+ . map_err ( |_| Status :: deadline_exceeded ( "Docker host gateway resolver timed out" ) ) ?
5174+ . ok_or_else ( || Status :: internal ( "Docker host gateway resolver wait ended" ) ) ?
5175+ . map_err ( |error| {
5176+ Status :: internal ( format ! ( "wait for Docker host gateway resolver: {error}" ) )
5177+ } ) ?;
5178+ if status. status_code != 0 {
5179+ return Err ( Status :: failed_precondition ( format ! (
5180+ "Docker host gateway resolver exited with status {}" ,
5181+ status. status_code
5182+ ) ) ) ;
5183+ }
5184+ let hosts = download_path_from_container ( docker, & created. id , "/etc/hosts" , true )
5185+ . await
5186+ . map_err ( |error| Status :: failed_precondition ( error. to_string ( ) ) ) ?;
5187+ parse_docker_host_gateway_hosts ( & hosts)
5188+ . map ( Some )
5189+ . map_err ( |error| Status :: failed_precondition ( error. to_string ( ) ) )
5190+ }
5191+ . await ;
5192+ let cleanup = docker
5193+ . remove_container (
5194+ & created. id ,
5195+ Some ( RemoveContainerOptionsBuilder :: default ( ) . force ( true ) . build ( ) ) ,
5196+ )
5197+ . await
5198+ . map_err ( |error| Status :: internal ( format ! ( "remove Docker host gateway resolver: {error}" ) ) ) ;
5199+ let address = result?;
5200+ cleanup?;
5201+ Ok ( address)
5202+ }
5203+
50415204async fn spawn_docker_control_process (
50425205 docker : & Docker ,
50435206 sandbox : & DriverSandbox ,
0 commit comments