feat: init commit
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,13 +1,10 @@
|
|||||||
|
.idea/
|
||||||
# ---> Rust
|
# ---> Rust
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
debug/
|
debug/
|
||||||
target/
|
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
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.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