diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bbb183..e3d09bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.24.9 +- Fix Rust type inference for zeroizing ACME/Base64 and AEAD buffers so the new Secret Store and encrypted backup code compile with the pinned dependency set. - Fix the secret-redaction KV regex literal so 0.24.9 compiles correctly; the redaction behavior itself is unchanged. - Harden the built-in release checker for FreeBSD/OPNsense and other Unix platforms: supplement bundled Mozilla roots with common system CA bundles/`SSL_CERT_FILE`/`CURL_CA_BUNDLE`, follow bounded non-downgrading HTTP(S) redirects, and report failed checks as normal application `ERROR` events without changing dashboard behavior. - Add a central AES-256-GCM encrypted Secret Store with private local master-key storage and migration of existing ACME DNS-provider credentials. diff --git a/SHA256SUMS b/SHA256SUMS index 3b6e615..1844b51 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -2,7 +2,7 @@ 4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example 9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore 60d7b6e6599269c73bb901383fc979be2403d41dbea221ff3100def26161baa3 AGENTS.md -b159e8d97a7de7bfc64256d13885b6f0c700433d265ecdbcee6d2312e7c91cc2 CHANGELOG.md +837e5d2bbbd973ce32b86db4b444fa677cc1f770d9d0f1b197b2d6c1a9f64af8 CHANGELOG.md 31147611b139a69c531fbdafcbe53e3331c7afbcfcab1167ebbca8b24b5bc4cb Cargo.lock 5928784bf7f69a9b21f2420d63a82986ae03a08998ceca6bae1900fda16804a2 Cargo.toml d6d22ba63f4bc3d16a13012ecaabff8a156be57c824d2f20f66976502fb27d1f Dockerfile @@ -121,7 +121,7 @@ edb6ee1445b01cd6f3a74a3a607f39fb107d411b49df028d2e0f4679e093de31 scripts/acme_p e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/acme_rfc2136.py ea2c670aa1fc2c145fb3666711642d526c2adb01537fc3b6398664e33136f0e4 scripts/admin_audit.py 20e83f8b6347b819d1d1ea3de5c691c36f0816954a4210b0d3f39557fefe1931 scripts/backup.py -551ccaf255b59a9219b4605c93c7c459f20639b253387d30d46f1a7d5f001b87 scripts/check_source.py +2ca3da5473554bf33555148a69ef81bcd690674e1de0deb0312ca096143b2398 scripts/check_source.py e19eba9a8b8a0a8eb26b1e70a78fdfea2661be3a32b3f769fe8129dc81b7aaf5 scripts/deploy.py 6facb2d65bfab234f09855097d12da8f72669a38ebceb4b1cbd03ab42b8d2469 scripts/langs_check.py 9f89ccdcd31da0f203113051a109ada8e51b44f91382ee2c5c75228d1e456540 scripts/memory_contracts.py @@ -143,7 +143,7 @@ c9be0bb829f45220653cf320b7466a567dd0745f511880eb6c27a3552c52e08b scripts/swagge 102b23de19a88a8e60de392fb83aefd3dfb33e90cc97ed1690d9f3d4a33d8866 scripts/test_compression.py 94b96941648424f44c86d3e7c6225c9afc1d91a13e2755b154591a2b9e950013 scripts/upgrade.py 081aad397b2b9485e20b684dc11682d3a16464c5b4f110cd461f8518eb74ad99 src/acl.rs -c7e6874ba7bfc64d5e5071386fa8ee58c338740ea7bd1c024becd25ba2c77571 src/acme.rs +4213236dc8317667313fb028fa0536ce957ccc6210bb364913f82730517a15cd src/acme.rs fdba60fa997aa18cb0a7ef50e286a8b6fd877d14fe829d4b0bcb5c7c269a5ae6 src/admin/documentation.rs 9457c2e3557df929da1118daab792373fffeb10ac5b71acf294bae5a2ed0f6e2 src/admin/mod.rs 358b21548909037c016356e9e2a7104fb175bdf7cd1ccf45cb0d8f5dc2c3b4c6 src/admin/openapi.json @@ -179,7 +179,7 @@ aad4a2f55f750c26ab98f125a8f7712b365e6c993cf4ee9a3636d05a6a0b41b6 src/admin/web/ 1e5c6c6157f8b66d90751a0480cb90c6fdfa229330fc4c3f3504bc0c689c3d60 src/auth/files.rs 606f5491f94da007881a679051677a4609881d3151ac753de02556e0e76e1ce8 src/auth/mod.rs 1ceb57d97cbff655f185e126d98b8dc7884c7aac865e1f70e3c6d199685c13a7 src/auth/store.rs -b1950a91bf4e2ff0474000486c212a6390b7696f3f559b93aaedf96614408c9a src/backup.rs +e85c6d9a9e80c59d0d8ed1ec8a1be5ebae18a1e9dba1dedcb0f5872c903bf569 src/backup.rs 168f55f75ad6ebd80f98c3376f5885cdc8d313f8b03f4e93066a28cf7b8c8b81 src/balance.rs e2bf9d4f8a8c8c7f9a8df96872a657b9706d05f3f3a83721fe912ca0af37b29a src/basic_auth.rs 9a2880276819a62fef40c4c926be688e38b7c0eb551995d5653c119ffced0db3 src/cache/cold.rs @@ -219,7 +219,7 @@ f355d84c16cef89c43785fdc02181d6ad234fbc9c834367843822ff970088d00 src/prometheus 6b24aa692ed3cc7d20b561c90239e26e203f8e44f64d4bb45b8b63ba05906d4f src/rate_limit.rs acdca1bf861f95b85f08dbbcf69e8a2090335cab4d542f2509383bcc67a0e699 src/redirect.rs 4e73633414a5d439447ec2cd1775dd0d8a81249c3183f0d351b830e7027c2f4a src/runtime.rs -a98a6a2a6274cc4d5cc4626f34eba7885dbf1c764ce5e5cf03dc3e084b3eb902 src/secret_store.rs +2f439d96a43536ac23ed7727bf96a616b26c2ff30d3813c44398755d8525fd97 src/secret_store.rs c403b63a4192e9549b8a82f29e6966fddd2130be1a9a86a895e7692168de98ea src/security.rs d35fbad33476b465d456dde8984daa4e52e34b9d546ab9e48dffcc40c74a4669 src/server.rs a80e8fc2990e62b8fd28426c8e1d213c9761577462179e96637d646ad3d87518 src/settings.rs diff --git a/scripts/check_source.py b/scripts/check_source.py index e0a4e5e..dc353e8 100755 --- a/scripts/check_source.py +++ b/scripts/check_source.py @@ -521,6 +521,12 @@ assert 'regex::Regex::new(r#"(?i)((?:authorization|' in logging_source, "KV reda assert '[^\\s,;\"\']+" )' not in logging_source, "do not reintroduce the unterminated raw-string form in KV redaction" assert "pub error_log: crate::logging::ErrorLogSettings" in config_source assert "credential_ini_b64" in acme_source and "credential_ini_b64" in acme_worker_source +assert "STANDARD.encode(credential.as_slice())" in acme_source, "Base64 input must be an unambiguous byte slice" +assert "STANDARD.encode(credential.as_ref())" not in acme_source, "Zeroizing>::as_ref() is ambiguous for Engine::encode" +assert "&mut *ciphertext" in secret_store_source, "ring seal must receive the underlying Vec, not an ambiguous AsMut target" +assert "&mut *encrypted" in backup_source, "ring seal must receive the underlying Vec, not an ambiguous AsMut target" +assert "seal_in_place_append_tag(\n Nonce::assume_unique_for_key(nonce),\n Aad::from(aad(secret_id, provider)),\n ciphertext.as_mut()," not in secret_store_source +assert "seal_in_place_append_tag(\n Nonce::assume_unique_for_key(nonce_bytes),\n Aad::from(KEY_AAD),\n encrypted.as_mut()," not in backup_source assert "finally:" in acme_worker_source and "credential_file.unlink" in acme_worker_source for marker in ['certificate_api_credentials', 'upsert_certificate_api_credential', 'revoke_certificate_api_credential', 'verify_certificate_api_credential', '0005_certificate_api.sql', '0006_certificate_api_credentials.sql']: assert marker in db_source, marker diff --git a/src/acme.rs b/src/acme.rs index d1087f3..9c1a5d7 100644 --- a/src/acme.rs +++ b/src/acme.rs @@ -916,7 +916,7 @@ impl Manager { .await .map_err(|_| ApiError(StatusCode::CONFLICT, "acme.secret_missing".into()))?; request["credential_ini_b64"] = Value::String( - base64::engine::general_purpose::STANDARD.encode(credential.as_ref()) + base64::engine::general_purpose::STANDARD.encode(credential.as_slice()) ); } let raw = Zeroizing::new(request.to_string()); diff --git a/src/backup.rs b/src/backup.rs index 0000303..5edf16e 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -367,7 +367,7 @@ fn seal_master_key(master: &[u8], backup_key: &[u8]) -> Result> { .seal_in_place_append_tag( Nonce::assume_unique_for_key(nonce_bytes), Aad::from(KEY_AAD), - encrypted.as_mut(), + &mut *encrypted, ) .map_err(|_| anyhow::anyhow!("could not wrap secret store key"))?; let mut output = Vec::with_capacity(4 + 12 + encrypted.len()); diff --git a/src/secret_store.rs b/src/secret_store.rs index a274777..2dbdc60 100644 --- a/src/secret_store.rs +++ b/src/secret_store.rs @@ -1,6 +1,6 @@ //! Central encrypted secret storage for provider/ACME credentials. //! Plaintext exists only for the duration of an explicit read/write operation. -use anyhow::{bail, ensure, Context, Result}; +use anyhow::{ensure, Context, Result}; use rand::{rngs::OsRng, RngCore}; use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM}; use serde::Serialize; @@ -170,7 +170,7 @@ pub async fn put( .seal_in_place_append_tag( Nonce::assume_unique_for_key(nonce), Aad::from(aad(secret_id, provider)), - ciphertext.as_mut(), + &mut *ciphertext, ) .map_err(|_| anyhow::anyhow!("secret encryption failed"))?; let now = crate::auth::now();