feat(tls): add certificate expiry validation and self-signed warning

This commit is contained in:
2026-03-04 13:30:46 +01:00
parent 950f477842
commit a3f67aca45

View File

@@ -28,6 +28,9 @@ pub fn build_server_config(
let cert_bytes = std::fs::read(cert_path).context("read cert")?; let cert_bytes = std::fs::read(cert_path).context("read cert")?;
let key_bytes = std::fs::read(key_path).context("read key")?; let key_bytes = std::fs::read(key_path).context("read key")?;
// Validate certificate expiry and warn about self-signed certs.
validate_certificate(&cert_bytes)?;
let cert_chain = vec![CertificateDer::from(cert_bytes)]; let cert_chain = vec![CertificateDer::from(cert_bytes)];
let key = PrivateKeyDer::try_from(key_bytes).map_err(|_| anyhow::anyhow!("invalid key"))?; let key = PrivateKeyDer::try_from(key_bytes).map_err(|_| anyhow::anyhow!("invalid key"))?;
@@ -76,3 +79,39 @@ fn generate_self_signed_cert(cert_path: &PathBuf, key_path: &PathBuf) -> anyhow:
Ok(()) Ok(())
} }
/// Validate a DER-encoded X.509 certificate: bail if expired, warn if expiring
/// soon or self-signed.
fn validate_certificate(der_bytes: &[u8]) -> anyhow::Result<()> {
use x509_parser::prelude::*;
let (_, cert) = X509Certificate::from_der(der_bytes)
.map_err(|e| anyhow::anyhow!("failed to parse X.509 certificate: {e}"))?;
let validity = cert.validity();
let now = ASN1Time::now();
if !validity.is_valid_at(now) {
anyhow::bail!(
"TLS certificate expired (not_after: {})",
validity.not_after
);
}
// Warn if expiring within 30 days.
let thirty_days = std::time::Duration::from_secs(30 * 24 * 60 * 60);
let cutoff = now.timestamp() + thirty_days.as_secs() as i64;
if validity.not_after.timestamp() < cutoff {
tracing::warn!(
not_after = %validity.not_after,
"TLS certificate expires within 30 days"
);
}
// Warn if self-signed (issuer == subject).
if cert.issuer() == cert.subject() {
tracing::warn!("TLS certificate is self-signed (issuer == subject)");
}
Ok(())
}