mirror of
https://github.com/usestrix/firewall.git
synced 2026-11-10 05:11:24 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20c781404f | ||
|
|
0ba2e23032 |
No files matched your search
@@ -80,6 +80,18 @@ impl FirewallConfig {
|
|||||||
pub fn local_url(&self, path: &str) -> String {
|
pub fn local_url(&self, path: &str) -> String {
|
||||||
crate::local_url(self.port, path)
|
crate::local_url(self.port, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Without building a `Firewall`, which needs a runtime.
|
||||||
|
pub fn hosts(&self) -> Vec<&'static str> {
|
||||||
|
let mut hosts: Vec<&'static str> = Vec::new();
|
||||||
|
for registry in &self.registries {
|
||||||
|
let host = registry.host();
|
||||||
|
if !hosts.contains(&host) {
|
||||||
|
hosts.push(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hosts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path() -> Option<PathBuf> {
|
pub fn path() -> Option<PathBuf> {
|
||||||
|
|||||||
@@ -40,14 +40,7 @@ impl Firewall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn hosts(&self) -> Vec<&'static str> {
|
pub fn hosts(&self) -> Vec<&'static str> {
|
||||||
let mut hosts: Vec<&'static str> = Vec::new();
|
self.config.hosts()
|
||||||
for registry in &self.config.registries {
|
|
||||||
let host = registry.host();
|
|
||||||
if !hosts.contains(&host) {
|
|
||||||
hosts.push(host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hosts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_index_url(&self, url: &str) -> bool {
|
pub fn is_index_url(&self, url: &str) -> bool {
|
||||||
|
|||||||
+22
-3
@@ -1,10 +1,12 @@
|
|||||||
use rama::error::BoxError;
|
use rama::error::BoxError;
|
||||||
use rama::tls::boring::core::x509::X509;
|
use rama::tls::boring::core::x509::X509;
|
||||||
use rcgen::{
|
use rcgen::{
|
||||||
BasicConstraints, CertificateParams, DistinguishedName, DnType, GeneralSubtree, IsCa,
|
BasicConstraints, CertificateParams, CertificateRevocationListParams, DistinguishedName,
|
||||||
KeyUsagePurpose, NameConstraints, SigningKey,
|
DnType, GeneralSubtree, IsCa, Issuer, KeyIdMethod, KeyUsagePurpose, NameConstraints,
|
||||||
|
SerialNumber, SigningKey,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
/// Held in the Secure Enclave on macOS, in memory elsewhere.
|
/// Held in the Secure Enclave on macOS, in memory elsewhere.
|
||||||
pub type CaSigner = Arc<dyn SigningKey + Send + Sync>;
|
pub type CaSigner = Arc<dyn SigningKey + Send + Sync>;
|
||||||
@@ -42,6 +44,22 @@ impl CertificateAuthority {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Empty, for verifiers that fail closed without one, such as schannel under cargo.
|
||||||
|
/// Signed fresh for each fetch; a week's validity outlasts any verifier's cache.
|
||||||
|
pub fn crl(hosts: &[&str], signer: &dyn SigningKey) -> Result<Vec<u8>, BoxError> {
|
||||||
|
let now = OffsetDateTime::now_utc();
|
||||||
|
let list = CertificateRevocationListParams {
|
||||||
|
this_update: now - Duration::hours(1),
|
||||||
|
next_update: now + Duration::days(7),
|
||||||
|
crl_number: SerialNumber::from(now.unix_timestamp() as u64),
|
||||||
|
issuing_distribution_point: None,
|
||||||
|
revoked_certs: Vec::new(),
|
||||||
|
key_identifier_method: KeyIdMethod::Sha256,
|
||||||
|
};
|
||||||
|
let ca = params(hosts);
|
||||||
|
Ok(list.signed_by(&Issuer::from_params(&ca, signer))?.der().to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
/// Also used by the issuer, whose issuer name must match the CA's subject.
|
/// Also used by the issuer, whose issuer name must match the CA's subject.
|
||||||
pub fn params(hosts: &[&str]) -> CertificateParams {
|
pub fn params(hosts: &[&str]) -> CertificateParams {
|
||||||
let mut name = DistinguishedName::new();
|
let mut name = DistinguishedName::new();
|
||||||
@@ -51,7 +69,8 @@ pub fn params(hosts: &[&str]) -> CertificateParams {
|
|||||||
let mut params = CertificateParams::default();
|
let mut params = CertificateParams::default();
|
||||||
params.distinguished_name = name;
|
params.distinguished_name = name;
|
||||||
params.is_ca = IsCa::Ca(BasicConstraints::Constrained(0));
|
params.is_ca = IsCa::Ca(BasicConstraints::Constrained(0));
|
||||||
params.key_usages = vec![KeyUsagePurpose::KeyCertSign];
|
// rcgen signs a CRL only for a CA that may.
|
||||||
|
params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
|
||||||
params.name_constraints = Some(NameConstraints {
|
params.name_constraints = Some(NameConstraints {
|
||||||
permitted_subtrees: hosts
|
permitted_subtrees: hosts
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
+12
-3
@@ -4,7 +4,7 @@ use rama::tls::boring::core::pkey::PKey;
|
|||||||
use rama::tls::boring::core::x509::X509;
|
use rama::tls::boring::core::x509::X509;
|
||||||
use rama::tls::boring::proxy::cert_issuer::{BoringMitmCertIssuer, MitmIssuedCert};
|
use rama::tls::boring::proxy::cert_issuer::{BoringMitmCertIssuer, MitmIssuedCert};
|
||||||
use rama::utils::collections::non_empty_vec;
|
use rama::utils::collections::non_empty_vec;
|
||||||
use rcgen::{CertificateParams, DistinguishedName, DnType, Issuer, KeyPair};
|
use rcgen::{CertificateParams, CrlDistributionPoint, DistinguishedName, DnType, Issuer, KeyPair};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Leaves name only screened hosts; any other name falls outside the CA's constraint.
|
/// Leaves name only screened hosts; any other name falls outside the CA's constraint.
|
||||||
@@ -14,12 +14,18 @@ pub struct ConstrainedCertIssuer {
|
|||||||
ca_params: Arc<CertificateParams>,
|
ca_params: Arc<CertificateParams>,
|
||||||
signer: CaSigner,
|
signer: CaSigner,
|
||||||
hosts: Vec<&'static str>,
|
hosts: Vec<&'static str>,
|
||||||
|
crl: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConstrainedCertIssuer {
|
impl ConstrainedCertIssuer {
|
||||||
pub(super) fn new(ca_crt: X509, signer: CaSigner, hosts: Vec<&'static str>) -> Self {
|
pub(super) fn new(
|
||||||
|
ca_crt: X509,
|
||||||
|
signer: CaSigner,
|
||||||
|
hosts: Vec<&'static str>,
|
||||||
|
crl: Option<String>,
|
||||||
|
) -> Self {
|
||||||
let ca_params = Arc::new(params(&hosts));
|
let ca_params = Arc::new(params(&hosts));
|
||||||
Self { ca_crt, ca_params, signer, hosts }
|
Self { ca_crt, ca_params, signer, hosts, crl }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +48,9 @@ impl BoringMitmCertIssuer for ConstrainedCertIssuer {
|
|||||||
subject.push(DnType::CommonName, common_name);
|
subject.push(DnType::CommonName, common_name);
|
||||||
let mut leaf = CertificateParams::new(names)?;
|
let mut leaf = CertificateParams::new(names)?;
|
||||||
leaf.distinguished_name = subject;
|
leaf.distinguished_name = subject;
|
||||||
|
if let Some(crl) = &self.crl {
|
||||||
|
leaf.crl_distribution_points = vec![CrlDistributionPoint { uris: vec![crl.clone()] }];
|
||||||
|
}
|
||||||
|
|
||||||
// The TLS server needs the leaf key in memory, so it can't be the platform key.
|
// The TLS server needs the leaf key in memory, so it can't be the platform key.
|
||||||
let leaf_key = KeyPair::generate()?;
|
let leaf_key = KeyPair::generate()?;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod issuer;
|
|||||||
mod route;
|
mod route;
|
||||||
mod screen;
|
mod screen;
|
||||||
|
|
||||||
pub use ca::{CaSigner, CertificateAuthority};
|
pub use ca::{crl, CaSigner, CertificateAuthority};
|
||||||
|
|
||||||
use crate::Firewall;
|
use crate::Firewall;
|
||||||
use issuer::ConstrainedCertIssuer;
|
use issuer::ConstrainedCertIssuer;
|
||||||
@@ -25,14 +25,16 @@ type RelayService = Arc<HttpMitmRelay<FirewallLayer>>;
|
|||||||
type InterceptService =
|
type InterceptService =
|
||||||
TlsMitmRelayService<CachedBoringMitmCertIssuer<ConstrainedCertIssuer>, RelayService>;
|
TlsMitmRelayService<CachedBoringMitmCertIssuer<ConstrainedCertIssuer>, RelayService>;
|
||||||
|
|
||||||
|
/// `crl` is where leaves say an empty CRL is served, for verifiers that insist on one.
|
||||||
pub fn mitm_service(
|
pub fn mitm_service(
|
||||||
firewall: Arc<Firewall>,
|
firewall: Arc<Firewall>,
|
||||||
ca: &CertificateAuthority,
|
ca: &CertificateAuthority,
|
||||||
signer: CaSigner,
|
signer: CaSigner,
|
||||||
|
crl: Option<String>,
|
||||||
exec: Executor,
|
exec: Executor,
|
||||||
) -> MitmService {
|
) -> MitmService {
|
||||||
let hosts = firewall.hosts();
|
let hosts = firewall.hosts();
|
||||||
let issuer = ConstrainedCertIssuer::new(ca.crt.clone(), signer, hosts.clone());
|
let issuer = ConstrainedCertIssuer::new(ca.crt.clone(), signer, hosts.clone(), crl);
|
||||||
|
|
||||||
let relay = Arc::new(HttpMitmRelay::new(exec).with_http_middleware(FirewallLayer { firewall }));
|
let relay = Arc::new(HttpMitmRelay::new(exec).with_http_middleware(FirewallLayer { firewall }));
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ fn handler(exec: Executor) -> Result<Handler, BoxError> {
|
|||||||
let signer: mitm::CaSigner = Arc::new(key);
|
let signer: mitm::CaSigner = Arc::new(key);
|
||||||
let ca = install::certificate_authority(&hosts, &*signer);
|
let ca = install::certificate_authority(&hosts, &*signer);
|
||||||
let ca = Arc::new(ca.inspect_err(|_| telemetry::error("ca"))?);
|
let ca = Arc::new(ca.inspect_err(|_| telemetry::error("ca"))?);
|
||||||
let service = mitm::mitm_service(firewall, &ca, signer, exec);
|
let service = mitm::mitm_service(firewall, &ca, signer, None, exec);
|
||||||
Ok(Handler { ca, hosts, service })
|
Ok(Handler { ca, hosts, service })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,3 +43,10 @@ fn stored_ca_is_rejected_when_the_signing_key_changed() {
|
|||||||
|
|
||||||
assert!(mitm::CertificateAuthority::from_blob(&blob, &hosts, &signer()).is_none());
|
assert!(mitm::CertificateAuthority::from_blob(&blob, &hosts, &signer()).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// rcgen signs a CRL only for a CA with cRLSign, and schannel wants one for every leaf.
|
||||||
|
#[test]
|
||||||
|
fn ca_signs_an_empty_crl() {
|
||||||
|
let crl = mitm::crl(&["registry.npmjs.org", "pypi.org"], &signer()).expect("sign a crl");
|
||||||
|
assert_eq!(crl[0], 0x30, "a DER SEQUENCE");
|
||||||
|
}
|
||||||
Reference in new issue
Block a user