This commit is contained in:
Mateusz Gruszczyński committed 2026-10-31 23:42:58 +01:00
1 parent 2c03fcf248
commit d3b492874b
95 files changed
+1628 -743

No files matched your search

+1 -1
View File
@@ -286,7 +286,7 @@ pub fn for_all_frontends(
}
for cert in &mut result.certificates {
cert.hosts.retain(|host| {
!assignments.contains_key(&host.to_ascii_lowercase().trim_end_matches('.').to_string())
!assignments.contains_key(host.to_ascii_lowercase().trim_end_matches('.'))
});
for (host, id) in &assignments {
if id == &cert.id {
+5 -5
View File
@@ -3,9 +3,9 @@
fn external_absolute_paths_follow_certbot_style_symlinks_on_each_reload() {
use std::os::unix::fs::{symlink, PermissionsExt};
let root = tempfile::tempdir().unwrap();
let root = private_tempdir();
let cert_dir = root.path().join("rustproxy-certs");
fs::create_dir(&cert_dir).unwrap();
crate::auth::prepare_directory(&cert_dir).unwrap();
initialize_default(&cert_dir).unwrap();
let archive = root.path().join("archive/example.test");
@@ -100,7 +100,7 @@ fn external_absolute_paths_follow_certbot_style_symlinks_on_each_reload() {
fn external_key_permissions_accept_acl_style_group_mask_but_reject_unsafe_write_or_world_read() {
use std::os::unix::fs::PermissionsExt;
let root = tempfile::tempdir().unwrap();
let root = private_tempdir();
let key = root.path().join("external.key");
fs::write(&key, b"not-a-real-key").unwrap();
@@ -119,7 +119,7 @@ fn external_key_permissions_accept_acl_style_group_mask_but_reject_unsafe_write_
fn external_paths_reject_writable_non_sticky_parent_directories() {
use std::os::unix::fs::PermissionsExt;
let root = tempfile::tempdir().unwrap();
let root = private_tempdir();
let unsafe_parent = root.path().join("unsafe");
fs::create_dir(&unsafe_parent).unwrap();
fs::set_permissions(&unsafe_parent, fs::Permissions::from_mode(0o770)).unwrap();
@@ -133,7 +133,7 @@ fn external_paths_reject_writable_non_sticky_parent_directories() {
fn certificate_input_rejects_fifo_without_waiting_for_a_writer() {
use std::os::unix::ffi::OsStrExt;
let root = tempfile::tempdir().unwrap();
let root = private_tempdir();
let fifo = root.path().join("certificate.pipe");
let path = std::ffi::CString::new(fifo.as_os_str().as_bytes()).unwrap();
assert_eq!(unsafe { libc::mkfifo(path.as_ptr(), 0o600) }, 0);
+5 -3
View File
@@ -85,10 +85,12 @@ pub struct CertificateMetadata {
fn der_text(tag: u8, raw: &[u8]) -> Option<String> {
let value = match tag {
0x0c | 0x13 | 0x14 | 0x16 => std::str::from_utf8(raw).ok()?.to_owned(),
0x1e if raw.len() % 2 == 0 => {
0x1e if raw.len().is_multiple_of(2) => {
let units: Vec<u16> = raw
.chunks_exact(2)
.map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
.as_chunks::<2>()
.0
.iter()
.map(|chunk| u16::from_be_bytes(*chunk))
.collect();
String::from_utf16(&units).ok()?
}
+29 -15
View File
@@ -1,5 +1,20 @@
mod tests {
use super::*;
fn private_tempdir() -> tempfile::TempDir {
let dir = tempfile::tempdir().unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(
dir.path(),
std::fs::Permissions::from_mode(0o700),
)
.unwrap();
}
dir
}
#[test]
fn certificate_removal_requires_prior_unassignment_including_administration() {
let certificate = Certificate {
@@ -67,7 +82,7 @@ mod tests {
#[test]
fn external_pair_stages_generation_and_rolls_back_cleanly() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
let generated = rcgen::generate_simple_self_signed(vec![
"shared.example.test".into(),
"www.shared.example.test".into(),
@@ -96,7 +111,7 @@ mod tests {
#[test]
fn external_pair_reconcile_is_idempotent_and_repairs_missing_generation() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
let generated =
rcgen::generate_simple_self_signed(vec!["reconcile.example.test".into()]).unwrap();
let certificate = generated.cert.pem();
@@ -166,7 +181,7 @@ mod tests {
#[test]
fn default_is_persistent_and_tls_advertises_both_protocols() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
initialize_default(dir.path()).unwrap();
let before = fs::read(dir.path().join("default.pem")).unwrap();
initialize_default(dir.path()).unwrap();
@@ -293,7 +308,7 @@ mod tests {
#[test]
fn pqc_builds_both_tcp_tls_and_quic_configs() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
initialize_default(dir.path()).unwrap();
let mut settings = Settings::default();
settings.policy.post_quantum = true;
@@ -308,7 +323,7 @@ mod tests {
#[test]
fn administration_tls_supports_default_and_selected_certificate() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
initialize_default(dir.path()).unwrap();
generate(dir.path(), "panel", vec!["panel.localhost".into()]).unwrap();
let settings = Settings {
@@ -347,7 +362,7 @@ mod tests {
b.signing_key.serialize_pem().as_bytes()
)
.is_err());
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
initialize_default(dir.path()).unwrap();
generate(dir.path(), "a", vec!["a.localhost".into()]).unwrap();
let mut settings = Settings {
@@ -370,7 +385,7 @@ mod tests {
}
#[test]
fn named_generation_never_overwrites_keys() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
generate(dir.path(), "app", vec!["app.localhost".into()]).unwrap();
let before = fs::read(dir.path().join("app.key")).unwrap();
assert!(generate(dir.path(), "app", vec!["other.localhost".into()]).is_err());
@@ -378,7 +393,7 @@ mod tests {
}
#[test]
fn certificate_hostname_bindings_are_not_artificially_capped() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
let hosts = (0..40)
.map(|index| format!("host-{index}.example.test"))
.collect::<Vec<_>>();
@@ -408,7 +423,7 @@ mod tests {
}
#[test]
fn uploads_validate_key_and_names_and_store_only_private_immutable_files() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
initialize_default(dir.path()).unwrap();
let pair = rcgen::generate_simple_self_signed(vec![
"app.localhost".into(),
@@ -481,7 +496,7 @@ mod tests {
#[test]
fn legacy_certificate_coverage_can_be_read_from_existing_pem() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
let pair = rcgen::generate_simple_self_signed(vec![
"app.example.org".into(),
"*.example.org".into(),
@@ -496,7 +511,7 @@ mod tests {
}
#[test]
fn mismatched_named_certificate_requires_explicit_per_vhost_consent() {
let dir = tempfile::tempdir().unwrap();
let dir = private_tempdir();
initialize_default(dir.path()).unwrap();
generate(dir.path(), "named", vec!["right.example.test".into()]).unwrap();
let settings = Settings {
@@ -511,13 +526,12 @@ mod tests {
..Default::default()
};
let mut config = crate::config::demo_config("127.0.0.1:9000".parse().unwrap());
let vhost = &mut config.vhosts[0];
vhost.certificate_id = Some("named".into());
vhost.frontend.hosts = vec!["other.example.test".into()];
config.vhosts[0].certificate_id = Some("named".into());
config.vhosts[0].frontend.hosts = vec!["other.example.test".into()];
let blocked = for_vhosts(&settings, &config.vhosts).unwrap();
assert!(compile(&blocked, Some(dir.path())).is_err());
assert!(blocked.forced_mismatches.is_empty());
vhost.allow_certificate_mismatch = true;
config.vhosts[0].allow_certificate_mismatch = true;
let forced = for_vhosts(&settings, &config.vhosts).unwrap();
assert!(forced
.forced_mismatches
+1 -11
View File
@@ -17,7 +17,7 @@ use zeroize::Zeroizing;
const MAX_PEM: u64 = 131_072;
const MAX_CERTIFICATE_PATH: usize = 4096;
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct Settings {
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -28,16 +28,6 @@ pub struct Settings {
#[serde(skip)]
pub forced_mismatches: HashSet<(String, String)>,
}
impl Default for Settings {
fn default() -> Self {
Self {
default_certificate: None,
certificates: Vec::new(),
policy: ProtocolPolicy::default(),
forced_mismatches: HashSet::new(),
}
}
}
impl Settings {
pub fn is_empty(&self) -> bool {
self.default_certificate.is_none()