aboutsummaryrefslogtreecommitdiffstats
path: root/src/config
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-02 21:34:12 +0100
committertv <tv@krebsco.de>2026-11-02 21:34:12 +0100
commitf2b90bbcdb5347bfb68ee0e3343358e2a8e466dc (patch)
treef33dfaa441d1d9bec58ca7a68aa3172f2bdc838d /src/config
parent7e3a474ad15131f5e246582df8d80f595a52da28 (diff)
api: allow auth delegation
Diffstat (limited to 'src/config')
-rw-r--r--src/config/mod.rs32
1 files changed, 27 insertions, 5 deletions
diff --git a/src/config/mod.rs b/src/config/mod.rs
index c7f96a3..37c010e 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -100,14 +100,28 @@ pub(crate) const OPTIONS: &[OptionSpec] = &[
Kind::Text,
"PATH",
None,
- "Users of the HTTP API (htpasswd file with Argon2 hashes). Required, but with relay-only (then without it, no HTTP API).",
+ "Users of the HTTP API (htpasswd file with Argon2 hashes). Required, but with user-header or relay-only (then without either, no HTTP API).",
+ ),
+ option(
+ "user-header",
+ Kind::Text,
+ "NAME",
+ None,
+ "A request header naming the user, set by a reverse proxy in front of the HTTP API that authenticated the request itself (e.g. X-Remote-User, from nginx's $remote_user). Believed only from trusted-proxies; other requests need htpasswd's basic authentication. The proxy must set or clear it on every request.",
+ ),
+ option(
+ "trusted-proxies",
+ Kind::List,
+ "ADDRESS,[ADDRESS...]",
+ Some("127.0.0.1,::1"),
+ "Addresses (an IP address, or a range as ADDRESS/BITS) of the reverse proxies whose user-header is believed.",
),
option(
"protocol-writers",
Kind::List,
"USER,[USER...]",
None,
- "HTTP API users (from htpasswd) who may write protocol data under /store/_continuum (remake records, members, forget records), e.g. to repair a stuck remake. Such writes act on the whole swarm and are logged. Epoch metadata and the swarm ID are never writable.",
+ "HTTP API users (from htpasswd or user-header) who may write protocol data under /store/_continuum (remake records, members, forget records), e.g. to repair a stuck remake. Such writes act on the whole swarm and are logged. Epoch metadata and the swarm ID are never writable.",
),
option(
"log-format",
@@ -482,8 +496,11 @@ pub(crate) fn spec(name: &str) -> Option<&'static OptionSpec> {
pub(crate) struct Settings {
#[serde(default = "default_bind_addr")]
pub(crate) bind_addr: SocketAddr,
- /// Required (`check`), but with `relay_only`.
+ /// Required (`check`), but with `user_header` or `relay_only`.
pub(crate) htpasswd: Option<String>,
+ pub(crate) user_header: Option<String>,
+ #[serde(default = "default_trusted_proxies")]
+ pub(crate) trusted_proxies: Vec<String>,
#[serde(default)]
pub(crate) protocol_writers: Vec<String>,
#[serde(default)]
@@ -621,6 +638,9 @@ pub(crate) struct Settings {
fn default_bind_addr() -> SocketAddr {
SocketAddr::from(([127, 0, 0, 1], 3000))
}
+fn default_trusted_proxies() -> Vec<String> {
+ vec!["127.0.0.1".to_owned(), "::1".to_owned()]
+}
fn default_document_append() -> Duration {
Duration::from_secs(5)
}
@@ -696,8 +716,10 @@ impl Settings {
if self.relay_only {
self.check_relay_only()?;
} else {
- if self.htpasswd.is_none() {
- bail!("htpasswd is required: the users of the HTTP API");
+ if self.htpasswd.is_none() && self.user_header.is_none() {
+ bail!(
+ "htpasswd is required: the users of the HTTP API (or user-header, with a reverse proxy that authenticates them)"
+ );
}
if self.node_key_file.is_none() {
bail!(