1
1
use std:: { collections:: HashMap , sync:: Arc } ;
2
2
3
3
use anyhow:: Context ;
4
+ use futures_util:: {
5
+ future:: { BoxFuture , Shared } ,
6
+ FutureExt ,
7
+ } ;
8
+ use spin_factor_variables:: VariablesFactor ;
4
9
use spin_factor_wasi:: WasiFactor ;
5
- use spin_factors:: { Factor , FactorInstancePreparer , Result , SpinFactors } ;
6
- use spin_outbound_networking:: { AllowedHostsConfig , HostConfig , PortConfig , ALLOWED_HOSTS_KEY } ;
10
+ use spin_factors:: {
11
+ Factor , FactorInstancePreparer , InstancePreparers , PrepareContext , Result , SpinFactors ,
12
+ } ;
13
+ use spin_outbound_networking:: { AllowedHostsConfig , ALLOWED_HOSTS_KEY } ;
7
14
8
15
pub struct OutboundNetworkingFactor ;
9
16
@@ -17,70 +24,93 @@ impl Factor for OutboundNetworkingFactor {
17
24
app : & spin_factors:: App ,
18
25
_ctx : spin_factors:: ConfigureAppContext < Factors > ,
19
26
) -> Result < Self :: AppConfig > {
20
- let mut cfg = AppConfig :: default ( ) ;
21
- // TODO: resolve resolver resolution
22
- let resolver = Default :: default ( ) ;
23
- for component in app. components ( ) {
24
- if let Some ( hosts) = component. get_metadata ( ALLOWED_HOSTS_KEY ) ? {
25
- let allowed_hosts = AllowedHostsConfig :: parse ( & hosts, & resolver) ?;
26
- cfg. component_allowed_hosts
27
- . insert ( component. id ( ) . to_string ( ) , Arc :: new ( allowed_hosts) ) ;
28
- }
29
- }
30
- Ok ( cfg)
27
+ // Extract allowed_outbound_hosts for all components
28
+ let component_allowed_hosts = app
29
+ . components ( )
30
+ . map ( |component| {
31
+ Ok ( (
32
+ component. id ( ) . to_string ( ) ,
33
+ component
34
+ . get_metadata ( ALLOWED_HOSTS_KEY ) ?
35
+ . unwrap_or_default ( )
36
+ . into_boxed_slice ( )
37
+ . into ( ) ,
38
+ ) )
39
+ } )
40
+ . collect :: < Result < _ > > ( ) ?;
41
+ Ok ( AppConfig {
42
+ component_allowed_hosts,
43
+ } )
31
44
}
32
45
}
33
46
34
47
#[ derive( Default ) ]
35
48
pub struct AppConfig {
36
- component_allowed_hosts : HashMap < String , Arc < AllowedHostsConfig > > ,
49
+ component_allowed_hosts : HashMap < String , Arc < [ String ] > > ,
37
50
}
38
51
39
- pub struct InstancePreparer {
40
- allowed_hosts : Arc < AllowedHostsConfig > ,
41
- }
52
+ type AllowedHostsFuture = Shared < BoxFuture < ' static , Arc < anyhow:: Result < AllowedHostsConfig > > > > ;
42
53
43
- impl InstancePreparer {
44
- pub fn allowed_hosts ( & self ) -> & Arc < AllowedHostsConfig > {
45
- & self . allowed_hosts
46
- }
54
+ pub struct InstancePreparer {
55
+ allowed_hosts_future : AllowedHostsFuture ,
47
56
}
48
57
49
58
impl FactorInstancePreparer < OutboundNetworkingFactor > for InstancePreparer {
50
59
fn new < Factors : SpinFactors > (
51
- _factor : & OutboundNetworkingFactor ,
52
- app_component : & spin_factors:: AppComponent ,
53
- mut ctx : spin_factors:: PrepareContext < Factors > ,
60
+ ctx : PrepareContext < OutboundNetworkingFactor > ,
61
+ mut preparers : InstancePreparers < Factors > ,
54
62
) -> Result < Self > {
55
- let allowed_hosts = ctx
56
- . app_config :: < OutboundNetworkingFactor > ( ) ?
63
+ let hosts = ctx
64
+ . app_config ( )
57
65
. component_allowed_hosts
58
- . get ( app_component. id ( ) )
59
- . context ( "missing component" ) ?
60
- . clone ( ) ;
66
+ . get ( ctx. app_component ( ) . id ( ) )
67
+ . cloned ( )
68
+ . context ( "missing component allowed hosts" ) ?;
69
+ let resolver = preparers. get_mut :: < VariablesFactor > ( ) ?. resolver ( ) . clone ( ) ;
70
+ let allowed_hosts_future = async move {
71
+ let prepared = resolver. prepare ( ) . await ?;
72
+ AllowedHostsConfig :: parse ( & hosts, & prepared)
73
+ }
74
+ . map ( Arc :: new)
75
+ . boxed ( )
76
+ . shared ( ) ;
77
+ // let prepared_resolver = resolver.prepare().await?;
78
+ // let allowed_hosts = AllowedHostsConfig::parse(
79
+ // .context("missing component allowed hosts")?,
80
+ // &prepared_resolver,
81
+ // )?;
61
82
62
83
// Update Wasi socket allowed ports
63
- let wasi_preparer = ctx. instance_preparer_mut :: < WasiFactor > ( ) ?;
64
- match & * allowed_hosts {
65
- AllowedHostsConfig :: All => wasi_preparer. inherit_network ( ) ,
66
- AllowedHostsConfig :: SpecificHosts ( configs) => {
67
- for config in configs {
68
- if config. scheme ( ) . allows_any ( ) {
69
- match ( config. host ( ) , config. port ( ) ) {
70
- ( HostConfig :: Cidr ( ip_net) , PortConfig :: Any ) => {
71
- wasi_preparer. socket_allow_ports ( * ip_net, 0 , None )
72
- }
73
- _ => todo ! ( ) , // TODO: complete and validate against existing Network TriggerHooks
74
- }
84
+ let wasi_preparer = preparers. get_mut :: < WasiFactor > ( ) ?;
85
+ let hosts_future = allowed_hosts_future. clone ( ) ;
86
+ wasi_preparer. outbound_socket_addr_check ( move |addr| {
87
+ let hosts_future = hosts_future. clone ( ) ;
88
+ async move {
89
+ match & * hosts_future. await {
90
+ Ok ( allowed_hosts) => {
91
+ // TODO: verify this actually works...
92
+ spin_outbound_networking:: check_url ( & addr. to_string ( ) , "*" , allowed_hosts)
93
+ }
94
+ Err ( err) => {
95
+ // TODO: should this trap (somehow)?
96
+ tracing:: error!( %err, "allowed_outbound_hosts variable resolution failed" ) ;
97
+ false
75
98
}
76
99
}
77
100
}
78
- }
79
-
80
- Ok ( Self { allowed_hosts } )
101
+ } ) ;
102
+ Ok ( Self {
103
+ allowed_hosts_future,
104
+ } )
81
105
}
82
106
83
107
fn prepare ( self ) -> Result < <OutboundNetworkingFactor as Factor >:: InstanceState > {
84
108
Ok ( ( ) )
85
109
}
86
110
}
111
+
112
+ impl InstancePreparer {
113
+ pub async fn resolve_allowed_hosts ( & self ) -> Arc < anyhow:: Result < AllowedHostsConfig > > {
114
+ self . allowed_hosts_future . clone ( ) . await
115
+ }
116
+ }
0 commit comments