audit: eliminate deployment-address hardcoding; single typed endpoint config
Mandatory OpenFUT architecture audit. Two real defects found and fixed, plus
the config surface tightened so neither class can recur.
DEFECT 1 -- hidden localhost fallback. The Rust host defaulted POW hosts to
127.0.0.1 while every other URL followed OPENFUT_ADVERTISE, so a remote
deployment would emit loopback POW URLs and fail far from the cause. It also
diverged from the deployed Python entrypoint, which derives them
(POW_HOST="${POW_HOST:-$ADV:8094}"). POW endpoints now derive from the
advertised address; explicit overrides still win.
DEFECT 2 -- Default gave loopback silently. `Endpoints::default()` and
`AdapterConfig::default()` supplied 127.0.0.1, so anything constructing a
config by omission got loopback with no signal. Both `Default` impls are
REMOVED. Loopback is now `Endpoints::loopback()` / `AdapterConfig::loopback()`:
an explicit, greppable decision. Production uses `advertising(host)`.
CONFIGURABILITY. `blaze_port` and `utas_port` are now config, not literals.
The advertised Blaze port is our choice -- the client goes wherever
<serverinstanceinfo> sends it -- and 8099 is the client's own built-in default
but still deployment config. A bad port value is an error, not a silent
fallback to the previous one.
TEST-NET EVERYWHERE. Committed fixtures and tests used the lab's real LAN
address; a test that passes because its constant matches the current lab
proves nothing about relocatability. Redirector fixtures regenerated on
RFC 5737 TEST-NET-1/2/3 plus loopback. Harness scripts no longer default the
client IP to the lab address -- client-state.sh now requires it.
SEVEN REQUIRED TESTS in tests/deployment_config.rs plus host-side coverage:
remote config never silently becomes localhost; missing advertise fails
clearly; bind may differ from advertise; changing the Blaze port changes the
redirect; changing the host updates all 200+ generated URLs with no
stragglers; no helper bypasses central config; mutations are detectable.
MUTATION TESTED, and it found a hole in the audit tests themselves. Hardcoding
utas_base, reverting the POW derivation and re-hardcoding the Blaze port were
all caught. Making the redirector read `bind` instead of `advertise` was NOT:
`advertising()` sets bind == advertise, so the two sources were
indistinguishable. That is the single most likely bypass -- the oracle really
does read bind for nucleusConnect -- so the test now forces bind != advertise
and asserts the bind address never reaches the wire. Re-mutated: caught.
Wire behaviour unchanged: oracle fixtures still current, 153 tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,12 +45,13 @@ pub struct BlazeEndpoint {
|
||||
}
|
||||
|
||||
impl BlazeEndpoint {
|
||||
/// Blaze lives on 42130 in this deployment; the advertised host comes from
|
||||
/// config so a split deployment reaches the right machine.
|
||||
/// Both host and port come from configuration. Neither is a protocol
|
||||
/// constant: the client goes wherever this response sends it, so hardcoding
|
||||
/// either would make the deployment un-relocatable.
|
||||
pub fn from_config(cfg: &AdapterConfig) -> BlazeEndpoint {
|
||||
BlazeEndpoint {
|
||||
host: cfg.endpoints.advertise.clone(),
|
||||
port: 42130,
|
||||
port: cfg.endpoints.blaze_port,
|
||||
secure: false,
|
||||
}
|
||||
}
|
||||
@@ -127,7 +128,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
fn cfg(advertise: &str) -> AdapterConfig {
|
||||
let mut c = AdapterConfig::default();
|
||||
let mut c = AdapterConfig::loopback();
|
||||
c.endpoints.advertise = advertise.into();
|
||||
c
|
||||
}
|
||||
@@ -136,7 +137,7 @@ mod tests {
|
||||
fn ip_encoding_is_host_order_decimal() {
|
||||
assert_eq!(ip_to_u32("127.0.0.1"), 2_130_706_433);
|
||||
assert_eq!(ip_to_u32("198.51.100.7"), 3_325_256_711);
|
||||
assert_eq!(ip_to_u32("10.10.0.120"), 168_427_640);
|
||||
assert_eq!(ip_to_u32("203.0.113.42"), 3_405_803_818);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -150,7 +151,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn secure_is_zero_confirming_the_plaintext_second_hop() {
|
||||
let body = server_instance_info_xml(&BlazeEndpoint::from_config(&cfg("10.0.0.5")));
|
||||
let body = server_instance_info_xml(&BlazeEndpoint::from_config(&cfg("203.0.113.42")));
|
||||
assert!(body.contains("<secure>0</secure>"));
|
||||
}
|
||||
|
||||
@@ -165,7 +166,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn content_length_matches_the_body_exactly() {
|
||||
let bytes = redirect_response(&cfg("10.10.0.120"));
|
||||
let bytes = redirect_response(&cfg("203.0.113.42"));
|
||||
let text = String::from_utf8(bytes).unwrap();
|
||||
let (head, body) = text.split_once("\r\n\r\n").expect("header/body split");
|
||||
let declared: usize = head
|
||||
|
||||
Reference in New Issue
Block a user