feat: works
This commit is contained in:
23
src/app.rs
23
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<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)
|
||||
}
|
||||
}
|
||||
19
src/cert.rs
19
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<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
|
||||
}
|
||||
|
||||
30
src/main.rs
30
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(),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user