@@ -257,7 +257,7 @@ pub async fn run(args: ServerArgs) -> Result<()> {
257257 get ( api:: evaluations:: get_evaluations) ,
258258 )
259259 . with_state ( state)
260- . layer ( CorsLayer :: permissive ( ) )
260+ . layer ( build_cors_layer ( ) )
261261 . layer ( TraceLayer :: new_for_http ( ) ) ;
262262
263263 info ! ( "╔══════════════════════════════════════════════════════════════╗" ) ;
@@ -437,10 +437,20 @@ async fn proxy_to_challenge(
437437 }
438438 } ;
439439
440- let client = reqwest:: Client :: builder ( )
440+ let client = match reqwest:: Client :: builder ( )
441441 . timeout ( std:: time:: Duration :: from_secs ( 600 ) )
442442 . build ( )
443- . unwrap ( ) ;
443+ {
444+ Ok ( c) => c,
445+ Err ( e) => {
446+ error ! ( "Failed to create HTTP client: {}" , e) ;
447+ return (
448+ StatusCode :: INTERNAL_SERVER_ERROR ,
449+ "Failed to create HTTP client" ,
450+ )
451+ . into_response ( ) ;
452+ }
453+ } ;
444454
445455 let mut req_builder = client. request ( method, & url) ;
446456 for ( key, value) in headers. iter ( ) {
@@ -481,6 +491,43 @@ async fn proxy_to_challenge(
481491 }
482492}
483493
494+ /// Build CORS layer based on environment configuration.
495+ /// In development mode (DEVELOPMENT_MODE env var set), allows any origin.
496+ /// In production, only whitelisted origins are allowed.
497+ fn build_cors_layer ( ) -> CorsLayer {
498+ let allowed_origins = std:: env:: var ( "CORS_ALLOWED_ORIGINS" )
499+ . unwrap_or_else ( |_| "https://platform.network,https://chain.platform.network" . to_string ( ) ) ;
500+
501+ if allowed_origins == "*" || std:: env:: var ( "DEVELOPMENT_MODE" ) . is_ok ( ) {
502+ tracing:: warn!( "CORS allowing all origins - this should only be used in development!" ) ;
503+ CorsLayer :: new ( )
504+ . allow_origin ( tower_http:: cors:: Any )
505+ . allow_methods ( tower_http:: cors:: Any )
506+ . allow_headers ( tower_http:: cors:: Any )
507+ } else {
508+ let origins: Vec < axum:: http:: HeaderValue > = allowed_origins
509+ . split ( ',' )
510+ . filter_map ( |s| s. trim ( ) . parse ( ) . ok ( ) )
511+ . collect ( ) ;
512+
513+ CorsLayer :: new ( )
514+ . allow_origin ( origins)
515+ . allow_methods ( [
516+ axum:: http:: Method :: GET ,
517+ axum:: http:: Method :: POST ,
518+ axum:: http:: Method :: PUT ,
519+ axum:: http:: Method :: DELETE ,
520+ axum:: http:: Method :: OPTIONS ,
521+ ] )
522+ . allow_headers ( [
523+ axum:: http:: header:: AUTHORIZATION ,
524+ axum:: http:: header:: CONTENT_TYPE ,
525+ axum:: http:: header:: ACCEPT ,
526+ ] )
527+ . allow_credentials ( true )
528+ }
529+ }
530+
484531/// Start the container broker WebSocket server
485532///
486533/// This allows challenges to create sandboxed containers without direct Docker access.
@@ -507,11 +554,25 @@ async fn start_container_broker(port: u16) -> Result<()> {
507554 // WebSocket config with JWT auth
508555 let jwt_secret = std:: env:: var ( "BROKER_JWT_SECRET" ) . ok ( ) ;
509556
557+ // Determine if authentication should be required
558+ let is_dev_mode = std:: env:: var ( "DEVELOPMENT_MODE" ) . is_ok ( ) ;
559+ if is_dev_mode {
560+ warn ! ( "SECURITY WARNING: DEVELOPMENT_MODE is set - WebSocket authentication is DISABLED!" ) ;
561+ warn ! ( "DO NOT use DEVELOPMENT_MODE in production environments!" ) ;
562+ }
563+
564+ if jwt_secret. is_none ( ) && !is_dev_mode {
565+ warn ! ( "BROKER_JWT_SECRET not set - a random JWT secret will be generated" ) ;
566+ warn ! ( "This means JWT tokens will be invalidated on server restart!" ) ;
567+ warn ! ( "Set BROKER_JWT_SECRET environment variable for production use" ) ;
568+ }
569+
510570 let ws_config = WsConfig {
511571 bind_addr : format ! ( "0.0.0.0:{}" , port) ,
512572 jwt_secret,
513573 allowed_challenges : vec ! [ ] , // Allow all challenges
514574 max_connections_per_challenge : 10 ,
575+ allow_unauthenticated : is_dev_mode, // Only allow unauthenticated in dev mode
515576 } ;
516577
517578 info ! (
0 commit comments