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:
co-authored by
factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent
cc1eb55c58
commit
a758e8e0bc
+61
-18
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user