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 shared_authz: Option<SharedAuthz>,
26) -> Result<boatramp_server::Auth> {
27 use boatramp_core::cose::{LocalSigner, Signer, TokenPublicKey};
28 let with_shared = |auth: boatramp_server::Auth| match &shared_authz {
32 Some(s) => auth.with_shared_authz(s.backing.clone(), s.fence.clone()),
33 None => auth,
34 };
35 if let Some(cfg) = signer {
38 let issuer = boatramp_server::signer::build_signer(&cfg.to_signer_config())
39 .await
40 .map_err(|e| Error::AuthPrivKey(e.to_string()))?;
41 let public = issuer.public_key();
42 options.issuer = Some(issuer);
43 return Ok(with_shared(boatramp_server::Auth::with_key(public, kv)));
44 }
45 if let Some(hex) = private_key {
46 let signer =
47 LocalSigner::from_private_hex(&hex).map_err(|e| Error::AuthPrivKey(e.to_string()))?;
48 let public = signer.public_key();
49 options.issuer = Some(Arc::new(signer) as Arc<dyn Signer>);
50 return Ok(with_shared(boatramp_server::Auth::with_key(public, kv)));
51 }
52 if let Some(hex) = public_key {
53 let public =
54 TokenPublicKey::from_hex(&hex).map_err(|e| Error::AuthPubKey(e.to_string()))?;
55 return Ok(with_shared(boatramp_server::Auth::with_key(public, kv)));
56 }
57 Ok(boatramp_server::Auth::disabled())
58}
59
60pub struct SharedAuthz {
65 pub backing: Arc<dyn KvStore>,
68 pub fence: Arc<boatramp_core::cache_coherence::AuthzFence>,
70}
71
72pub fn enforce_auth_bind(
76 addr: SocketAddr,
77 auth: &boatramp_server::Auth,
78 posture: &boatramp_core::security::SecurityPosture,
79) -> Result<()> {
80 if auth.is_disabled() {
81 if !addr.ip().is_loopback() && !posture.allow_unauthenticated_public_bind {
82 return Err(Error::UnauthenticatedPublicBind { addr });
83 }
84 tracing::warn!(
85 %addr,
86 "control-plane auth is DISABLED — do not expose this listener to an untrusted network"
87 );
88 }
89 Ok(())
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use boatramp_core::security::SecurityProfile;
96
97 #[test]
100 fn fail_closed_refuses_unauthenticated_public_bind() {
101 let disabled = boatramp_server::Auth::disabled();
102 let strict = SecurityProfile::MultiTenant.preset();
103 let dev = SecurityProfile::Dev.preset();
104 let public: SocketAddr = "0.0.0.0:8080".parse().unwrap();
105 let loopback: SocketAddr = "127.0.0.1:8080".parse().unwrap();
106
107 assert!(matches!(
109 enforce_auth_bind(public, &disabled, &strict),
110 Err(Error::UnauthenticatedPublicBind { .. })
111 ));
112 assert!(enforce_auth_bind(loopback, &disabled, &strict).is_ok());
114 assert!(enforce_auth_bind(public, &disabled, &dev).is_ok());
116 }
117}