a (hacky, wip) multi-tenant oidc-terminating reverse proxy, written in anger on top of pingora

fix uds backends

ketama hashing skips uds backends cause it can't handle it, so
choose roundrobin at service launch if uds backends are in play for a
domain.

+104 -21
+14 -14
Cargo.lock
··· 2488 2488 [[package]] 2489 2489 name = "pingora" 2490 2490 version = "0.7.0" 2491 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2491 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2492 2492 dependencies = [ 2493 2493 "pingora-cache", 2494 2494 "pingora-core", ··· 2501 2501 [[package]] 2502 2502 name = "pingora-cache" 2503 2503 version = "0.7.0" 2504 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2504 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2505 2505 dependencies = [ 2506 2506 "ahash", 2507 2507 "async-trait", ··· 2537 2537 [[package]] 2538 2538 name = "pingora-core" 2539 2539 version = "0.7.0" 2540 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2540 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2541 2541 dependencies = [ 2542 2542 "ahash", 2543 2543 "async-trait", ··· 2589 2589 [[package]] 2590 2590 name = "pingora-error" 2591 2591 version = "0.7.0" 2592 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2592 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2593 2593 2594 2594 [[package]] 2595 2595 name = "pingora-header-serde" 2596 2596 version = "0.7.0" 2597 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2597 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2598 2598 dependencies = [ 2599 2599 "bytes", 2600 2600 "http", ··· 2609 2609 [[package]] 2610 2610 name = "pingora-http" 2611 2611 version = "0.7.0" 2612 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2612 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2613 2613 dependencies = [ 2614 2614 "bytes", 2615 2615 "http", ··· 2619 2619 [[package]] 2620 2620 name = "pingora-ketama" 2621 2621 version = "0.7.0" 2622 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2622 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2623 2623 dependencies = [ 2624 2624 "crc32fast", 2625 2625 ] ··· 2627 2627 [[package]] 2628 2628 name = "pingora-load-balancing" 2629 2629 version = "0.7.0" 2630 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2630 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2631 2631 dependencies = [ 2632 2632 "arc-swap", 2633 2633 "async-trait", ··· 2648 2648 [[package]] 2649 2649 name = "pingora-lru" 2650 2650 version = "0.7.0" 2651 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2651 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2652 2652 dependencies = [ 2653 2653 "arrayvec", 2654 2654 "hashbrown 0.16.1", ··· 2659 2659 [[package]] 2660 2660 name = "pingora-pool" 2661 2661 version = "0.7.0" 2662 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2662 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2663 2663 dependencies = [ 2664 2664 "crossbeam-queue", 2665 2665 "log", ··· 2673 2673 [[package]] 2674 2674 name = "pingora-proxy" 2675 2675 version = "0.7.0" 2676 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2676 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2677 2677 dependencies = [ 2678 2678 "async-trait", 2679 2679 "bytes", ··· 2695 2695 [[package]] 2696 2696 name = "pingora-runtime" 2697 2697 version = "0.7.0" 2698 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2698 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2699 2699 dependencies = [ 2700 2700 "once_cell", 2701 2701 "rand 0.8.5", ··· 2706 2706 [[package]] 2707 2707 name = "pingora-rustls" 2708 2708 version = "0.7.0" 2709 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2709 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2710 2710 dependencies = [ 2711 2711 "log", 2712 2712 "no_debug", ··· 2722 2722 [[package]] 2723 2723 name = "pingora-timeout" 2724 2724 version = "0.7.0" 2725 - source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#771c7926b9635f716ddb30a24ba1d941a77596aa" 2725 + source = "git+https://github.com/directxman12/pingora?branch=custom%2Fmain#4a9a8abc2c25267bde433bb2b8555736989c4075" 2726 2726 dependencies = [ 2727 2727 "once_cell", 2728 2728 "parking_lot",
+79
src/backend_selection.rs
··· 1 + //! _grumbles_ ketama doesn't support uds _grumbles_ 2 + //! 3 + //! (also this lets us add an option to choose in the future) 4 + 5 + use std::sync::Arc; 6 + 7 + use pingora::lb::selection::BackendSelection; 8 + use pingora::lb::selection::consistent::{KetamaConfig, KetamaHashing}; 9 + use pingora::lb::{self, Backend}; 10 + use pingora::prelude::*; 11 + 12 + pub enum SelectionChoice { 13 + Ketama(Arc<KetamaHashing>), 14 + RoundRobin(Arc<RoundRobin>), 15 + } 16 + 17 + #[allow( 18 + dead_code, 19 + reason = "required for trait impl, will use the in the future" 20 + )] 21 + pub enum SelectionConfig { 22 + Ketama(KetamaConfig), 23 + RoundRobin(()), 24 + } 25 + pub enum SelectionIter { 26 + Ketama(<KetamaHashing as BackendSelection>::Iter), 27 + RoundRobin(<RoundRobin as BackendSelection>::Iter), 28 + } 29 + impl lb::selection::BackendIter for SelectionIter { 30 + fn next(&mut self) -> Option<&Backend> { 31 + match self { 32 + SelectionIter::Ketama(k) => k.next(), 33 + SelectionIter::RoundRobin(rr) => rr.next(), 34 + } 35 + } 36 + } 37 + impl BackendSelection for SelectionChoice { 38 + type Iter = SelectionIter; 39 + 40 + type Config = SelectionConfig; 41 + 42 + fn build(backends: &std::collections::BTreeSet<lb::Backend>) -> Self { 43 + // need the arc here for iter 44 + if backends 45 + .iter() 46 + .any(|b| matches!(b.addr, pingora::protocols::l4::socket::SocketAddr::Unix(_))) 47 + { 48 + // ketama does not support unix domain sockets 49 + Self::RoundRobin(Arc::new(RoundRobin::build(backends))) 50 + } else { 51 + Self::Ketama(Arc::new(KetamaHashing::build(backends))) 52 + } 53 + } 54 + 55 + fn iter(self: &std::sync::Arc<Self>, key: &[u8]) -> Self::Iter 56 + where 57 + Self::Iter: lb::selection::BackendIter, 58 + { 59 + match &**self { 60 + SelectionChoice::Ketama(k) => SelectionIter::Ketama(k.iter(key)), 61 + SelectionChoice::RoundRobin(rr) => SelectionIter::RoundRobin(rr.iter(key)), 62 + } 63 + } 64 + 65 + fn build_with_config( 66 + backends: &std::collections::BTreeSet<Backend>, 67 + config: &Self::Config, 68 + ) -> Self { 69 + // need the arc here for iter 70 + match config { 71 + SelectionConfig::Ketama(ketama) => { 72 + Self::Ketama(Arc::new(KetamaHashing::build_with_config(backends, ketama))) 73 + } 74 + SelectionConfig::RoundRobin(rr) => { 75 + Self::RoundRobin(Arc::new(RoundRobin::build_with_config(backends, rr))) 76 + } 77 + } 78 + } 79 + }
+5 -2
src/gateway.rs
··· 8 8 use cookie_rs::CookieJar; 9 9 use http::status::StatusCode; 10 10 use http::{HeaderName, HeaderValue}; 11 - use pingora::lb::selection::consistent::KetamaHashing; 12 11 use pingora::prelude::*; 13 12 use pingora::protocols::tls::CaType; 14 13 use url::Url; ··· 23 22 /// per-domain information about backends and such 24 23 pub struct DomainInfo { 25 24 /// the load balancer to use to select backends 26 - pub balancer: Arc<LoadBalancer<KetamaHashing>>, 25 + pub balancer: Arc<LoadBalancer<super::SelectionChoice>>, 27 26 /// whether or not we allow insecure connections from clients 28 27 pub tls_mode: config::format::domain::TlsMode, 29 28 /// the sni name of this domain, used to pass to backends ··· 636 635 peer.options.ca = ca; 637 636 peer 638 637 } 638 + BackendData::HttpOverUds => { 639 + HttpPeer::new_from_sockaddr(backend.addr, false, backends.sni_name.to_string()) 640 + } 639 641 }; 640 642 Ok(Box::new(peer)) 641 643 } ··· 692 694 /// for use in [`AuthGateway::upstream_peer`] 693 695 #[derive(Clone)] 694 696 pub enum BackendData { 697 + HttpOverUds, 695 698 HttpOnly, 696 699 Tls { 697 700 /// should skip we verifying the cert (useful if the certs are selfsigned and we don't want to
+6 -5
src/main.rs
··· 1 1 use std::collections::HashMap; 2 2 3 3 use color_eyre::eyre::Context as _; 4 - use pingora::lb; 5 - use pingora::lb::selection::consistent::KetamaHashing; 4 + use pingora::lb::{self, Backend}; 6 5 use pingora::listeners::tls::{BundleCert, CertAndKey, TlsSettings}; 7 6 use pingora::prelude::*; 8 7 use pingora::utils::tls::CertKey; 9 8 9 + use self::backend_selection::SelectionChoice; 10 10 use self::gateway::{AuthGateway, BackendData, DomainInfo, oidc}; 11 11 12 + mod backend_selection; 12 13 mod config; 13 14 mod cookies; 14 15 mod gateway; ··· 17 18 18 19 /// constructed load balancer, with [backend info][`BackendInfo`] to be passed to [`AuthGateway`] 19 20 type BalancerInfo = ( 20 - Vec<pingora::services::background::GenBackgroundService<LoadBalancer<KetamaHashing>>>, 21 + Vec<pingora::services::background::GenBackgroundService<LoadBalancer<SelectionChoice>>>, 21 22 HashMap<String, DomainInfo>, 22 23 ); 23 24 24 25 /// construct the load balancer and initialize the [backend info][`BackendInfo`] for the 25 26 /// [`AuthGateway`] 26 27 fn balancer(domains: &HashMap<String, config::format::Domain>) -> color_eyre::Result<BalancerInfo> { 27 - use lb::{self, Backend, discovery}; 28 + use lb::{self, discovery}; 28 29 use pingora::protocols::l4::socket::SocketAddr; 29 30 30 31 let mut balancers = HashMap::with_capacity(domains.len()); ··· 68 69 })) 69 70 .chain(domain.uds.iter().map(|backend| { 70 71 let mut ext = lb::Extensions::new(); 71 - ext.insert(BackendData::HttpOnly); 72 + ext.insert(BackendData::HttpOverUds); 72 73 Ok(Backend { 73 74 addr: SocketAddr::Unix( 74 75 std::os::unix::net::SocketAddr::from_pathname(&backend.path)