feat: works

This commit is contained in:
2024-03-30 12:11:11 +08:00
parent 4aaec3aa95
commit 277843967b
4 changed files with 56 additions and 76 deletions

View File

@@ -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<bool>
@@ -75,7 +80,7 @@ impl ProxyHttp for ProxyApp {
_ => None,
};
info!("Request:\n{}\n\n{}", req, body.unwrap_or_else(|| "<None>".into()));
log::info!("Request:\n{}\n\n{}", req, body.unwrap_or_else(|| "<None>".into()));
Ok(false)
}
}

View File

@@ -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<Certificate> = 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<Certificate> = 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<RwLock<HashMap<String, Cert>>> = 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
}

View File

@@ -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(),
},
],
);

View File

@@ -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<Private>)>);
struct Callback();
impl Callback {
fn new(config: Vec<HostConfig>) -> 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);