fix(appgate): classify ports from the catalog even for on-node-built apps
The port map deferred to DISK manifests for any app with a build source — which is exactly the four companion UIs (lnd-ui, bitcoin-ui, electrs-ui, fips-ui). Their disk manifests reach nodes only via the frontend runtime payload or a per-node repo checkout, and in the v1.7.125 rollout both proved stale or entirely absent: one node had no checkout at all, others restored an older payload over apps/ at every boot. Result: session_passthrough never reached the gate, so the node's own screens 401'd on every data call, and on nodes whose UI rebuilt from a stale context the app held its port UNGATED. Classification now uses a ports-only overlay that accepts build-source manifests (install/orchestration still defers to disk — unchanged). The signed catalog is the freshest, operator-signed source, and the gate's address binds fail safely against a container publishing differently (logged CANNOT PROTECT), so this can only tighten policy, never expose. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
83d7234824
commit
6c0fc366b3
@@ -147,11 +147,18 @@ pub fn build_port_map() -> PortMap {
|
|||||||
let mut seen_apps: std::collections::HashSet<String> = std::collections::HashSet::new();
|
let mut seen_apps: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||||
|
|
||||||
for (app_id, value) in crate::container::app_catalog::catalog_manifest_values() {
|
for (app_id, value) in crate::container::app_catalog::catalog_manifest_values() {
|
||||||
|
// Ports-only overlay: unlike the install path, classification also
|
||||||
|
// accepts BUILD-SOURCE manifests. The on-node-built companion UIs
|
||||||
|
// are exactly the apps whose gate policy (session_passthrough,
|
||||||
|
// auth: gated) must arrive reliably, and their disk manifests
|
||||||
|
// proved stale or absent fleet-wide in the v1.7.125 rollout. The
|
||||||
|
// gate's binds fail safely on conflict with a differently-published
|
||||||
|
// container, so a fresher catalog can only tighten, never expose.
|
||||||
let Some(manifest) =
|
let Some(manifest) =
|
||||||
crate::container::app_catalog::catalog_manifest_overlay(&app_id, value)
|
crate::container::app_catalog::catalog_manifest_ports_overlay(&app_id, value)
|
||||||
else {
|
else {
|
||||||
// Unparseable/invalid/build-source → the orchestrator falls back
|
// Unparseable/invalid → the orchestrator falls back to disk for
|
||||||
// to disk for this app, so classification must too.
|
// this app, so classification must too.
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if seen_apps.insert(app_id) {
|
if seen_apps.insert(app_id) {
|
||||||
|
|||||||
@@ -256,6 +256,33 @@ pub fn catalog_manifest_overlay(
|
|||||||
Some(m)
|
Some(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Like [`catalog_manifest_overlay`] but WITHOUT the build-source refusal —
|
||||||
|
/// for PORT CLASSIFICATION only, never for install/orchestration.
|
||||||
|
///
|
||||||
|
/// The on-node-built companion UIs (lnd-ui, bitcoin-ui, electrs-ui, fips-ui)
|
||||||
|
/// are exactly the apps whose port policy (auth/bind/session_passthrough)
|
||||||
|
/// must reach the gate reliably, yet their build sources made the overlay
|
||||||
|
/// defer to DISK manifests — whose only delivery paths (frontend runtime
|
||||||
|
/// payload, per-node repo copies) proved stale or absent across the fleet in
|
||||||
|
/// the v1.7.125 rollout: nodes served ungated UIs or 401-dead panels until
|
||||||
|
/// hand-fixed. The signed catalog is fresher and operator-signed; and the
|
||||||
|
/// gate's address binds fail safely on conflict with a container that
|
||||||
|
/// publishes differently (logged as CANNOT PROTECT), so classifying from the
|
||||||
|
/// catalog cannot open anything the running container hasn't already opened.
|
||||||
|
pub fn catalog_manifest_ports_overlay(
|
||||||
|
app_id: &str,
|
||||||
|
value: serde_json::Value,
|
||||||
|
) -> Option<archipelago_container::manifest::AppManifest> {
|
||||||
|
let m: archipelago_container::manifest::AppManifest = serde_json::from_value(value).ok()?;
|
||||||
|
if m.app.id != app_id {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if m.validate().is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(m)
|
||||||
|
}
|
||||||
|
|
||||||
/// The catalog's default/latest version string for an app (the top-level
|
/// The catalog's default/latest version string for an app (the top-level
|
||||||
/// `version` field), if covered. Used to decide whether an install-time
|
/// `version` field), if covered. Used to decide whether an install-time
|
||||||
/// selection should pin (older) or track-latest (default).
|
/// selection should pin (older) or track-latest (default).
|
||||||
|
|||||||
Reference in New Issue
Block a user