feat: init commit
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -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
2594
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
Normal file
15
Cargo.toml
Normal 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
44
src/app.rs
Normal 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
56
src/main.rs
Normal 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
86
src/service.rs
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user