@@ -49,6 +49,9 @@ pub struct SandboxClient {
4949 ssh_handshake_skew_secs : u64 ,
5050 /// When non-empty, sandbox pods get this K8s secret mounted for mTLS to the server.
5151 client_tls_secret_name : String ,
52+ /// When non-empty, sandbox pods get `hostAliases` entries mapping
53+ /// `host.docker.internal` and `host.openshell.internal` to this IP.
54+ host_gateway_ip : String ,
5255}
5356
5457impl std:: fmt:: Debug for SandboxClient {
@@ -71,6 +74,7 @@ impl SandboxClient {
7174 ssh_handshake_secret : String ,
7275 ssh_handshake_skew_secs : u64 ,
7376 client_tls_secret_name : String ,
77+ host_gateway_ip : String ,
7478 ) -> Result < Self , KubeError > {
7579 let mut config = match kube:: Config :: incluster ( ) {
7680 Ok ( c) => c,
@@ -92,6 +96,7 @@ impl SandboxClient {
9296 ssh_handshake_secret,
9397 ssh_handshake_skew_secs,
9498 client_tls_secret_name,
99+ host_gateway_ip,
95100 } )
96101 }
97102
@@ -206,6 +211,7 @@ impl SandboxClient {
206211 self . ssh_handshake_secret ( ) ,
207212 self . ssh_handshake_skew_secs ( ) ,
208213 & self . client_tls_secret_name ,
214+ & self . host_gateway_ip ,
209215 ) ;
210216 let api = self . api ( ) ;
211217
@@ -759,6 +765,7 @@ fn sandbox_to_k8s_spec(
759765 ssh_handshake_secret : & str ,
760766 ssh_handshake_skew_secs : u64 ,
761767 client_tls_secret_name : & str ,
768+ host_gateway_ip : & str ,
762769) -> serde_json:: Value {
763770 let mut root = serde_json:: Map :: new ( ) ;
764771 if let Some ( spec) = spec {
@@ -787,6 +794,7 @@ fn sandbox_to_k8s_spec(
787794 ssh_handshake_skew_secs,
788795 & spec. environment ,
789796 client_tls_secret_name,
797+ host_gateway_ip,
790798 ) ,
791799 ) ;
792800 if !template. agent_socket . is_empty ( ) {
@@ -820,6 +828,7 @@ fn sandbox_to_k8s_spec(
820828 ssh_handshake_skew_secs,
821829 spec_env,
822830 client_tls_secret_name,
831+ host_gateway_ip,
823832 ) ,
824833 ) ;
825834 }
@@ -843,6 +852,7 @@ fn sandbox_template_to_k8s(
843852 ssh_handshake_skew_secs : u64 ,
844853 spec_environment : & std:: collections:: HashMap < String , String > ,
845854 client_tls_secret_name : & str ,
855+ host_gateway_ip : & str ,
846856) -> serde_json:: Value {
847857 if let Some ( pod_template) = struct_to_json ( & template. pod_template ) {
848858 return inject_pod_template (
@@ -859,6 +869,7 @@ fn sandbox_template_to_k8s(
859869 ssh_handshake_skew_secs,
860870 spec_environment,
861871 client_tls_secret_name,
872+ host_gateway_ip,
862873 ) ;
863874 }
864875
@@ -968,6 +979,17 @@ fn sandbox_template_to_k8s(
968979 ) ;
969980 }
970981
982+ // Add hostAliases so sandbox pods can reach the Docker host.
983+ if !host_gateway_ip. is_empty ( ) {
984+ spec. insert (
985+ "hostAliases" . to_string ( ) ,
986+ serde_json:: json!( [ {
987+ "ip" : host_gateway_ip,
988+ "hostnames" : [ "host.docker.internal" , "host.openshell.internal" ]
989+ } ] ) ,
990+ ) ;
991+ }
992+
971993 let mut template_value = serde_json:: Map :: new ( ) ;
972994 if !metadata. is_empty ( ) {
973995 template_value. insert ( "metadata" . to_string ( ) , serde_json:: Value :: Object ( metadata) ) ;
@@ -997,6 +1019,7 @@ fn inject_pod_template(
9971019 ssh_handshake_skew_secs : u64 ,
9981020 spec_environment : & std:: collections:: HashMap < String , String > ,
9991021 client_tls_secret_name : & str ,
1022+ host_gateway_ip : & str ,
10001023) -> serde_json:: Value {
10011024 let Some ( spec) = pod_template
10021025 . get_mut ( "spec" )
@@ -1012,6 +1035,17 @@ fn inject_pod_template(
10121035 ) ;
10131036 }
10141037
1038+ // Add hostAliases so sandbox pods can reach the Docker host.
1039+ if !host_gateway_ip. is_empty ( ) {
1040+ spec. insert (
1041+ "hostAliases" . to_string ( ) ,
1042+ serde_json:: json!( [ {
1043+ "ip" : host_gateway_ip,
1044+ "hostnames" : [ "host.docker.internal" , "host.openshell.internal" ]
1045+ } ] ) ,
1046+ ) ;
1047+ }
1048+
10151049 // Inject TLS volume at the pod spec level.
10161050 if !client_tls_secret_name. is_empty ( ) {
10171051 let volumes = spec
@@ -1806,6 +1840,7 @@ mod tests {
18061840 300 ,
18071841 & std:: collections:: HashMap :: new ( ) ,
18081842 "" ,
1843+ "" ,
18091844 ) ;
18101845
18111846 assert_eq ! (
@@ -1851,6 +1886,7 @@ mod tests {
18511886 300 ,
18521887 & std:: collections:: HashMap :: new ( ) ,
18531888 "" ,
1889+ "" ,
18541890 ) ;
18551891
18561892 let limits = & pod_template[ "spec" ] [ "containers" ] [ 0 ] [ "resources" ] [ "limits" ] ;
@@ -1910,6 +1946,7 @@ mod tests {
19101946 300 ,
19111947 & std:: collections:: HashMap :: new ( ) ,
19121948 "" ,
1949+ "" ,
19131950 ) ;
19141951
19151952 assert_eq ! (
@@ -1921,4 +1958,116 @@ mod tests {
19211958 serde_json:: json!( GPU_RESOURCE_QUANTITY )
19221959 ) ;
19231960 }
1961+
1962+ #[ test]
1963+ fn host_aliases_injected_when_gateway_ip_set ( ) {
1964+ let pod_template = sandbox_template_to_k8s (
1965+ & SandboxTemplate :: default ( ) ,
1966+ false ,
1967+ "openshell/sandbox:latest" ,
1968+ "" ,
1969+ "sandbox-id" ,
1970+ "sandbox-name" ,
1971+ "https://gateway.example.com" ,
1972+ "0.0.0.0:2222" ,
1973+ "secret" ,
1974+ 300 ,
1975+ & std:: collections:: HashMap :: new ( ) ,
1976+ "" ,
1977+ "172.17.0.1" ,
1978+ ) ;
1979+
1980+ let host_aliases = pod_template[ "spec" ] [ "hostAliases" ]
1981+ . as_array ( )
1982+ . expect ( "hostAliases should exist" ) ;
1983+ assert_eq ! ( host_aliases. len( ) , 1 ) ;
1984+ assert_eq ! ( host_aliases[ 0 ] [ "ip" ] , "172.17.0.1" ) ;
1985+ let hostnames = host_aliases[ 0 ] [ "hostnames" ]
1986+ . as_array ( )
1987+ . expect ( "hostnames should exist" ) ;
1988+ assert ! ( hostnames. contains( & serde_json:: json!( "host.docker.internal" ) ) ) ;
1989+ assert ! ( hostnames. contains( & serde_json:: json!( "host.openshell.internal" ) ) ) ;
1990+ }
1991+
1992+ #[ test]
1993+ fn host_aliases_not_injected_when_gateway_ip_empty ( ) {
1994+ let pod_template = sandbox_template_to_k8s (
1995+ & SandboxTemplate :: default ( ) ,
1996+ false ,
1997+ "openshell/sandbox:latest" ,
1998+ "" ,
1999+ "sandbox-id" ,
2000+ "sandbox-name" ,
2001+ "https://gateway.example.com" ,
2002+ "0.0.0.0:2222" ,
2003+ "secret" ,
2004+ 300 ,
2005+ & std:: collections:: HashMap :: new ( ) ,
2006+ "" ,
2007+ "" ,
2008+ ) ;
2009+
2010+ assert ! (
2011+ pod_template[ "spec" ] [ "hostAliases" ] . is_null( ) ,
2012+ "hostAliases should not be present when host_gateway_ip is empty"
2013+ ) ;
2014+ }
2015+
2016+ #[ test]
2017+ fn host_aliases_injected_in_custom_pod_template ( ) {
2018+ let template = SandboxTemplate {
2019+ pod_template : Some ( Struct {
2020+ fields : [ (
2021+ "spec" . to_string ( ) ,
2022+ Value {
2023+ kind : Some ( Kind :: StructValue ( Struct {
2024+ fields : [ (
2025+ "containers" . to_string ( ) ,
2026+ Value {
2027+ kind : Some ( Kind :: ListValue ( prost_types:: ListValue {
2028+ values : vec ! [ Value {
2029+ kind: Some ( Kind :: StructValue ( Struct {
2030+ fields: [ (
2031+ "name" . to_string( ) ,
2032+ string_value( "agent" ) ,
2033+ ) ]
2034+ . into_iter( )
2035+ . collect( ) ,
2036+ } ) ) ,
2037+ } ] ,
2038+ } ) ) ,
2039+ } ,
2040+ ) ]
2041+ . into_iter ( )
2042+ . collect ( ) ,
2043+ } ) ) ,
2044+ } ,
2045+ ) ]
2046+ . into_iter ( )
2047+ . collect ( ) ,
2048+ } ) ,
2049+ ..SandboxTemplate :: default ( )
2050+ } ;
2051+
2052+ let pod_template = sandbox_template_to_k8s (
2053+ & template,
2054+ false ,
2055+ "openshell/sandbox:latest" ,
2056+ "" ,
2057+ "sandbox-id" ,
2058+ "sandbox-name" ,
2059+ "https://gateway.example.com" ,
2060+ "0.0.0.0:2222" ,
2061+ "secret" ,
2062+ 300 ,
2063+ & std:: collections:: HashMap :: new ( ) ,
2064+ "" ,
2065+ "192.168.65.2" ,
2066+ ) ;
2067+
2068+ let host_aliases = pod_template[ "spec" ] [ "hostAliases" ]
2069+ . as_array ( )
2070+ . expect ( "hostAliases should exist in custom pod template" ) ;
2071+ assert_eq ! ( host_aliases[ 0 ] [ "ip" ] , "192.168.65.2" ) ;
2072+ }
19242073}
0 commit comments