diff --git a/src/app.rs b/src/app.rs index 7bb7b87..83c3639 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,7 +2,6 @@ use async_trait::async_trait; use base64::Engine; use base64::engine::general_purpose::STANDARD; use http::HeaderName; -use log::{debug, info}; use pingora::{Error, ErrorType}; use pingora::prelude::{HttpPeer, ProxyHttp, Result, Session}; @@ -29,24 +28,30 @@ impl ProxyHttp for ProxyApp { .unwrap() .to_str() .expect("get host from http header failed"); - debug!("host header: {host_header}"); + log::info!("host header: {host_header}"); - if host_header == "localhost" || host_header.starts_with("localhost:") { + let hostname = if host_header.contains(':') { + host_header.chars().take_while(|c| c != &':').collect() + } else { + host_header.to_string() + }; + + if hostname == "localhost" { return Err(Error::new(ErrorType::CustomCode("bad host", 400))); } let host_config = self .host_configs .iter() - .find(|x| x.proxy_hostname == host_header) - .unwrap_or_else(|| panic!("find config for: {} failed", host_header)); - let proxy_to = HttpPeer::new( + .find(|x| x.proxy_hostname == hostname) + .unwrap_or_else(|| panic!("find config for: {} failed", hostname)); + let peer = HttpPeer::new( host_config.proxy_addr.as_str(), host_config.proxy_tls, host_config.proxy_hostname.clone(), ); - let peer = Box::new(proxy_to); - Ok(peer) + log::info!("Find peer: {:?}", peer._address); + Ok(Box::new(peer)) } async fn request_filter(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result @@ -75,7 +80,7 @@ impl ProxyHttp for ProxyApp { _ => None, }; - info!("Request:\n{}\n\n{}", req, body.unwrap_or_else(|| "".into())); + log::info!("Request:\n{}\n\n{}", req, body.unwrap_or_else(|| "".into())); Ok(false) } } \ No newline at end of file diff --git a/src/cert.rs b/src/cert.rs index 1840a15..b2821d6 100644 --- a/src/cert.rs +++ b/src/cert.rs @@ -1,15 +1,15 @@ use std::collections::HashMap; use std::fs; -use std::sync::RwLock; use once_cell::sync::Lazy; use rcgen::{Certificate, CertificateParams, DnType, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose}; use time::{Duration, OffsetDateTime}; +use tokio::sync::RwLock; const INTERMEDIATE_CERT_ENV_VAR: &str = "INTERMEDIATE_CERT"; const INTERMEDIATE_KEY_ENV_VAR: &str = "INTERMEDIATE_KEY"; -static INTERMEDIATE_CA: Lazy = Lazy::new(|| { +static INTERMEDIATE_CA: Lazy<(Certificate, String)> = Lazy::new(|| { let cert_fn = std::env::var(INTERMEDIATE_CERT_ENV_VAR) .unwrap_or("__ignore_intermediate_cert.pem".to_string()); let key_fn = std::env::var(INTERMEDIATE_KEY_ENV_VAR) @@ -21,8 +21,9 @@ static INTERMEDIATE_CA: Lazy = Lazy::new(|| { // 底层逻辑限制,P256 与 SHA256 搭配,P384 与 SHA384 搭配 let certificate_params = CertificateParams::from_ca_cert_pem(&cert_pem, key_pair) .expect("Cert and keypair mismatch"); - Certificate::from_params(certificate_params) - .expect("Parse cert params failed") + let cert = Certificate::from_params(certificate_params) + .expect("Parse cert params failed"); + (cert, cert_pem) }); #[derive(Debug, Clone)] @@ -36,23 +37,23 @@ static CERTIFICATE_CACHE_MAP: Lazy>> = Lazy::new(|| RwLock::new(HashMap::new()) }); -pub fn issue_certificate(domain: &str) -> Cert { +pub async fn issue_certificate(domain: &str) -> Cert { { - if let Some(cert) = CERTIFICATE_CACHE_MAP.read().unwrap().get(domain) { + if let Some(cert) = CERTIFICATE_CACHE_MAP.read().await.get(domain) { return cert.clone(); } } let cert = new_end_entity(domain); - let cert_pem = cert.serialize_pem_with_signer(&INTERMEDIATE_CA).expect("Sign cert failed"); + let cert_pem = cert.serialize_pem_with_signer(&INTERMEDIATE_CA.0).expect("Sign cert failed"); let key_pem = cert.serialize_private_key_pem(); - let intermediate_pem = INTERMEDIATE_CA.serialize_pem().expect("Ser intermediate cert failed"); + let intermediate_pem = INTERMEDIATE_CA.1.clone(); let cert = Cert { cert_pem, intermediate_pem, key_pem, }; { - CERTIFICATE_CACHE_MAP.write().unwrap().insert(domain.to_string(), cert.clone()); + CERTIFICATE_CACHE_MAP.write().await.insert(domain.to_string(), cert.clone()); } cert } diff --git a/src/main.rs b/src/main.rs index 3cd906b..02677da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,16 +6,14 @@ use pingora::{ use pretty_env_logger::env_logger::Builder; use structopt::StructOpt; +use crate::service::HostConfig; + mod app; mod service; mod cert; pub fn main() { init_logger(); - let cert = cert::issue_certificate("example.com"); - println!("{:#?}", cert); - println!("{}", cert.cert_pem); - panic!("END"); let opt = Some(Opt::from_args()); let mut my_server = Server::new(opt).unwrap(); @@ -31,20 +29,16 @@ pub fn main() { &my_server.configuration, "0.0.0.0:4430", vec![ - // HostConfig { - // proxy_addr: "127.0.0.1:4000".to_owned(), - // proxy_tls: false, - // proxy_hostname: "somedomain.com".to_owned(), - // cert_path: format!("{}/keys/some_domain_cert.crt", env!("CARGO_MANIFEST_DIR")), - // key_path: format!("{}/keys/some_domain_key.pem", env!("CARGO_MANIFEST_DIR")), - // }, - // HostConfig { - // proxy_addr: "1.1.1.1:443".to_owned(), - // proxy_tls: true, - // proxy_hostname: "one.one.one.one".to_owned(), - // cert_path: format!("{}/keys/one_cert.crt", env!("CARGO_MANIFEST_DIR")), - // key_path: format!("{}/keys/one_key.pem", env!("CARGO_MANIFEST_DIR")), - // }, + HostConfig { + proxy_addr: "101.132.122.240:443".to_owned(), + proxy_tls: true, + proxy_hostname: "hatter.ink".to_owned(), + }, + HostConfig { + proxy_addr: "1.1.1.1:443".to_owned(), + proxy_tls: true, + proxy_hostname: "one.one.one.one".to_owned(), + }, ], ); diff --git a/src/service.rs b/src/service.rs index 922dc10..c9b1ef1 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,49 +1,24 @@ -use std::fs; use std::sync::Arc; use async_trait::async_trait; -use log::debug; use pingora::{ listeners::{TlsAccept, TlsSettings}, prelude::http_proxy_service, server::configuration::ServerConf, }; use pingora::tls::ext; -use pingora::tls::pkey::{PKey, Private}; +use pingora::tls::pkey::PKey; use pingora::tls::ssl::{NameType, SslRef}; use pingora::tls::x509::X509; use crate::app::ProxyApp; +use crate::cert; -struct Callback(Vec<(String, X509, PKey)>); +struct Callback(); impl Callback { - fn new(config: Vec) -> Self { - let config = config - .into_iter() - .map( - |HostConfig { - proxy_hostname, - cert_path, - key_path, - proxy_addr: _, - proxy_tls: _, - }| { - let cert_bytes = fs::read(&cert_path) - .unwrap_or_else(|_| panic!("read file: {} failed", cert_path)); - let cert = X509::from_pem(&cert_bytes) - .unwrap_or_else(|_| panic!("parse file: {} failed", cert_path)); - - let key_bytes = fs::read(&key_path) - .unwrap_or_else(|_| panic!("read file: {} failed", key_path)); - let key = PKey::private_key_from_pem(&key_bytes) - .unwrap_or_else(|_| panic!("parse file: {} failed", key_path)); - - (proxy_hostname, cert, key) - }, - ) - .collect(); - Self(config) + fn new() -> Self { + Self() } } @@ -51,12 +26,20 @@ impl Callback { impl TlsAccept for Callback { async fn certificate_callback(&self, ssl: &mut SslRef) -> () { let sni_provided = ssl.servername(NameType::HOST_NAME).expect("get sni failed").to_string(); - let sni_provided = &sni_provided; - debug!("SNI provided: {}", sni_provided); - let (_, cert, key) = self.0.iter().find(|x| &x.0 == sni_provided) - .unwrap_or_else(|| panic!("cannot find any certificates fro: {}", sni_provided)); - ext::ssl_use_certificate(ssl, cert).unwrap_or_else(|_| panic!("apply certificate for: {} failed", sni_provided)); - ext::ssl_use_private_key(ssl, key).unwrap_or_else(|_| panic!("apply key for: {} failed", sni_provided)); + log::info!("SNI provided: {}", sni_provided); + + let cert = cert::issue_certificate(&sni_provided).await; + + let x509_cert = X509::from_pem(cert.cert_pem.as_bytes()) + .unwrap_or_else(|_| panic!("parse cert: {} failed", cert.cert_pem)); + let x509_intermediate_cert = X509::from_pem(cert.intermediate_pem.as_bytes()) + .unwrap_or_else(|_| panic!("parse intermediate cert: {} failed", cert.intermediate_pem)); + let private_key = PKey::private_key_from_pem(cert.key_pem.as_bytes()) + .unwrap_or_else(|_| panic!("parse key: {} failed", cert.key_pem)); + + ext::ssl_use_certificate(ssl, &x509_cert).unwrap_or_else(|_| panic!("apply certificate for: {} failed", sni_provided)); + ext::ssl_add_chain_cert(ssl, &x509_intermediate_cert).unwrap_or_else(|_| panic!("apply intermediate certificate for: {} failed", sni_provided)); + ext::ssl_use_private_key(ssl, &private_key).unwrap_or_else(|_| panic!("apply key for: {} failed", sni_provided)); } } @@ -65,8 +48,6 @@ pub struct HostConfig { pub proxy_addr: String, pub proxy_tls: bool, pub proxy_hostname: String, - pub cert_path: String, - pub key_path: String, } pub fn proxy_service_tcp( @@ -90,8 +71,7 @@ pub fn proxy_service_tls( let proxy_app = ProxyApp::new(host_configs.clone()); let mut service = http_proxy_service(server_conf, proxy_app); - let cb = Callback::new(host_configs); - let cb = Box::new(cb); + let cb = Box::new(Callback::new()); let tls_settings = TlsSettings::with_callbacks(cb).unwrap(); service.add_tls_with_settings(listen_addr, None, tls_settings);