Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/aura-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ tracing-subscriber = "0.3.19"
futures = "0.3.31"
tokio-stream = { version = "0.1.18", features = ["net"] }
rustls = { version = "0.23.37", default-features = false, features = ["ring"] }
rustls-acme = { version = "0.15.1", features = ["tokio", "axum"] }
rustls-acme = { version = "0.15.4", features = ["tokio", "axum"] }
axum = "0.8.9"
rustls-pki-types = { version = "1.14.1", features = ["std"] }
prost = "0.14.3"
Expand Down
25 changes: 17 additions & 8 deletions crates/aura-server/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,25 @@ pub struct QuicServer {
impl QuicServer {
/// Create a new QUIC server based on the provided configuration.
pub fn new(bind_addr: SocketAddr, state: Arc<ServerState>) -> Result<Self> {
// We're bypassing ACME for now due to JSON parsing issues with Let's Encrypt Staging ("missing field token").
// Falling back to self-signed or manual TLS as requested.

// Still start the health-check listener so Fly.io doesn't think we're dead
Self::start_health_check_listener(&state);

// TLS source priority: explicit cert/key files > ACME (Let's Encrypt) > self-signed.
let server_config = if let (Some(cert_path), Some(key_path)) = (
&state.config.server.cert_path,
&state.config.server.key_path,
) {
info!("Loading custom TLS certificates from {:?}...", cert_path);
// Nothing else binds the health-check port in this mode.
Self::start_health_check_listener(&state);
Self::configure_manual_tls(cert_path, key_path)?
} else if let Some(domain) = state.config.server.acme_domain.clone() {
info!("Configuring ACME (Let's Encrypt) for domain {}...", domain);
// configure_acme spawns its own Axum server on acme_bind_port that
// serves the HTTP-01 challenge route AND a "/" health check, so we
// must NOT also call start_health_check_listener — both would bind
// acme_bind_port and the second bind would fail.
Self::configure_acme(&domain, &state)?
} else {
info!("ACME disabled or bypassed. Generating self-signed fallback...");
info!("No TLS cert or ACME domain configured. Generating self-signed fallback...");
Self::start_health_check_listener(&state);
Self::generate_self_signed_config()?
};

Expand All @@ -101,7 +106,6 @@ impl QuicServer {
})
}

#[allow(dead_code)]
/// Configure ACME (Let's Encrypt) for automated certificate management.
fn configure_acme(domain: &str, state: &Arc<ServerState>) -> Result<ServerConfig> {
let contact = state
Expand Down Expand Up @@ -132,6 +136,11 @@ impl QuicServer {
if let Some(url) = &state.config.server.acme_directory_url {
info!("[ACME] Using custom directory URL: {}", url);
acme_builder = acme_builder.directory(url);
} else {
// rustls-acme's builder defaults to the Let's Encrypt STAGING
// directory, whose certificates clients don't trust. No configured
// URL means production.
acme_builder = acme_builder.directory_lets_encrypt(true);
}

let mut acme_state = acme_builder.state();
Expand Down
3 changes: 2 additions & 1 deletion fly.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ primary_region = 'ord'
AURA_ACME_CACHE_PATH = '/data/acme'
AURA_BIND_ADDRESS = '0.0.0.0:8443'
AURA_DATABASE_PATH = '/data/aura.db'
AURA_ACME_DIRECTORY_URL = 'https://acme-staging-v02.api.letsencrypt.org/directory'
# Unset = Let's Encrypt production. Uncomment to test against staging:
# AURA_ACME_DIRECTORY_URL = 'https://acme-staging-v02.api.letsencrypt.org/directory'

[[mounts]]
source = 'aura_data'
Expand Down
Loading