mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-09 03:20:56 +00:00
Support base64-encoded PEM for ARC_JWT_PUBLIC_KEY and ARC_JWT_PRIVATE_KEY
Some deployment environments (e.g. container orchestrators) make it easier to pass secrets as single-line base64 strings rather than multi-line PEM. Both env vars now auto-detect the format: if the value starts with "-----" it's treated as raw PEM, otherwise base64-decoded. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b7f69a6b13
commit
d5b98af6d1
4 changed files with 23 additions and 4 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -141,6 +141,7 @@ dependencies = [
|
|||
"arc-util",
|
||||
"arc-workflows",
|
||||
"axum",
|
||||
"base64",
|
||||
"clap",
|
||||
"dirs",
|
||||
"http-body-util",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ import { getAppConfig } from "./lib/config.server";
|
|||
|
||||
const ARC_JWT_PRIVATE_KEY = process.env.ARC_JWT_PRIVATE_KEY;
|
||||
|
||||
function decodePemEnv(value: string): string {
|
||||
if (value.startsWith("-----")) return value;
|
||||
return Buffer.from(value, "base64").toString("utf-8");
|
||||
}
|
||||
|
||||
let cachedKey: CryptoKey | null = null;
|
||||
|
||||
async function getSigningKey(): Promise<CryptoKey> {
|
||||
|
|
@ -10,7 +15,7 @@ async function getSigningKey(): Promise<CryptoKey> {
|
|||
if (!ARC_JWT_PRIVATE_KEY) {
|
||||
throw new Error("ARC_JWT_PRIVATE_KEY environment variable is not set");
|
||||
}
|
||||
cachedKey = await importPKCS8(ARC_JWT_PRIVATE_KEY, "EdDSA");
|
||||
cachedKey = await importPKCS8(decodePemEnv(ARC_JWT_PRIVATE_KEY), "EdDSA");
|
||||
return cachedKey;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ dirs.workspace = true
|
|||
sqlx.workspace = true
|
||||
tower = "0.5"
|
||||
tokio-stream = { workspace = true, features = ["sync"] }
|
||||
base64.workspace = true
|
||||
jsonwebtoken.workspace = true
|
||||
tokio.workspace = true
|
||||
serde.workspace = true
|
||||
|
|
|
|||
|
|
@ -26,6 +26,16 @@ pub enum AuthMode {
|
|||
Disabled,
|
||||
}
|
||||
|
||||
/// Decode a PEM env var that may be raw PEM or base64-encoded PEM.
|
||||
fn decode_pem_env(name: &str, value: &str) -> String {
|
||||
if value.starts_with("-----") {
|
||||
return value.to_string();
|
||||
}
|
||||
let bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, value)
|
||||
.unwrap_or_else(|e| panic!("{name} is not valid PEM or base64: {e}"));
|
||||
String::from_utf8(bytes).unwrap_or_else(|e| panic!("{name} base64 decoded to invalid UTF-8: {e}"))
|
||||
}
|
||||
|
||||
/// Resolve the authentication mode from the API config section.
|
||||
///
|
||||
/// Call this once at startup before serving requests. Panics if the
|
||||
|
|
@ -39,13 +49,15 @@ pub fn resolve_auth_mode(api_config: &crate::server_config::ApiConfig) -> AuthMo
|
|||
AuthMode::Disabled
|
||||
}
|
||||
ApiAuthenticationStrategy::Jwt => {
|
||||
let pem = std::env::var("ARC_JWT_PUBLIC_KEY").unwrap_or_else(|_| {
|
||||
let raw = std::env::var("ARC_JWT_PUBLIC_KEY").unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"ARC_JWT_PUBLIC_KEY is not set. Either provide an Ed25519 public key in PEM \
|
||||
format or set authentication_strategy = \"insecure_disabled\" in \
|
||||
~/.arc/arc.toml to allow unauthenticated access (development only)."
|
||||
format (or base64-encoded PEM) or set authentication_strategy = \
|
||||
\"insecure_disabled\" in ~/.arc/arc.toml to allow unauthenticated access \
|
||||
(development only)."
|
||||
)
|
||||
});
|
||||
let pem = decode_pem_env("ARC_JWT_PUBLIC_KEY", &raw);
|
||||
let key = DecodingKey::from_ed_pem(pem.as_bytes())
|
||||
.expect("ARC_JWT_PUBLIC_KEY contains an invalid Ed25519 PEM public key");
|
||||
AuthMode::Jwt(Arc::new(key))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue