1use std::net::SocketAddr;
9use std::sync::Arc;
10
11use boatramp_core::kv::KvStore;
12
13use crate::error::{Error, Result};
14
15pub async fn configure_auth(
20 signer: Option<&crate::config::AuthSignerConfig>,
21 private_key: Option<String>,
22 public_key: Option<String>,
23 options: &mut boatramp_server::ServerOptions,
24 kv: Arc<dyn KvStore>,
25) -> Result<boatramp_server::Auth> {
26 use boatramp_core::cose::{LocalSigner, Signer, TokenPublicKey};
27 if let Some(cfg) = signer {
30 let issuer = boatramp_server::signer::build_signer(&cfg.to_signer_config())
31 .await
32 .map_err(|e| Error::AuthPrivKey(e.to_string()))?;
33 let public = issuer.public_key();
34 options.issuer = Some(issuer);
35 return Ok(boatramp_server::Auth::with_key(public, kv));
36 }
37 if let Some(hex) = private_key {
38 let signer =
39 LocalSigner::from_private_hex(&hex).map_err(|e| Error::AuthPrivKey(e.to_string()))?;
40 let public = signer.public_key();
41 options.issuer = Some(Arc::new(signer) as Arc<dyn Signer>);
42 return Ok(boatramp_server::Auth::with_key(public, kv));
43 }
44 if let Some(hex) = public_key {
45 let public =
46 TokenPublicKey::from_hex(&hex).map_err(|e| Error::AuthPubKey(e.to_string()))?;
47 return Ok(boatramp_server::Auth::with_key(public, kv));
48 }
49 Ok(boatramp_server::Auth::disabled())
50}
51
52pub fn enforce_auth_bind(
56 addr: SocketAddr,
57 auth: &boatramp_server::Auth,
58 posture: &boatramp_core::security::SecurityPosture,
59) -> Result<()> {
60 if auth.is_disabled() {
61 if !addr.ip().is_loopback() && !posture.allow_unauthenticated_public_bind {
62 return Err(Error::UnauthenticatedPublicBind { addr });
63 }
64 tracing::warn!(
65 %addr,
66 "control-plane auth is DISABLED — do not expose this listener to an untrusted network"
67 );
68 }
69 Ok(())
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use boatramp_core::security::SecurityProfile;
76
77 #[test]
80 fn fail_closed_refuses_unauthenticated_public_bind() {
81 let disabled = boatramp_server::Auth::disabled();
82 let strict = SecurityProfile::MultiTenant.preset();
83 let dev = SecurityProfile::Dev.preset();
84 let public: SocketAddr = "0.0.0.0:8080".parse().unwrap();
85 let loopback: SocketAddr = "127.0.0.1:8080".parse().unwrap();
86
87 assert!(matches!(
89 enforce_auth_bind(public, &disabled, &strict),
90 Err(Error::UnauthenticatedPublicBind { .. })
91 ));
92 assert!(enforce_auth_bind(loopback, &disabled, &strict).is_ok());
94 assert!(enforce_auth_bind(public, &disabled, &dev).is_ok());
96 }
97}