KL-1 Configurable CORS support

This commit is contained in:
mbecker20
2025-11-28 00:37:44 -08:00
parent a3a01f1625
commit 4a50b780a6
4 changed files with 81 additions and 10 deletions

View File

@@ -1,6 +1,7 @@
use std::{path::PathBuf, sync::OnceLock};
use anyhow::Context;
use axum::http::HeaderValue;
use colored::Colorize;
use config::ConfigLoader;
use environment_file::{
@@ -14,6 +15,7 @@ use komodo_client::entities::{
logger::LogConfig,
};
use noise::key::{RotatableKeyPair, SpkiPublicKey};
use tower_http::cors::CorsLayer;
/// Should call in startup to ensure Core errors without valid private key.
pub fn core_keys() -> &'static RotatableKeyPair {
@@ -89,6 +91,36 @@ pub fn periphery_public_keys() -> Option<&'static [SpkiPublicKey]> {
.as_deref()
}
/// Creates a CORS layer based on the Core configuration.
///
/// - If `cors_allowed_origins` is empty: Allows all origins (backward compatibility)
/// - If `cors_allowed_origins` is set: Only allows the specified origins
/// - Methods and headers are always allowed (Any)
/// - Credentials are only allowed if `cors_allow_credentials` is true
pub fn cors_layer() -> CorsLayer {
let config = core_config();
let allowed_origins = if config.cors_allowed_origins.is_empty() {
vec![HeaderValue::from_static("*")]
} else {
config
.cors_allowed_origins
.iter()
.filter_map(|origin| {
HeaderValue::from_str(origin)
.inspect_err(|e| {
warn!("Invalid CORS allowed origin: {origin} | {e:?}")
})
.ok()
})
.collect()
};
CorsLayer::new()
.allow_origin(allowed_origins)
.allow_methods(tower_http::cors::Any)
.allow_headers(tower_http::cors::Any)
.allow_credentials(config.cors_allow_credentials)
}
pub fn core_config() -> &'static CoreConfig {
static CORE_CONFIG: OnceLock<CoreConfig> = OnceLock::new();
CORE_CONFIG.get_or_init(|| {
@@ -281,6 +313,12 @@ pub fn core_config() -> &'static CoreConfig {
.komodo_frontend_path
.unwrap_or(config.frontend_path),
jwt_ttl: env.komodo_jwt_ttl.unwrap_or(config.jwt_ttl),
cors_allowed_origins: env
.komodo_cors_allowed_origins
.unwrap_or(config.cors_allowed_origins),
cors_allow_credentials: env
.komodo_cors_allow_credentials
.unwrap_or(config.cors_allow_credentials),
sync_directory: env
.komodo_sync_directory
.unwrap_or(config.sync_directory),

View File

@@ -8,10 +8,7 @@ use std::{net::SocketAddr, str::FromStr};
use anyhow::Context;
use axum::{Router, routing::get};
use axum_server::{Handle, tls_rustls::RustlsConfig};
use tower_http::{
cors::{Any, CorsLayer},
services::{ServeDir, ServeFile},
};
use tower_http::services::{ServeDir, ServeFile};
use tracing::Instrument;
use crate::config::{core_config, core_keys};
@@ -108,12 +105,7 @@ async fn app() -> anyhow::Result<()> {
.nest("/ws", ws::router())
.nest("/client", ts_client::router())
.fallback_service(serve_frontend)
.layer(
CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any),
)
.layer(config::cors_layer())
.into_make_service();
let addr =

View File

@@ -218,6 +218,11 @@ pub struct Env {
/// Override `github_oauth.secret` from file
pub komodo_github_oauth_secret_file: Option<PathBuf>,
/// Override `cors_allowed_origins`
pub komodo_cors_allowed_origins: Option<Vec<String>>,
/// Override `cors_allow_credentials`
pub komodo_cors_allow_credentials: Option<bool>,
/// Override `database.uri`
#[serde(alias = "komodo_mongo_uri")]
pub komodo_database_uri: Option<String>,
@@ -511,6 +516,21 @@ pub struct CoreConfig {
#[serde(default)]
pub github_oauth: OauthCredentials,
// =======
// = CORS =
// =======
/// List of CORS allowed origins.
/// If empty, allows all origins (`*`).
/// Production setups should configure this explicitly.
/// Example: `["https://komodo.example.com", "https://app.example.com"]`.
#[serde(default)]
pub cors_allowed_origins: Vec<String>,
/// Tell CORS to allow credentials in requests.
/// Used if needed for authentication proxy.
#[serde(default)]
pub cors_allow_credentials: bool,
// ============
// = Webhooks =
// ============
@@ -757,6 +777,8 @@ impl Default for CoreConfig {
oidc_additional_audiences: Default::default(),
google_oauth: Default::default(),
github_oauth: Default::default(),
cors_allowed_origins: Default::default(),
cors_allow_credentials: Default::default(),
webhook_secret: Default::default(),
webhook_base_url: Default::default(),
logging: Default::default(),
@@ -853,6 +875,8 @@ impl CoreConfig {
id: empty_or_redacted(&config.github_oauth.id),
secret: empty_or_redacted(&config.github_oauth.id),
},
cors_allowed_origins: config.cors_allowed_origins,
cors_allow_credentials: config.cors_allow_credentials,
webhook_secret: empty_or_redacted(&config.webhook_secret),
webhook_base_url: config.webhook_base_url,
database: config.database.sanitized(),

View File

@@ -294,6 +294,23 @@ github_oauth.id = ""
## Required if github_oauth is enabled.
github_oauth.secret = ""
########
# CORS #
########
## Specifically set list of CORS allowed origins.
## If empty, allows all origins (`*`).
## Production setups should configure this explicitly.
## Env: KOMODO_CORS_ALLOWED_ORIGINS
## Default: empty
cors_allowed_origins = ["*"]
## Tell CORS to allow credentials in requests.
## Set true only if needed for authentication proxy.
## Env: KOMODO_CORS_ALLOW_CREDENTIALS
## Default: false
cors_allow_credentials = false
##################
# POLL INTERVALS #
##################