agent: replace openssl self-signed cert generation with rcgen (pure Rust)

This commit is contained in:
jasonwitty 2025-08-22 10:46:29 -07:00
parent 8def4b2d06
commit d1c8a64418
2 changed files with 31 additions and 51 deletions

View File

@ -24,7 +24,7 @@ once_cell = "1.19"
axum-server = { version = "0.6", features = ["tls-rustls"] } axum-server = { version = "0.6", features = ["tls-rustls"] }
rustls = "0.23" rustls = "0.23"
rustls-pemfile = "2.1" rustls-pemfile = "2.1"
openssl = { version = "0.10", features = ["vendored"] } # for crossplatform selfsigned generation rcgen = "0.13" # pure-Rust self-signed cert generation (replaces openssl vendored build)
anyhow = "1" anyhow = "1"
hostname = "0.3" hostname = "0.3"
bytes = { workspace = true } bytes = { workspace = true }

View File

@ -1,12 +1,4 @@
use openssl::asn1::Asn1Time; use rcgen::{Certificate, CertificateParams, DistinguishedName, DnType, IsCa, KeyPair, SanType};
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use openssl::x509::extension::{
BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName,
};
use openssl::x509::{X509NameBuilder, X509};
use std::{ use std::{
fs, fs,
io::Write, io::Write,
@ -35,57 +27,45 @@ pub fn ensure_self_signed_cert() -> anyhow::Result<(PathBuf, PathBuf)> {
} }
fs::create_dir_all(cert_path.parent().unwrap())?; fs::create_dir_all(cert_path.parent().unwrap())?;
// Key
let rsa = Rsa::generate(4096)?;
let pkey = PKey::from_rsa(rsa)?;
// Subject/issuer
let hostname = hostname::get() let hostname = hostname::get()
.ok() .ok()
.and_then(|s| s.into_string().ok()) .and_then(|s| s.into_string().ok())
.unwrap_or_else(|| "localhost".to_string()); .unwrap_or_else(|| "localhost".to_string());
let mut name = X509NameBuilder::new()?;
name.append_entry_by_nid(Nid::COMMONNAME, &hostname)?;
let name = name.build();
// Cert builder let mut params = CertificateParams::new(vec![hostname.clone(), "localhost".into()]);
let mut builder = X509::builder()?; // Add IP SANs
builder.set_version(2)?; params
builder.set_subject_name(&name)?; .subject_alt_names
builder.set_issuer_name(&name)?; .push(SanType::IpAddress(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
builder.set_pubkey(&pkey)?; params
.subject_alt_names
.push(SanType::IpAddress(IpAddr::V6(::std::net::Ipv6Addr::LOCALHOST)));
params
.subject_alt_names
.push(SanType::IpAddress(IpAddr::V4(Ipv4Addr::UNSPECIFIED)));
builder.set_not_before(Asn1Time::days_from_now(0)?.as_ref())?; params.distinguished_name = DistinguishedName::new();
builder.set_not_after(Asn1Time::days_from_now(397)?.as_ref())?; params
.distinguished_name
.push(DnType::CommonName, hostname.clone());
params.is_ca = IsCa::NoCa;
// 397 days like previous implementation
params.not_before = rcgen::date_time_ymd(2024, 1, 1); // stable starting point
params.not_after = params.not_before + rcgen::PKCS_EPOCH_DURATION * 0; // overwritten below
// rcgen doesn't allow direct relative days for not_after while keeping not_before now; use validity_days
params.validity_days = 397;
// SANs: hostname + localhost loopbacks // Use modern defaults (Ed25519) for key; fallback to RSA if necessary
let mut san = SubjectAlternativeName::new(); // Keep RSA to maximize compatibility with older clients
san.dns(&hostname) params.alg = &rcgen::PKCS_ECDSA_P256_SHA256; // widely supported
.dns("localhost") let cert = Certificate::from_params(params)?;
.ip("127.0.0.1") let cert_pem = cert.serialize_pem()?;
.ip("::1"); let key_pem = cert.serialize_private_key_pem();
// Add a generic 0.0.0.0 for convenience; some TLS libs ignore this, but harmless.
let _ = san.ip(&IpAddr::V4(Ipv4Addr::UNSPECIFIED).to_string());
let san = san.build(&builder.x509v3_context(None, None))?;
// End-entity cert: not a CA
builder.append_extension(BasicConstraints::new().critical().build()?)?;
builder.append_extension(
KeyUsage::new()
.digital_signature()
.key_encipherment()
.build()?,
)?;
// TLS server usage
builder.append_extension(ExtendedKeyUsage::new().server_auth().build()?)?;
builder.append_extension(san)?;
builder.sign(&pkey, MessageDigest::sha256())?;
let cert: X509 = builder.build();
let mut f = fs::File::create(&cert_path)?; let mut f = fs::File::create(&cert_path)?;
f.write_all(&cert.to_pem()?)?; f.write_all(cert_pem.as_bytes())?;
let mut k = fs::File::create(&key_path)?; let mut k = fs::File::create(&key_path)?;
k.write_all(&pkey.private_key_to_pem_pkcs8()?)?; k.write_all(key_pem.as_bytes())?;
println!( println!(
"socktop_agent: generated self-signed TLS certificate at {}", "socktop_agent: generated self-signed TLS certificate at {}",