feat: add connection key encoding and full tunnel implementation

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
root
2026-06-04 13:54:55 -06:00
co-authored by factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent cc1eb55c58
commit a758e8e0bc
10 changed files with 1677 additions and 390 deletions
+61 -18
View File
@@ -9,6 +9,17 @@ use rcgen::{
use crate::config::Config;
use crate::redact::Redacted;
#[derive(Debug, Clone)]
pub struct GeneratedMaterial {
pub ca_cert_pem: String,
pub ca_key_pem: String,
pub server_cert_pem: String,
pub server_key_pem: String,
pub client_cert_pem: String,
pub client_key_pem: String,
pub auth_token: String,
}
/// Generate all credential material and write it to the output directory.
///
/// Creates:
@@ -29,32 +40,29 @@ pub fn generate(
std::fs::create_dir_all(out_dir)
.with_context(|| format!("failed to create output directory: {}", out_dir.display()))?;
// Generate CA
let ca = generate_ca(ca_name)?;
let material = generate_material(ca_name, server_name, client_name)?;
let ca_cert_path = out_dir.join("ca.pem");
let ca_key_path = out_dir.join("ca.key");
std::fs::write(&ca_cert_path, ca.as_ref().pem()).with_context(|| {
std::fs::write(&ca_cert_path, &material.ca_cert_pem).with_context(|| {
format!(
"failed to write CA certificate to {}",
ca_cert_path.display()
)
})?;
std::fs::write(&ca_key_path, ca.key().serialize_pem())
std::fs::write(&ca_key_path, &material.ca_key_pem)
.with_context(|| format!("failed to write CA key to {}", ca_key_path.display()))?;
tracing::info!("Generated CA certificate at {}", ca_cert_path.display());
tracing::info!("Generated CA key at {}", ca_key_path.display());
// Generate server certificate
let (server_cert, server_key) = generate_server_cert(&ca, server_name)?;
let server_cert_path = out_dir.join("server.crt");
let server_key_path = out_dir.join("server.key");
std::fs::write(&server_cert_path, server_cert.pem()).with_context(|| {
std::fs::write(&server_cert_path, &material.server_cert_pem).with_context(|| {
format!(
"failed to write server cert to {}",
server_cert_path.display()
)
})?;
std::fs::write(&server_key_path, server_key.serialize_pem()).with_context(|| {
std::fs::write(&server_key_path, &material.server_key_pem).with_context(|| {
format!(
"failed to write server key to {}",
server_key_path.display()
@@ -66,17 +74,15 @@ pub fn generate(
);
tracing::info!("Generated server key at {}", server_key_path.display());
// Generate client certificate
let (client_cert, client_key) = generate_client_cert(&ca, client_name)?;
let client_cert_path = out_dir.join("client.crt");
let client_key_path = out_dir.join("client.key");
std::fs::write(&client_cert_path, client_cert.pem()).with_context(|| {
std::fs::write(&client_cert_path, &material.client_cert_pem).with_context(|| {
format!(
"failed to write client cert to {}",
client_cert_path.display()
)
})?;
std::fs::write(&client_key_path, client_key.serialize_pem()).with_context(|| {
std::fs::write(&client_key_path, &material.client_key_pem).with_context(|| {
format!(
"failed to write client key to {}",
client_key_path.display()
@@ -88,11 +94,9 @@ pub fn generate(
);
tracing::info!("Generated client key at {}", client_key_path.display());
// Generate auth token
let auth_token = generate_auth_token();
let auth_token_redacted = Redacted::new(auth_token.clone());
let auth_token_redacted = Redacted::new(material.auth_token.clone());
let token_path = out_dir.join("token.txt");
std::fs::write(&token_path, auth_token)
std::fs::write(&token_path, &material.auth_token)
.with_context(|| format!("failed to write token to {}", token_path.display()))?;
tracing::info!(
"Generated auth token at {} (value: {})",
@@ -102,7 +106,7 @@ pub fn generate(
// Generate config
let config = Config {
listen_address: "127.0.0.1".to_string(),
listen_address: "0.0.0.0".to_string(),
listen_port: 4180,
socks_address: "127.0.0.1".to_string(),
socks_port: 1180,
@@ -128,6 +132,34 @@ pub fn generate(
Ok(())
}
pub fn generate_material(
ca_name: &str,
server_name: &str,
client_name: &str,
) -> anyhow::Result<GeneratedMaterial> {
generate_material_with_server_host(ca_name, server_name, client_name, None)
}
pub fn generate_material_with_server_host(
ca_name: &str,
server_name: &str,
client_name: &str,
server_host: Option<&str>,
) -> anyhow::Result<GeneratedMaterial> {
let ca = generate_ca(ca_name)?;
let (server_cert, server_key) = generate_server_cert(&ca, server_name, server_host)?;
let (client_cert, client_key) = generate_client_cert(&ca, client_name)?;
Ok(GeneratedMaterial {
ca_cert_pem: ca.as_ref().pem(),
ca_key_pem: ca.key().serialize_pem(),
server_cert_pem: server_cert.pem(),
server_key_pem: server_key.serialize_pem(),
client_cert_pem: client_cert.pem(),
client_key_pem: client_key.serialize_pem(),
auth_token: generate_auth_token(),
})
}
fn generate_ca(cn: &str) -> anyhow::Result<CertifiedIssuer<'static, KeyPair>> {
let mut params = CertificateParams::default();
let mut dn = DistinguishedName::new();
@@ -145,6 +177,7 @@ fn generate_ca(cn: &str) -> anyhow::Result<CertifiedIssuer<'static, KeyPair>> {
fn generate_server_cert(
ca: &CertifiedIssuer<'_, KeyPair>,
cn: &str,
extra_host: Option<&str>,
) -> anyhow::Result<(Certificate, KeyPair)> {
let mut params = CertificateParams::default();
let mut dn = DistinguishedName::new();
@@ -157,6 +190,16 @@ fn generate_server_cert(
SanType::DnsName("rustunnel".try_into().unwrap()),
SanType::IpAddress(std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1))),
];
if let Some(host) = extra_host
&& host != "0.0.0.0"
&& host != "::"
{
if let Ok(ip) = host.parse::<std::net::IpAddr>() {
params.subject_alt_names.push(SanType::IpAddress(ip));
} else if let Ok(dns) = host.try_into() {
params.subject_alt_names.push(SanType::DnsName(dns));
}
}
params.serial_number = Some(SerialNumber::from(2));
let key_pair = KeyPair::generate()?;
@@ -297,7 +340,7 @@ mod tests {
let config_str = std::fs::read_to_string(dir.join("config.json")).unwrap();
let config: Config = serde_json::from_str(&config_str).unwrap();
assert_eq!(config.listen_address, "127.0.0.1");
assert_eq!(config.listen_address, "0.0.0.0");
assert_eq!(config.listen_port, 4180);
assert_eq!(config.socks_address, "127.0.0.1");
assert_eq!(config.socks_port, 1180);