gateway: serve the hive dashboard on <hive>.hives.<swarm> behind authelia, drop htpasswd
The dashboard, its API, the swagger UI and the agent routes move to their own vhost, `swarm.dashboardDomain` = `<hive>.hives.<swarm.domain>`, under the swarm domain so authelia's session cookie reaches it. `/`, `/api/` and `/api/docs/` ask authelia through `auth_request`, and authelia's `operatorsOnly` rule for each hive's dashboard name lets only the admins group in. The hive domain keeps matrix discovery and redirects every other path to the new name. The name rides as a SAN on the hive's own gateway leaf: the per-hive bao role allows it, and hive-tls asks for it and checks coverage. HTTP Basic auth is gone: the htpasswd writer and its test, the `hivectl gateway` verbs and host-socket requests, the `gateway.auth` options (now a removed-option error), the unauthorized page and the bcrypt dependency. tmpfiles deletes a leftover gateway.htpasswd, which held bcrypt hashes at 0644. c0re hands out dashboard and agent URLs on the new name (HYPERHIVE_DASHBOARD_DOMAIN), and the swarm controller's hive roster carries each hive's dashboard name for the swarm UI's hive list. Closes #3469
This commit is contained in:
parent
09ad7386b2
commit
92559af8e7
47 changed files with 351 additions and 623 deletions
|
|
@ -76,6 +76,8 @@ pub struct HiveStatus {
|
|||
pub name: String,
|
||||
/// From the roster; `None` for a hive the roster doesn't list.
|
||||
pub domain: Option<String>,
|
||||
/// The hive's dashboard host, from the roster; `None` as for `domain`.
|
||||
pub dashboard: Option<String>,
|
||||
pub freshness: Freshness,
|
||||
/// When the snapshot was stored, unix seconds. `None` when nothing
|
||||
/// has been.
|
||||
|
|
@ -285,6 +287,7 @@ pub(crate) fn render(
|
|||
Some(offered) => row(
|
||||
hive.name.clone(),
|
||||
Some(hive.domain.clone()),
|
||||
Some(hive.dashboard.clone()),
|
||||
offered,
|
||||
now,
|
||||
stale_after,
|
||||
|
|
@ -292,6 +295,7 @@ pub(crate) fn render(
|
|||
None => HiveStatus {
|
||||
name: hive.name.clone(),
|
||||
domain: Some(hive.domain.clone()),
|
||||
dashboard: Some(hive.dashboard.clone()),
|
||||
freshness: Freshness::NeverReported,
|
||||
last_seen_unix: None,
|
||||
age_seconds: None,
|
||||
|
|
@ -305,7 +309,7 @@ pub(crate) fn render(
|
|||
.iter()
|
||||
.filter(|(name, _)| !roster.iter().any(|hive| &&hive.name == name))
|
||||
.map(|(name, offered)| {
|
||||
let mut unknown = row(name.clone(), None, offered, now, stale_after);
|
||||
let mut unknown = row(name.clone(), None, None, offered, now, stale_after);
|
||||
unknown.freshness = Freshness::Unknown;
|
||||
unknown
|
||||
}),
|
||||
|
|
@ -319,6 +323,7 @@ pub(crate) fn render(
|
|||
pub(crate) fn row(
|
||||
name: String,
|
||||
domain: Option<String>,
|
||||
dashboard: Option<String>,
|
||||
offered: &Offered,
|
||||
now: SystemTime,
|
||||
stale_after: Duration,
|
||||
|
|
@ -335,6 +340,7 @@ pub(crate) fn row(
|
|||
HiveStatus {
|
||||
name,
|
||||
domain,
|
||||
dashboard,
|
||||
freshness,
|
||||
last_seen_unix: offered
|
||||
.received_at
|
||||
|
|
@ -358,10 +364,12 @@ mod tests {
|
|||
HiveEntry {
|
||||
name: "pr1ma".to_owned(),
|
||||
domain: "pr1ma.example.com".to_owned(),
|
||||
dashboard: "pr1ma.hives.example".to_owned(),
|
||||
},
|
||||
HiveEntry {
|
||||
name: "umbra".to_owned(),
|
||||
domain: "umbra.example.com".to_owned(),
|
||||
dashboard: "umbra.hives.example".to_owned(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
@ -444,6 +452,11 @@ mod tests {
|
|||
assert_eq!(rows[0].freshness, Freshness::Fresh);
|
||||
assert_eq!(rows[1].name, "umbra");
|
||||
assert_eq!(rows[1].freshness, Freshness::NeverReported);
|
||||
assert_eq!(
|
||||
[rows[0].dashboard.as_deref(), rows[1].dashboard.as_deref()],
|
||||
[Some("pr1ma.hives.example"), Some("umbra.hives.example")],
|
||||
"a roster hive's row links its dashboard whether or not it reported"
|
||||
);
|
||||
}
|
||||
|
||||
/// An observation the daemon cannot explain is surfaced, not dropped.
|
||||
|
|
@ -463,6 +476,7 @@ mod tests {
|
|||
ghost.domain.is_none(),
|
||||
"the roster is where a domain comes from, and this hive isn't in it"
|
||||
);
|
||||
assert!(ghost.dashboard.is_none());
|
||||
}
|
||||
|
||||
/// Clock skew must not produce a negative age or a panic. A snapshot
|
||||
|
|
|
|||
Loading…
Reference in a new issue