272 lines
11 KiB
Rust
272 lines
11 KiB
Rust
mod tests {
|
|
use super::*;
|
|
#[test]
|
|
fn http2_connection_limits_default_and_validate_independently() {
|
|
let defaults: Http2Connections = serde_json::from_str("{}").unwrap();
|
|
assert_eq!(defaults.idle_timeout_seconds, 300);
|
|
assert_eq!(defaults.max_connection_lifetime_seconds, 0);
|
|
assert_eq!(defaults.drain_timeout_seconds, 10);
|
|
defaults.validate().unwrap();
|
|
|
|
let configured = Http2Connections {
|
|
idle_timeout_seconds: 45,
|
|
max_connection_lifetime_seconds: 3_600,
|
|
drain_timeout_seconds: 20,
|
|
};
|
|
configured.validate().unwrap();
|
|
assert_eq!(configured.idle_timeout(), Duration::from_secs(45));
|
|
assert_eq!(configured.max_connection_lifetime(), Some(Duration::from_secs(3_600)));
|
|
assert_eq!(configured.drain_timeout(), Duration::from_secs(20));
|
|
assert!(Http2Connections { idle_timeout_seconds: 0, ..defaults }.validate().is_err());
|
|
assert!(Http2Connections { max_connection_lifetime_seconds: 2_592_001, ..defaults }.validate().is_err());
|
|
assert!(Http2Connections { drain_timeout_seconds: 0, ..defaults }.validate().is_err());
|
|
}
|
|
#[test]
|
|
fn new_install_server_header_defaults_to_rustproxy_without_changing_legacy_missing_field() {
|
|
let fresh = crate::config::Config::default();
|
|
assert!(matches!(fresh.server_header.mode, ServerMode::Custom));
|
|
assert_eq!(fresh.server_header.value, "RustProxy");
|
|
|
|
let legacy: crate::config::Config = serde_json::from_str("{}").unwrap();
|
|
assert!(matches!(legacy.server_header.mode, ServerMode::Preserve));
|
|
}
|
|
|
|
#[test]
|
|
fn server_policy_and_injection() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("server", "origin".parse().unwrap());
|
|
ServerHeader {
|
|
mode: ServerMode::Hide,
|
|
..Default::default()
|
|
}
|
|
.apply(&mut headers);
|
|
assert!(!headers.contains_key("server"));
|
|
let custom = ServerHeader {
|
|
mode: ServerMode::Custom,
|
|
value: "RustProxy/v0.1.0".into(),
|
|
};
|
|
custom.validate().unwrap();
|
|
custom.apply(&mut headers);
|
|
assert_eq!(headers["server"], "RustProxy/v0.1.0");
|
|
assert!(ServerHeader {
|
|
mode: ServerMode::Custom,
|
|
value: "bad\r\nx: y".into()
|
|
}
|
|
.validate()
|
|
.is_err());
|
|
}
|
|
#[test]
|
|
fn explicit_startup_binds_are_injected_and_cannot_be_removed() {
|
|
let locks = StartupLocks {
|
|
http: Some("127.0.0.1:18080".parse().unwrap()),
|
|
https: Some("127.0.0.1:18443".parse().unwrap()),
|
|
administration: Some("127.0.0.1:19090".parse().unwrap()),
|
|
};
|
|
let mut config = crate::config::Config::default();
|
|
config.administration.listen = Some("127.0.0.1:19999".parse().unwrap());
|
|
locks.ensure_config(&mut config);
|
|
assert!(config.administration.listen.is_none());
|
|
locks
|
|
.validate(&config, "127.0.0.1:19090".parse().unwrap())
|
|
.unwrap();
|
|
|
|
let http = config
|
|
.listeners
|
|
.endpoints
|
|
.iter_mut()
|
|
.find(|endpoint| {
|
|
endpoint.bindings().iter().any(|binding| {
|
|
binding.address == "127.0.0.1:18080".parse().unwrap() && !binding.tls
|
|
})
|
|
})
|
|
.unwrap();
|
|
http.enabled = false;
|
|
locks.ensure_config(&mut config);
|
|
assert!(config.listeners.endpoints.iter().any(|endpoint| {
|
|
endpoint.enabled
|
|
&& endpoint.bindings().iter().any(|binding| {
|
|
binding.address == "127.0.0.1:18080".parse().unwrap() && !binding.tls
|
|
})
|
|
}));
|
|
|
|
config.listeners.endpoints.retain(|endpoint| {
|
|
!endpoint.bindings().iter().any(|binding| {
|
|
binding.address == "127.0.0.1:18080".parse().unwrap() && !binding.tls
|
|
})
|
|
});
|
|
assert!(locks
|
|
.validate(&config, "127.0.0.1:19090".parse().unwrap())
|
|
.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn explicit_startup_bind_replaces_canonical_saved_binding() {
|
|
let locks = StartupLocks {
|
|
http: Some("0.0.0.0:8181".parse().unwrap()),
|
|
https: Some("0.0.0.0:8443".parse().unwrap()),
|
|
administration: None,
|
|
};
|
|
let mut config = crate::config::Config::default();
|
|
config.listeners = Listeners::from_startup(
|
|
"0.0.0.0:80".parse().unwrap(),
|
|
Some("0.0.0.0:443".parse().unwrap()),
|
|
);
|
|
config.listeners.endpoints[0]
|
|
.additional_addresses
|
|
.push("127.0.0.1:80".parse().unwrap());
|
|
config.listeners.endpoints[1]
|
|
.additional_addresses
|
|
.push("127.0.0.1:443".parse().unwrap());
|
|
|
|
locks.ensure_config(&mut config);
|
|
|
|
let http = config.listeners.endpoints.iter().find(|item| item.id == "http").unwrap();
|
|
let https = config.listeners.endpoints.iter().find(|item| item.id == "https").unwrap();
|
|
assert_eq!(http.binding.address, "0.0.0.0:8181".parse().unwrap());
|
|
assert!(http.additional_addresses.is_empty());
|
|
assert_eq!(https.binding.address, "0.0.0.0:8443".parse().unwrap());
|
|
assert!(https.additional_addresses.is_empty());
|
|
assert!(!config.listeners.endpoints.iter().filter(|item| item.enabled).flat_map(|item| item.bindings()).any(|binding| binding.address.port() == 80 || binding.address.port() == 443));
|
|
}
|
|
|
|
#[test]
|
|
fn startup_defaults_become_named_listeners() {
|
|
let saved = Listeners::from_startup(
|
|
"0.0.0.0:80".parse().unwrap(),
|
|
Some("0.0.0.0:443".parse().unwrap()),
|
|
);
|
|
assert_eq!(saved.endpoints.len(), 2);
|
|
assert_eq!(saved.endpoints[0].id, "http");
|
|
assert_eq!(saved.endpoints[0].binding.address.port(), 80);
|
|
assert_eq!(saved.endpoints[1].id, "https");
|
|
assert_eq!(saved.endpoints[1].binding.address.port(), 443);
|
|
assert_eq!(
|
|
saved.endpoints[1].binding.http2_settings,
|
|
Some(Http2Connections::default())
|
|
);
|
|
assert!(saved.endpoints[1].binding.http3.is_some());
|
|
}
|
|
#[test]
|
|
fn named_listener_can_bind_multiple_explicit_addresses() {
|
|
let endpoint = NamedListener {
|
|
id: "web".into(),
|
|
name: "Web".into(),
|
|
scope: ListenerScope::Public,
|
|
enabled: true,
|
|
binding: crate::vhost::Listener {
|
|
address: "192.0.2.10:443".parse().unwrap(),
|
|
tls: true,
|
|
http2: true,
|
|
http2_settings: Some(Http2Connections::default()),
|
|
h2c: false,
|
|
http3: None,
|
|
},
|
|
additional_addresses: vec!["192.0.2.11:443".parse().unwrap()],
|
|
};
|
|
let bindings = endpoint.bindings();
|
|
assert_eq!(bindings.len(), 2);
|
|
assert_eq!(bindings[0].address, "192.0.2.10:443".parse().unwrap());
|
|
assert_eq!(bindings[1].address, "192.0.2.11:443".parse().unwrap());
|
|
assert_eq!(bindings[0].tls, bindings[1].tls);
|
|
assert_eq!(bindings[0].http2, bindings[1].http2);
|
|
}
|
|
|
|
#[test]
|
|
fn multi_address_listener_rejects_unspecified_or_duplicate_addresses() {
|
|
let policy = crate::config::ValidationPolicy {
|
|
tls_addr: None,
|
|
cert_dir: None,
|
|
data_dir: None,
|
|
proxy_addr: "127.0.0.1:18080".parse().unwrap(),
|
|
admin_addr: "127.0.0.1:19090".parse().unwrap(),
|
|
};
|
|
let mut listeners = Listeners {
|
|
endpoints: vec![NamedListener {
|
|
id: "web".into(),
|
|
name: "Web".into(),
|
|
scope: ListenerScope::Public,
|
|
enabled: true,
|
|
binding: crate::vhost::Listener {
|
|
address: "0.0.0.0:8080".parse().unwrap(),
|
|
tls: false,
|
|
http2: false,
|
|
http2_settings: None,
|
|
h2c: false,
|
|
http3: None,
|
|
},
|
|
additional_addresses: vec!["192.0.2.10:8080".parse().unwrap()],
|
|
}],
|
|
};
|
|
assert!(listeners.validate(&policy).is_err());
|
|
listeners.endpoints[0].binding.address = "192.0.2.10:8080".parse().unwrap();
|
|
assert!(listeners.validate(&policy).is_err());
|
|
listeners.endpoints[0].additional_addresses = vec!["192.0.2.11:8080".parse().unwrap()];
|
|
assert!(listeners.validate(&policy).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn administration_certificate_requires_tls_and_known_inventory_id() {
|
|
let inventory = crate::tls::Settings {
|
|
certificates: vec![crate::tls::Certificate {
|
|
issuer: None,
|
|
id: "panel".into(),
|
|
covered_hosts: Vec::new(),
|
|
hosts: vec![],
|
|
cert_file: "panel.pem".into(),
|
|
key_file: "panel.key".into(),
|
|
}],
|
|
..crate::tls::Settings::default()
|
|
};
|
|
let mut admin = Administration {
|
|
tls: false,
|
|
certificate_id: Some("panel".into()),
|
|
..Administration::default()
|
|
};
|
|
assert!(admin.validate(&inventory).is_err());
|
|
admin.tls = true;
|
|
assert!(admin.validate(&inventory).is_ok());
|
|
admin.certificate_id = Some("missing".into());
|
|
assert!(admin.validate(&inventory).is_err());
|
|
}
|
|
#[test]
|
|
fn administration_session_lifetime_is_7_to_30_days_and_hot_apply_only() {
|
|
let tls = crate::tls::Settings::default();
|
|
let mut admin = Administration::default();
|
|
assert_eq!(admin.session_days, 30);
|
|
assert_eq!(admin.session_seconds(), 30 * 24 * 60 * 60);
|
|
assert!(admin.validate(&tls).is_ok());
|
|
let active = admin.clone();
|
|
admin.session_days = 7;
|
|
assert!(admin.validate(&tls).is_ok());
|
|
assert!(!admin.restart_differs(&active));
|
|
admin.session_days = 6;
|
|
assert!(admin.validate(&tls).is_err());
|
|
admin.session_days = 31;
|
|
assert!(admin.validate(&tls).is_err());
|
|
}
|
|
#[test]
|
|
fn administration_hostnames_are_normalized_and_unique() {
|
|
let mut admin = Administration {
|
|
tls: true,
|
|
allowed_hosts: vec!["Panel.Example.COM.".into()],
|
|
..Administration::default()
|
|
};
|
|
admin.validate(&crate::tls::Settings::default()).unwrap();
|
|
assert_eq!(admin.allowed_hosts, vec!["panel.example.com".to_owned()]);
|
|
admin.allowed_hosts = vec!["panel.example.com".into(), "PANEL.EXAMPLE.COM.".into()];
|
|
assert!(admin.validate(&crate::tls::Settings::default()).is_err());
|
|
}
|
|
#[test]
|
|
fn administration_hostname_count_is_not_artificially_capped() {
|
|
let mut admin = Administration {
|
|
tls: true,
|
|
allowed_hosts: (0..40)
|
|
.map(|index| format!("admin-{index}.example.test"))
|
|
.collect(),
|
|
..Administration::default()
|
|
};
|
|
assert!(admin.validate(&crate::tls::Settings::default()).is_ok());
|
|
assert_eq!(admin.allowed_hosts.len(), 40);
|
|
}
|
|
}
|