feat: init commit

This commit is contained in:
2024-03-24 18:03:20 +08:00
parent 0ae2b28434
commit db37ba5fd3
6 changed files with 2796 additions and 4 deletions

5
.gitignore vendored
View File

@@ -1,13 +1,10 @@
.idea/
# ---> Rust
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

2594
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

15
Cargo.toml Normal file
View File

@@ -0,0 +1,15 @@
[package]
name = "proxy-inspector"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
pingora = { version = "0.1", features = ["proxy"] }
tokio = { version = "1.36", features = ["rt-multi-thread", "signal"] }
pretty_env_logger = "0.5"
async-trait = "0.1"
log = "0.4"
http = "1.1"
structopt = "0.3"

44
src/app.rs Normal file
View File

@@ -0,0 +1,44 @@
use async_trait::async_trait;
use http::HeaderName;
use log::debug;
use pingora::prelude::{HttpPeer, ProxyHttp, Result, Session};
use super::service::HostConfig;
pub struct ProxyApp {
host_configs: Vec<HostConfig>,
}
impl ProxyApp {
pub fn new(host_configs: Vec<HostConfig>) -> Self {
ProxyApp { host_configs }
}
}
#[async_trait]
impl ProxyHttp for ProxyApp {
type CTX = ();
fn new_ctx(&self) {}
async fn upstream_peer(&self, session: &mut Session, _ctx: &mut ()) -> Result<Box<HttpPeer>> {
let host_header = session
.get_header(HeaderName::from_static("host"))
.unwrap()
.to_str()
.expect("get host from http header failed");
debug!("host header: {host_header}");
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(
host_config.proxy_addr.as_str(),
host_config.proxy_tls,
host_config.proxy_hostname.clone(),
);
let peer = Box::new(proxy_to);
Ok(peer)
}
}

56
src/main.rs Normal file
View File

@@ -0,0 +1,56 @@
use pingora::{
server::{configuration::Opt, Server},
services::{listening::Service as ListeningService, Service},
};
use service::HostConfig;
use structopt::StructOpt;
mod app;
mod service;
pub fn main() {
init_logger();
let opt = Some(Opt::from_args());
let mut my_server = Server::new(opt).unwrap();
my_server.bootstrap();
let proxy_service_ssl2 = service::proxy_service_tls(
&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")),
},
],
);
let mut prometheus_service_http = ListeningService::prometheus_http_service();
prometheus_service_http.add_tcp("127.0.0.1:6150");
let services: Vec<Box<dyn Service>> = vec![
Box::new(proxy_service_ssl2),
Box::new(prometheus_service_http),
];
my_server.add_services(services);
my_server.run_forever();
}
fn init_logger() {
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "pingora_reverse_proxy=debug");
}
pretty_env_logger::init_timed();
}

86
src/service.rs Normal file
View File

@@ -0,0 +1,86 @@
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::ssl::{NameType, SslRef};
use pingora::tls::x509::X509;
use crate::app::ProxyApp;
struct Callback(Vec<(String, X509, PKey<Private>)>);
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)
}
}
#[async_trait]
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));
}
}
#[derive(Clone)]
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_tls(
server_conf: &Arc<ServerConf>,
listen_addr: &str,
host_configs: Vec<HostConfig>,
) -> impl pingora::services::Service {
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 tls_settings = TlsSettings::with_callbacks(cb).unwrap();
service.add_tls_with_settings(listen_addr, None, tls_settings);
service
}