@@ -22,7 +22,7 @@ use iroh_relay::{
22
22
use n0_future:: FutureExt ;
23
23
use serde:: { Deserialize , Serialize } ;
24
24
use tokio_rustls_acme:: { caches:: DirCache , AcmeConfig } ;
25
- use tracing:: debug;
25
+ use tracing:: { debug, warn } ;
26
26
use tracing_subscriber:: { prelude:: * , EnvFilter } ;
27
27
28
28
/// The default `http_bind_port` when using `--dev`.
@@ -181,6 +181,14 @@ enum AccessConfig {
181
181
Allowlist ( Vec < NodeId > ) ,
182
182
/// Allows everyone, except these nodes.
183
183
Denylist ( Vec < NodeId > ) ,
184
+ /// Performs a HTTP POST request to determine access for each node that connects to the relay.
185
+ ///
186
+ /// The string value is used as the URL template, where `{node_id}` will be replaced
187
+ /// with the hex-encoded node id that is connecting.
188
+ ///
189
+ /// To grant access, the HTTP endpoint must return a `200` response with `true` as the response text.
190
+ /// In all other cases, the node will be denied accesss.
191
+ Http ( String ) ,
184
192
}
185
193
186
194
impl From < AccessConfig > for iroh_relay:: server:: AccessConfig {
@@ -215,10 +223,59 @@ impl From<AccessConfig> for iroh_relay::server::AccessConfig {
215
223
. boxed ( )
216
224
} ) )
217
225
}
226
+ AccessConfig :: Http ( url_template) => {
227
+ let client = reqwest:: Client :: default ( ) ;
228
+ let url_template = Arc :: new ( url_template) ;
229
+ iroh_relay:: server:: AccessConfig :: Restricted ( Box :: new ( move |node_id| {
230
+ let client = client. clone ( ) ;
231
+ let url_template = url_template. clone ( ) ;
232
+ async move { http_access_check ( & client, & url_template, node_id) . await } . boxed ( )
233
+ } ) )
234
+ }
218
235
}
219
236
}
220
237
}
221
238
239
+ #[ tracing:: instrument( "http-access-check" , skip_all, fields( node_id=%node_id. fmt_short( ) ) ) ]
240
+ async fn http_access_check (
241
+ client : & reqwest:: Client ,
242
+ url_template : & str ,
243
+ node_id : NodeId ,
244
+ ) -> iroh_relay:: server:: Access {
245
+ use iroh_relay:: server:: Access ;
246
+ let url = url_template. replace ( "{node_id}" , & node_id. to_string ( ) ) ;
247
+ debug ! ( %url, "check relay access via HTTP POST" ) ;
248
+ let res = match client. post ( & url) . send ( ) . await {
249
+ Ok ( t) => t,
250
+ Err ( err) => {
251
+ warn ! ( "request failed: {err}" ) ;
252
+ return Access :: Deny ;
253
+ }
254
+ } ;
255
+ if res. status ( ) . is_success ( ) {
256
+ match res. text ( ) . await {
257
+ Err ( err) => {
258
+ warn ! ( "failed to read response: {err}" ) ;
259
+ Access :: Deny
260
+ }
261
+ Ok ( text) if text == "true" => {
262
+ debug ! ( "request succesfull: grant access" ) ;
263
+ Access :: Allow
264
+ }
265
+ Ok ( _) => {
266
+ warn ! ( "request succesfull but response text is not `true`: deny access" ) ;
267
+ Access :: Deny
268
+ }
269
+ }
270
+ } else {
271
+ debug ! (
272
+ "request returned non-success code {}: deny access" ,
273
+ res. status( )
274
+ ) ;
275
+ Access :: Deny
276
+ }
277
+ }
278
+
222
279
impl Config {
223
280
fn http_bind_addr ( & self ) -> SocketAddr {
224
281
self . http_bind_addr
0 commit comments