mirror of
https://github.com/sadoyan/aralez.git
synced 2026-04-30 23:08:40 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bee307793c | ||
|
|
6e83775127 | ||
|
|
baded40e6e | ||
|
|
c0a419f6f7 | ||
|
|
8aff2fa875 | ||
|
|
9b4ee26a2b |
86
Cargo.lock
generated
86
Cargo.lock
generated
@@ -120,6 +120,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
|||||||
name = "aralez"
|
name = "aralez"
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -132,6 +133,7 @@ dependencies = [
|
|||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"log",
|
"log",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
|
"moka",
|
||||||
"notify",
|
"notify",
|
||||||
"pingora",
|
"pingora",
|
||||||
"pingora-core",
|
"pingora-core",
|
||||||
@@ -140,7 +142,7 @@ dependencies = [
|
|||||||
"pingora-proxy",
|
"pingora-proxy",
|
||||||
"privdrop",
|
"privdrop",
|
||||||
"prometheus 0.14.0",
|
"prometheus 0.14.0",
|
||||||
"rand 0.10.0",
|
"rand 0.10.1",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -256,9 +258,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum-core",
|
"axum-core",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -468,7 +470,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6565523d8145e63e0cf1b397a5f1bd4e90d5652a7dffb2de8cec460ff23ef6b1"
|
checksum = "6565523d8145e63e0cf1b397a5f1bd4e90d5652a7dffb2de8cec460ff23ef6b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"rand 0.10.0",
|
"rand 0.10.1",
|
||||||
"tokio",
|
"tokio",
|
||||||
"trackable",
|
"trackable",
|
||||||
]
|
]
|
||||||
@@ -483,7 +485,7 @@ dependencies = [
|
|||||||
"hostname",
|
"hostname",
|
||||||
"local-ip-address",
|
"local-ip-address",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand 0.10.0",
|
"rand 0.10.1",
|
||||||
"thrift_codec",
|
"thrift_codec",
|
||||||
"tokio",
|
"tokio",
|
||||||
"trackable",
|
"trackable",
|
||||||
@@ -645,6 +647,24 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.12"
|
version = "0.3.12"
|
||||||
@@ -1867,12 +1887,11 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libmimalloc-sys"
|
name = "libmimalloc-sys"
|
||||||
version = "0.1.44"
|
version = "0.1.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870"
|
checksum = "2d1eacfa31c33ec25e873c136ba5669f00f9866d0688bea7be4d3f7e43067df6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1965,9 +1984,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mimalloc"
|
name = "mimalloc"
|
||||||
version = "0.1.48"
|
version = "0.1.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8"
|
checksum = "b3627c4272df786b9260cabaa46aec1d59c93ede723d4c3ef646c503816b0640"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libmimalloc-sys",
|
"libmimalloc-sys",
|
||||||
]
|
]
|
||||||
@@ -2016,6 +2035,23 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "moka"
|
||||||
|
version = "0.12.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"equivalent",
|
||||||
|
"parking_lot",
|
||||||
|
"portable-atomic",
|
||||||
|
"smallvec",
|
||||||
|
"tagptr",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "neli"
|
name = "neli"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
@@ -2093,9 +2129,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify"
|
name = "notify"
|
||||||
version = "9.0.0-rc.2"
|
version = "9.0.0-rc.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8b6510a5042c64929d0278a8d24f5f90aa3a9b5be52e08e4f8bf7403adb01dc"
|
checksum = "783683ce6e1059e3747190a6c05688db21e07a846bf90babb351365f9133fe3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
"inotify",
|
"inotify",
|
||||||
@@ -2960,9 +2996,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
|
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"getrandom 0.4.2",
|
"getrandom 0.4.2",
|
||||||
@@ -3060,6 +3096,7 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2",
|
||||||
@@ -3672,6 +3709,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tagptr"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
@@ -3789,9 +3832,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.51.1"
|
version = "1.52.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c"
|
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -4082,6 +4125,17 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.4.2",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|||||||
14
Cargo.toml
14
Cargo.toml
@@ -11,7 +11,7 @@ panic = "abort"
|
|||||||
strip = true
|
strip = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.51.1", features = ["full"] }
|
tokio = { version = "1.52.1", features = ["full"] }
|
||||||
pingora = { version = "0.8.0", features = ["lb", "openssl"] } # openssl, rustls, boringssl
|
pingora = { version = "0.8.0", features = ["lb", "openssl"] } # openssl, rustls, boringssl
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
dashmap = "7.0.0-rc2"
|
dashmap = "7.0.0-rc2"
|
||||||
@@ -23,11 +23,11 @@ async-trait = "0.1.89"
|
|||||||
env_logger = "0.11.10"
|
env_logger = "0.11.10"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
futures = "0.3.32"
|
futures = "0.3.32"
|
||||||
notify = "9.0.0-rc.2"
|
notify = "9.0.0-rc.3"
|
||||||
axum = { version = "0.8.8" }
|
axum = { version = "0.8.9" }
|
||||||
reqwest = { version = "0.13.2", features = ["json", "stream"] }
|
reqwest = { version = "0.13.2", features = ["json", "stream", "blocking"] }
|
||||||
serde_yml = "0.0.12"
|
serde_yml = "0.0.12"
|
||||||
rand = "0.10.0"
|
rand = "0.10.1"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
jsonwebtoken = { version = "10.3.0", default-features = false, features = ["use_pem", "rust_crypto"] }
|
jsonwebtoken = { version = "10.3.0", default-features = false, features = ["use_pem", "rust_crypto"] }
|
||||||
tonic = "0.14.5"
|
tonic = "0.14.5"
|
||||||
@@ -35,7 +35,7 @@ sha2 = { version = "0.11.0-rc.5", default-features = false }
|
|||||||
base16ct = { version = "1.0.0", features = ["alloc"] }
|
base16ct = { version = "1.0.0", features = ["alloc"] }
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
arc-swap = "1.9.1"
|
arc-swap = "1.9.1"
|
||||||
mimalloc = { version = "0.1.48", default-features = false }
|
mimalloc = { version = "0.1.50", default-features = false }
|
||||||
prometheus = "0.14.0"
|
prometheus = "0.14.0"
|
||||||
x509-parser = "0.18.1"
|
x509-parser = "0.18.1"
|
||||||
rustls-pemfile = "2.2.0"
|
rustls-pemfile = "2.2.0"
|
||||||
@@ -44,3 +44,5 @@ privdrop = "0.5.6"
|
|||||||
ctrlc = "3.5.2"
|
ctrlc = "3.5.2"
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
subtle = "2.6.1"
|
subtle = "2.6.1"
|
||||||
|
moka = { version = "0.12.1", features = ["sync"] }
|
||||||
|
ahash = "0.8.12"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
mod tls;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
|
|||||||
2
src/tls.rs
Normal file
2
src/tls.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod grades;
|
||||||
|
pub mod load;
|
||||||
75
src/tls/grades.rs
Normal file
75
src/tls/grades.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use log::{info, warn};
|
||||||
|
use pingora::tls::ssl::{select_next_proto, AlpnError, SslRef, SslVersion};
|
||||||
|
use pingora_core::listeners::tls::TlsSettings;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CipherSuite {
|
||||||
|
pub high: &'static str,
|
||||||
|
pub medium: &'static str,
|
||||||
|
pub legacy: &'static str,
|
||||||
|
}
|
||||||
|
const CIPHERS: CipherSuite = CipherSuite {
|
||||||
|
high: "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305",
|
||||||
|
medium: "ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:AES128-GCM-SHA256",
|
||||||
|
legacy: "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH",
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TlsGrade {
|
||||||
|
HIGH,
|
||||||
|
MEDIUM,
|
||||||
|
LEGACY,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TlsGrade {
|
||||||
|
pub fn from_str(s: &str) -> Option<Self> {
|
||||||
|
match s.to_ascii_lowercase().as_str() {
|
||||||
|
"high" => Some(TlsGrade::HIGH),
|
||||||
|
"medium" => Some(TlsGrade::MEDIUM),
|
||||||
|
"unsafe" => Some(TlsGrade::LEGACY),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn prefer_h2<'a>(_ssl: &mut SslRef, alpn_in: &'a [u8]) -> Result<&'a [u8], AlpnError> {
|
||||||
|
match select_next_proto("\x02h2\x08http/1.1".as_bytes(), alpn_in) {
|
||||||
|
Some(p) => Ok(p),
|
||||||
|
_ => Err(AlpnError::NOACK),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_tsl_grade(tls_settings: &mut TlsSettings, grade: &str) {
|
||||||
|
let config_grade = TlsGrade::from_str(grade);
|
||||||
|
match config_grade {
|
||||||
|
Some(TlsGrade::HIGH) => {
|
||||||
|
let _ = tls_settings.set_min_proto_version(Some(SslVersion::TLS1_2));
|
||||||
|
// let _ = tls_settings.set_max_proto_version(Some(SslVersion::TLS1_3));
|
||||||
|
let _ = tls_settings.set_cipher_list(CIPHERS.high);
|
||||||
|
// let _ = tls_settings.set_ciphersuites(CIPHERS.high);
|
||||||
|
let _ = tls_settings.set_cipher_list(CIPHERS.high);
|
||||||
|
info!("TLS grade: {:?}, => HIGH", tls_settings.options());
|
||||||
|
}
|
||||||
|
Some(TlsGrade::MEDIUM) => {
|
||||||
|
let _ = tls_settings.set_min_proto_version(Some(SslVersion::TLS1));
|
||||||
|
let _ = tls_settings.set_cipher_list(CIPHERS.medium);
|
||||||
|
// let _ = tls_settings.set_ciphersuites(CIPHERS.medium);
|
||||||
|
let _ = tls_settings.set_cipher_list(CIPHERS.medium);
|
||||||
|
info!("TLS grade: {:?}, => MEDIUM", tls_settings.options());
|
||||||
|
}
|
||||||
|
Some(TlsGrade::LEGACY) => {
|
||||||
|
let _ = tls_settings.set_min_proto_version(Some(SslVersion::SSL3));
|
||||||
|
let _ = tls_settings.set_cipher_list(CIPHERS.legacy);
|
||||||
|
// let _ = tls_settings.set_ciphersuites(CIPHERS.legacy);
|
||||||
|
let _ = tls_settings.set_cipher_list(CIPHERS.legacy);
|
||||||
|
warn!("TLS grade: {:?}, => UNSAFE", tls_settings.options());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Defaults to MEDIUM
|
||||||
|
let _ = tls_settings.set_min_proto_version(Some(SslVersion::TLS1));
|
||||||
|
let _ = tls_settings.set_cipher_list(CIPHERS.medium);
|
||||||
|
// let _ = tls_settings.set_ciphersuites(CIPHERS.medium);
|
||||||
|
let _ = tls_settings.set_cipher_list(CIPHERS.medium);
|
||||||
|
warn!("TLS grade is not detected defaulting top MEDIUM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
use crate::tls::grades;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use log::{error, info, warn};
|
use log::error;
|
||||||
use pingora::tls::ssl::{select_next_proto, AlpnError, NameType, SniError, SslAlert, SslContext, SslFiletype, SslMethod, SslRef, SslVersion};
|
use pingora::tls::ssl::{NameType, SniError, SslAlert, SslContext, SslFiletype, SslMethod, SslRef};
|
||||||
use pingora_core::listeners::tls::TlsSettings;
|
|
||||||
use rustls_pemfile::{read_one, Item};
|
use rustls_pemfile::{read_one, Item};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@@ -10,7 +10,6 @@ use std::io::BufReader;
|
|||||||
use x509_parser::extensions::GeneralName;
|
use x509_parser::extensions::GeneralName;
|
||||||
use x509_parser::nom::Err as NomErr;
|
use x509_parser::nom::Err as NomErr;
|
||||||
use x509_parser::prelude::*;
|
use x509_parser::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Debug)]
|
#[derive(Clone, Deserialize, Debug)]
|
||||||
pub struct CertificateConfig {
|
pub struct CertificateConfig {
|
||||||
pub cert_path: String,
|
pub cert_path: String,
|
||||||
@@ -180,79 +179,7 @@ fn create_ssl_context(cert_path: &str, key_path: &str) -> Result<SslContext, Box
|
|||||||
let mut ctx = SslContext::builder(SslMethod::tls())?;
|
let mut ctx = SslContext::builder(SslMethod::tls())?;
|
||||||
ctx.set_certificate_chain_file(cert_path)?;
|
ctx.set_certificate_chain_file(cert_path)?;
|
||||||
ctx.set_private_key_file(key_path, SslFiletype::PEM)?;
|
ctx.set_private_key_file(key_path, SslFiletype::PEM)?;
|
||||||
ctx.set_alpn_select_callback(prefer_h2);
|
ctx.set_alpn_select_callback(grades::prefer_h2);
|
||||||
let built = ctx.build();
|
let built = ctx.build();
|
||||||
Ok(built)
|
Ok(built)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CipherSuite {
|
|
||||||
pub high: &'static str,
|
|
||||||
pub medium: &'static str,
|
|
||||||
pub legacy: &'static str,
|
|
||||||
}
|
|
||||||
const CIPHERS: CipherSuite = CipherSuite {
|
|
||||||
high: "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305",
|
|
||||||
medium: "ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:AES128-GCM-SHA256",
|
|
||||||
legacy: "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH",
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum TlsGrade {
|
|
||||||
HIGH,
|
|
||||||
MEDIUM,
|
|
||||||
LEGACY,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TlsGrade {
|
|
||||||
pub fn from_str(s: &str) -> Option<Self> {
|
|
||||||
match s.to_ascii_lowercase().as_str() {
|
|
||||||
"high" => Some(TlsGrade::HIGH),
|
|
||||||
"medium" => Some(TlsGrade::MEDIUM),
|
|
||||||
"unsafe" => Some(TlsGrade::LEGACY),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn prefer_h2<'a>(_ssl: &mut SslRef, alpn_in: &'a [u8]) -> Result<&'a [u8], AlpnError> {
|
|
||||||
match select_next_proto("\x02h2\x08http/1.1".as_bytes(), alpn_in) {
|
|
||||||
Some(p) => Ok(p),
|
|
||||||
_ => Err(AlpnError::NOACK),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_tsl_grade(tls_settings: &mut TlsSettings, grade: &str) {
|
|
||||||
let config_grade = TlsGrade::from_str(grade);
|
|
||||||
match config_grade {
|
|
||||||
Some(TlsGrade::HIGH) => {
|
|
||||||
let _ = tls_settings.set_min_proto_version(Some(SslVersion::TLS1_2));
|
|
||||||
// let _ = tls_settings.set_max_proto_version(Some(SslVersion::TLS1_3));
|
|
||||||
let _ = tls_settings.set_cipher_list(CIPHERS.high);
|
|
||||||
// let _ = tls_settings.set_ciphersuites(CIPHERS.high);
|
|
||||||
let _ = tls_settings.set_cipher_list(CIPHERS.high);
|
|
||||||
info!("TLS grade: {:?}, => HIGH", tls_settings.options());
|
|
||||||
}
|
|
||||||
Some(TlsGrade::MEDIUM) => {
|
|
||||||
let _ = tls_settings.set_min_proto_version(Some(SslVersion::TLS1));
|
|
||||||
let _ = tls_settings.set_cipher_list(CIPHERS.medium);
|
|
||||||
// let _ = tls_settings.set_ciphersuites(CIPHERS.medium);
|
|
||||||
let _ = tls_settings.set_cipher_list(CIPHERS.medium);
|
|
||||||
info!("TLS grade: {:?}, => MEDIUM", tls_settings.options());
|
|
||||||
}
|
|
||||||
Some(TlsGrade::LEGACY) => {
|
|
||||||
let _ = tls_settings.set_min_proto_version(Some(SslVersion::SSL3));
|
|
||||||
let _ = tls_settings.set_cipher_list(CIPHERS.legacy);
|
|
||||||
// let _ = tls_settings.set_ciphersuites(CIPHERS.legacy);
|
|
||||||
let _ = tls_settings.set_cipher_list(CIPHERS.legacy);
|
|
||||||
warn!("TLS grade: {:?}, => UNSAFE", tls_settings.options());
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// Defaults to MEDIUM
|
|
||||||
let _ = tls_settings.set_min_proto_version(Some(SslVersion::TLS1));
|
|
||||||
let _ = tls_settings.set_cipher_list(CIPHERS.medium);
|
|
||||||
// let _ = tls_settings.set_ciphersuites(CIPHERS.medium);
|
|
||||||
let _ = tls_settings.set_cipher_list(CIPHERS.medium);
|
|
||||||
warn!("TLS grade is not detected defaulting top MEDIUM");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,5 @@ pub mod metrics;
|
|||||||
pub mod parceyaml;
|
pub mod parceyaml;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod structs;
|
pub mod structs;
|
||||||
pub mod tls;
|
|
||||||
pub mod tools;
|
pub mod tools;
|
||||||
// pub mod watchksecret;
|
// pub mod watchksecret;
|
||||||
|
|||||||
@@ -1,21 +1,157 @@
|
|||||||
use crate::utils::jwt::check_jwt;
|
use crate::utils::jwt::check_jwt;
|
||||||
|
// use reqwest::Client;
|
||||||
|
use axum::http::StatusCode;
|
||||||
use base64::engine::general_purpose::STANDARD;
|
use base64::engine::general_purpose::STANDARD;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use pingora_proxy::Session;
|
use pingora_proxy::Session;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, LazyLock};
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
use urlencoding::decode;
|
use urlencoding::decode;
|
||||||
|
|
||||||
|
// use pingora::http::{RequestHeader, ResponseHeader, StatusCode};
|
||||||
|
use pingora::http::RequestHeader;
|
||||||
|
// --------------------------------- //
|
||||||
|
use pingora_core::connectors::http::Connector;
|
||||||
|
use pingora_core::upstreams::peer::HttpPeer;
|
||||||
|
use pingora_http::ResponseHeader;
|
||||||
|
// --------------------------------- //
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
trait AuthValidator {
|
trait AuthValidator {
|
||||||
fn validate(&self, session: &Session) -> bool;
|
async fn validate(&self, session: &mut Session) -> bool;
|
||||||
}
|
}
|
||||||
struct BasicAuth<'a>(&'a str);
|
struct BasicAuth<'a>(&'a str);
|
||||||
struct ApiKeyAuth<'a>(&'a str);
|
struct ApiKeyAuth<'a>(&'a str);
|
||||||
struct JwtAuth<'a>(&'a str);
|
struct JwtAuth<'a>(&'a str);
|
||||||
|
struct ForwardAuth<'a>(&'a str);
|
||||||
|
|
||||||
|
pub static AUTH_CONNECTOR: LazyLock<Connector> = LazyLock::new(|| Connector::new(None));
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl AuthValidator for ForwardAuth<'_> {
|
||||||
|
async fn validate(&self, session: &mut Session) -> bool {
|
||||||
|
let method = match session.req_header().method.as_str() {
|
||||||
|
"HEAD" => "HEAD",
|
||||||
|
_ => "GET",
|
||||||
|
};
|
||||||
|
|
||||||
|
let auth_url = self.0;
|
||||||
|
|
||||||
|
let (plain, tls) = if let Some(p) = auth_url.strip_prefix("http://") {
|
||||||
|
(p, false)
|
||||||
|
} else if let Some(p) = auth_url.strip_prefix("https://") {
|
||||||
|
(p, true)
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (addr, uri) = if let Some(pos) = plain.find('/') {
|
||||||
|
(&plain[..pos], &plain[pos..])
|
||||||
|
} else {
|
||||||
|
(plain, "/")
|
||||||
|
};
|
||||||
|
|
||||||
|
let hp = match split_host_port(addr, tls) {
|
||||||
|
Some(hp) => hp,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let peer = HttpPeer::new((hp.0, hp.1), tls, hp.0.to_string());
|
||||||
|
|
||||||
|
let (mut http_session, _) = match AUTH_CONNECTOR.get_http_session(&peer).await {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("ForwardAuth: connect failed: {}", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut auth_req = match RequestHeader::build(method, uri.as_bytes(), None) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("ForwardAuth: failed to build request: {}", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// auth_req.headers = session.req_header().headers.clone();
|
||||||
|
auth_req.insert_header("Host", addr).ok();
|
||||||
|
auth_req.insert_header("X-Forwarded-Uri", uri).ok();
|
||||||
|
auth_req.insert_header("X-Forwarded-Method", session.req_header().method.as_str()).ok();
|
||||||
|
if let Some(auth) = session.req_header().headers.get("authorization") {
|
||||||
|
auth_req.insert_header("Authorization", auth.clone()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(cookie) = session.req_header().headers.get("cookie") {
|
||||||
|
auth_req.insert_header("Cookie", cookie.clone()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
if tls {
|
||||||
|
auth_req.insert_header("X-Forwarded-Proto", "https").ok();
|
||||||
|
} else {
|
||||||
|
auth_req.insert_header("X-Forwarded-Proto", "http").ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = http_session.write_request_header(Box::new(auth_req)).await {
|
||||||
|
log::warn!("ForwardAuth: write failed: {}", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = match http_session.read_response_header().await {
|
||||||
|
Ok(_) => http_session.response_header().map(|r| r.status.as_u16()).unwrap_or(500),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("ForwardAuth: read failed: {}", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let auth_headers_to_forward: Vec<(String, String)> = if let Some(resp_header) = http_session.response_header() {
|
||||||
|
resp_header
|
||||||
|
.headers
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(name, value)| {
|
||||||
|
let name_str = name.as_str();
|
||||||
|
if name_str.starts_with("x-") || name_str.starts_with("remote-") || name_str.starts_with("locat") {
|
||||||
|
value.to_str().ok().map(|v| (name_str.to_string(), v.to_string()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
AUTH_CONNECTOR.release_http_session(http_session, &peer, None).await;
|
||||||
|
|
||||||
|
if (200..300).contains(&status) {
|
||||||
|
for (name, value) in auth_headers_to_forward {
|
||||||
|
session.req_header_mut().insert_header(name, value).ok();
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else if status == 302 || status == 301 {
|
||||||
|
let resp = ResponseHeader::build(StatusCode::MOVED_PERMANENTLY, None);
|
||||||
|
match resp {
|
||||||
|
Ok(mut r) => {
|
||||||
|
for (name, value) in auth_headers_to_forward {
|
||||||
|
r.insert_header(name, value).ok();
|
||||||
|
}
|
||||||
|
let _ = r.insert_header("Content-Length", "0");
|
||||||
|
let _ = session.write_response_header(Box::new(r), true).await;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(_) => return false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
impl AuthValidator for BasicAuth<'_> {
|
impl AuthValidator for BasicAuth<'_> {
|
||||||
fn validate(&self, session: &Session) -> bool {
|
async fn validate(&self, session: &mut Session) -> bool {
|
||||||
if let Some(header) = session.get_header("authorization") {
|
if let Some(header) = session.get_header("authorization") {
|
||||||
if let Some(h) = header.to_str().ok() {
|
if let Some(h) = header.to_str().ok() {
|
||||||
if let Some((_, val)) = h.split_once(' ') {
|
if let Some((_, val)) = h.split_once(' ') {
|
||||||
@@ -31,8 +167,9 @@ impl AuthValidator for BasicAuth<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
impl AuthValidator for ApiKeyAuth<'_> {
|
impl AuthValidator for ApiKeyAuth<'_> {
|
||||||
fn validate(&self, session: &Session) -> bool {
|
async fn validate(&self, session: &mut Session) -> bool {
|
||||||
if let Some(header) = session.get_header("x-api-key") {
|
if let Some(header) = session.get_header("x-api-key") {
|
||||||
if let Some(h) = header.to_str().ok() {
|
if let Some(h) = header.to_str().ok() {
|
||||||
return h.as_bytes().ct_eq(self.0.as_bytes()).into();
|
return h.as_bytes().ct_eq(self.0.as_bytes()).into();
|
||||||
@@ -42,8 +179,9 @@ impl AuthValidator for ApiKeyAuth<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
impl AuthValidator for JwtAuth<'_> {
|
impl AuthValidator for JwtAuth<'_> {
|
||||||
fn validate(&self, session: &Session) -> bool {
|
async fn validate(&self, session: &mut Session) -> bool {
|
||||||
let jwtsecret = self.0;
|
let jwtsecret = self.0;
|
||||||
if let Some(tok) = get_query_param(session, "araleztoken") {
|
if let Some(tok) = get_query_param(session, "araleztoken") {
|
||||||
return check_jwt(tok.as_str(), jwtsecret);
|
return check_jwt(tok.as_str(), jwtsecret);
|
||||||
@@ -61,11 +199,12 @@ impl AuthValidator for JwtAuth<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticate(auth_type: &Arc<str>, credentials: &Arc<str>, session: &Session) -> bool {
|
pub async fn authenticate(auth_type: &Arc<str>, credentials: &Arc<str>, session: &mut Session) -> bool {
|
||||||
match &**auth_type {
|
match &**auth_type {
|
||||||
"basic" => BasicAuth(credentials).validate(session),
|
"basic" => BasicAuth(credentials).validate(session).await,
|
||||||
"apikey" => ApiKeyAuth(credentials).validate(session),
|
"apikey" => ApiKeyAuth(credentials).validate(session).await,
|
||||||
"jwt" => JwtAuth(credentials).validate(session),
|
"jwt" => JwtAuth(credentials).validate(session).await,
|
||||||
|
"forward" => ForwardAuth(credentials).validate(session).await,
|
||||||
_ => {
|
_ => {
|
||||||
log::warn!("Unsupported authentication mechanism : {}", auth_type);
|
log::warn!("Unsupported authentication mechanism : {}", auth_type);
|
||||||
false
|
false
|
||||||
@@ -73,7 +212,7 @@ pub fn authenticate(auth_type: &Arc<str>, credentials: &Arc<str>, session: &Sess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_query_param(session: &Session, key: &str) -> Option<String> {
|
pub fn get_query_param(session: &mut Session, key: &str) -> Option<String> {
|
||||||
let query = session.req_header().uri.query()?;
|
let query = session.req_header().uri.query()?;
|
||||||
|
|
||||||
let params: HashMap<_, _> = query
|
let params: HashMap<_, _> = query
|
||||||
@@ -87,3 +226,22 @@ pub fn get_query_param(session: &Session, key: &str) -> Option<String> {
|
|||||||
.collect();
|
.collect();
|
||||||
params.get(key).and_then(|v| decode(v).ok()).map(|s| s.to_string())
|
params.get(key).and_then(|v| decode(v).ok()).map(|s| s.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn split_host_port(addr: &str, tls: bool) -> Option<(&str, u16, bool, &str)> {
|
||||||
|
match addr.split_once(':') {
|
||||||
|
Some((h, p)) => match p.parse::<u16>() {
|
||||||
|
Ok(port) => return Some((h, port, tls, h)),
|
||||||
|
Err(_) => {
|
||||||
|
log::warn!("ForwardAuth: invalid port in {}", addr);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
if tls {
|
||||||
|
return Some((addr, 443u16, tls, addr));
|
||||||
|
} else {
|
||||||
|
return Some((addr, 80u16, tls, addr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,87 @@
|
|||||||
|
use ahash::AHasher;
|
||||||
|
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
|
||||||
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
||||||
|
use moka::sync::Cache;
|
||||||
|
use moka::Expiry;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
use std::time::{Duration, Instant, SystemTime};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub(crate) struct Claims {
|
pub struct Claims {
|
||||||
pub(crate) user: String,
|
pub master_key: String,
|
||||||
pub(crate) exp: u64,
|
pub owner: String,
|
||||||
|
pub exp: u64,
|
||||||
|
pub random: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Expired {
|
||||||
|
exp: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static JWT_VALIDATION: LazyLock<Validation> = LazyLock::new(|| Validation::new(Algorithm::HS256));
|
||||||
|
|
||||||
|
static JWT_CACHE: LazyLock<Cache<u64, u64>> = LazyLock::new(|| Cache::builder().max_capacity(100_000).expire_after(JwtExpiry).build());
|
||||||
|
struct JwtExpiry;
|
||||||
|
impl Expiry<u64, u64> for JwtExpiry {
|
||||||
|
fn expire_after_create(&self, _key: &u64, value: &u64, _current_time: Instant) -> Option<Duration> {
|
||||||
|
let now = SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_secs();
|
||||||
|
if *value > now {
|
||||||
|
Some(Duration::from_secs(value - now))
|
||||||
|
} else {
|
||||||
|
Some(Duration::ZERO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_jwt(token: &str, secret: &str) -> bool {
|
||||||
|
let key = hash_token(token, secret);
|
||||||
|
let now = SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_secs();
|
||||||
|
if let Some(exp) = JWT_CACHE.get(&key) {
|
||||||
|
if exp < now {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
match is_expired(token, now) {
|
||||||
|
Ok(true) => return false,
|
||||||
|
Ok(false) => {}
|
||||||
|
Err(_) => return false,
|
||||||
|
}
|
||||||
|
|
||||||
|
match decode::<Claims>(token, &DecodingKey::from_secret(secret.as_ref()), &JWT_VALIDATION) {
|
||||||
|
Ok(data) => {
|
||||||
|
let now = SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_secs();
|
||||||
|
if data.claims.exp > now {
|
||||||
|
JWT_CACHE.insert(key, data.claims.exp);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn check_jwt(input: &str, secret: &str) -> bool {
|
|
||||||
let validation = Validation::new(Algorithm::HS256);
|
|
||||||
let token_data = decode::<Claims>(&input, &DecodingKey::from_secret(secret.as_ref()), &validation);
|
|
||||||
match token_data {
|
|
||||||
Ok(_) => true,
|
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_expired(token: &str, now: u64) -> Result<bool, Box<dyn std::error::Error>> {
|
||||||
|
let parts: Vec<&str> = token.split('.').collect();
|
||||||
|
if parts.len() != 3 {
|
||||||
|
return Err("Invalid JWT format".into());
|
||||||
|
}
|
||||||
|
let decoded = URL_SAFE_NO_PAD.decode(parts[1])?;
|
||||||
|
let claims: Expired = serde_json::from_slice(&decoded)?;
|
||||||
|
if let Some(exp) = claims.exp {
|
||||||
|
Ok(exp < now)
|
||||||
|
} else {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_token(token: &str, secret: &str) -> u64 {
|
||||||
|
let mut hasher = AHasher::default();
|
||||||
|
token.hash(&mut hasher);
|
||||||
|
secret.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ pub struct HostConfig {
|
|||||||
pub struct Auth {
|
pub struct Auth {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub auth_type: String,
|
pub auth_type: String,
|
||||||
#[serde(rename = "creds")]
|
#[serde(rename = "data")]
|
||||||
pub auth_cred: String,
|
pub auth_cred: String,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use crate::tls::load;
|
||||||
|
use crate::tls::load::CertificateConfig;
|
||||||
use crate::utils::structs::{InnerMap, InnerMapForJson, UpstreamSnapshotForJson, UpstreamsDashMap, UpstreamsIdMap};
|
use crate::utils::structs::{InnerMap, InnerMapForJson, UpstreamSnapshotForJson, UpstreamsDashMap, UpstreamsIdMap};
|
||||||
use crate::utils::tls;
|
|
||||||
use crate::utils::tls::CertificateConfig;
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use notify::{event::ModifyKind, Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
use notify::{event::ModifyKind, Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
@@ -207,9 +207,9 @@ pub fn clone_idmap_into(original: &UpstreamsDashMap, cloned: &UpstreamsIdMap) {
|
|||||||
info!("Upstreams are fully populated. Ready to server requests");
|
info!("Upstreams are fully populated. Ready to server requests");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listdir(dir: String) -> Vec<tls::CertificateConfig> {
|
pub fn listdir(dir: String) -> Vec<load::CertificateConfig> {
|
||||||
let mut f = HashMap::new();
|
let mut f = HashMap::new();
|
||||||
let mut certificate_configs: Vec<tls::CertificateConfig> = vec![];
|
let mut certificate_configs: Vec<load::CertificateConfig> = vec![];
|
||||||
let paths = fs::read_dir(dir).unwrap();
|
let paths = fs::read_dir(dir).unwrap();
|
||||||
for path in paths {
|
for path in paths {
|
||||||
let path_str = path.unwrap().path().to_str().unwrap().to_owned();
|
let path_str = path.unwrap().path().to_str().unwrap().to_owned();
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ impl ProxyHttp for LB {
|
|||||||
None => return Ok(false),
|
None => return Ok(false),
|
||||||
Some(ref innermap) => {
|
Some(ref innermap) => {
|
||||||
if let Some(auth) = _ctx.extraparams.authentication.as_ref().or(innermap.authorization.as_ref()) {
|
if let Some(auth) = _ctx.extraparams.authentication.as_ref().or(innermap.authorization.as_ref()) {
|
||||||
if !authenticate(&auth.auth_type, &auth.auth_cred, &session) {
|
if !authenticate(&auth.auth_type, &auth.auth_cred, session).await {
|
||||||
let _ = session.respond_error(401).await;
|
let _ = session.respond_error(401).await;
|
||||||
warn!("Forbidden: {:?}, {}", session.client_addr(), session.req_header().uri.path());
|
warn!("Forbidden: {:?}, {}", session.client_addr(), session.req_header().uri.path());
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// use rustls::crypto::ring::default_provider;
|
// use rustls::crypto::ring::default_provider;
|
||||||
|
use crate::tls::grades;
|
||||||
|
use crate::tls::load;
|
||||||
|
use crate::tls::load::CertificateConfig;
|
||||||
use crate::utils::structs::Extraparams;
|
use crate::utils::structs::Extraparams;
|
||||||
use crate::utils::tls;
|
|
||||||
use crate::utils::tls::CertificateConfig;
|
|
||||||
use crate::utils::tools::*;
|
use crate::utils::tools::*;
|
||||||
use crate::web::proxyhttp::LB;
|
use crate::web::proxyhttp::LB;
|
||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
@@ -68,27 +69,27 @@ pub fn run() {
|
|||||||
watch_folder(certs_path, tx).unwrap();
|
watch_folder(certs_path, tx).unwrap();
|
||||||
});
|
});
|
||||||
let certificate_configs = rx.recv().unwrap();
|
let certificate_configs = rx.recv().unwrap();
|
||||||
let first_set = tls::Certificates::new(&certificate_configs, grade.as_str()).unwrap_or_else(|| panic!("Unable to load initial certificate info"));
|
let first_set = load::Certificates::new(&certificate_configs, grade.as_str()).unwrap_or_else(|| panic!("Unable to load initial certificate info"));
|
||||||
let certificates = Arc::new(ArcSwap::from_pointee(first_set));
|
let certificates = Arc::new(ArcSwap::from_pointee(first_set));
|
||||||
let certs_for_callback = certificates.clone();
|
let certs_for_callback = certificates.clone();
|
||||||
|
|
||||||
let certs_for_watcher = certificates.clone();
|
let certs_for_watcher = certificates.clone();
|
||||||
let new_certs = tls::Certificates::new(&certificate_configs, grade.as_str());
|
let new_certs = load::Certificates::new(&certificate_configs, grade.as_str());
|
||||||
certs_for_watcher.store(Arc::new(new_certs.unwrap()));
|
certs_for_watcher.store(Arc::new(new_certs.unwrap()));
|
||||||
|
|
||||||
let mut tls_settings =
|
let mut tls_settings =
|
||||||
TlsSettings::intermediate(&certs_for_callback.load().default_cert_path, &certs_for_callback.load().default_key_path).expect("unable to load or parse cert/key");
|
TlsSettings::intermediate(&certs_for_callback.load().default_cert_path, &certs_for_callback.load().default_key_path).expect("unable to load or parse cert/key");
|
||||||
|
|
||||||
tls::set_tsl_grade(&mut tls_settings, grade.as_str());
|
grades::set_tsl_grade(&mut tls_settings, grade.as_str());
|
||||||
tls_settings.set_servername_callback(move |ssl_ref: &mut SslRef, ssl_alert: &mut SslAlert| certs_for_callback.load().server_name_callback(ssl_ref, ssl_alert));
|
tls_settings.set_servername_callback(move |ssl_ref: &mut SslRef, ssl_alert: &mut SslAlert| certs_for_callback.load().server_name_callback(ssl_ref, ssl_alert));
|
||||||
tls_settings.set_alpn_select_callback(tls::prefer_h2);
|
tls_settings.set_alpn_select_callback(grades::prefer_h2);
|
||||||
|
|
||||||
proxy.add_tls_with_settings(&bind_address_tls, None, tls_settings);
|
proxy.add_tls_with_settings(&bind_address_tls, None, tls_settings);
|
||||||
|
|
||||||
let certs_for_watcher = certificates.clone();
|
let certs_for_watcher = certificates.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
while let Ok(new_configs) = rx.recv() {
|
while let Ok(new_configs) = rx.recv() {
|
||||||
let new_certs = tls::Certificates::new(&new_configs, grade.as_str());
|
let new_certs = load::Certificates::new(&new_configs, grade.as_str());
|
||||||
match new_certs {
|
match new_certs {
|
||||||
Some(new_certs) => {
|
Some(new_certs) => {
|
||||||
certs_for_watcher.store(Arc::new(new_certs));
|
certs_for_watcher.store(Arc::new(new_certs));
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::utils::discovery::APIUpstreamProvider;
|
use crate::utils::discovery::APIUpstreamProvider;
|
||||||
|
// use std::net::SocketAddr;
|
||||||
|
use crate::utils::jwt::Claims;
|
||||||
use crate::utils::structs::{Config, Configuration, UpstreamsDashMap};
|
use crate::utils::structs::{Config, Configuration, UpstreamsDashMap};
|
||||||
use crate::utils::tools::{upstreams_liveness_json, upstreams_to_json};
|
use crate::utils::tools::{upstreams_liveness_json, upstreams_to_json};
|
||||||
use axum::body::Body;
|
use axum::body::Body;
|
||||||
@@ -13,22 +15,14 @@ use futures::SinkExt;
|
|||||||
use jsonwebtoken::{encode, EncodingKey, Header};
|
use jsonwebtoken::{encode, EncodingKey, Header};
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use prometheus::{gather, Encoder, TextEncoder};
|
use prometheus::{gather, Encoder, TextEncoder};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Serialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
// use std::net::SocketAddr;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct InputKey {
|
|
||||||
master_key: String,
|
|
||||||
owner: String,
|
|
||||||
valid: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
struct OutToken {
|
struct OutToken {
|
||||||
token: String,
|
token: String,
|
||||||
@@ -119,15 +113,21 @@ async fn apply_config(content: &str, mut st: AppState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn jwt_gen(State(state): State<AppState>, Json(payload): Json<InputKey>) -> (StatusCode, Json<OutToken>) {
|
async fn jwt_gen(State(state): State<AppState>, Json(payload): Json<Claims>) -> (StatusCode, Json<OutToken>) {
|
||||||
if payload.master_key == state.master_key {
|
if payload.master_key == state.master_key {
|
||||||
let now = SystemTime::now() + Duration::from_secs(payload.valid * 60);
|
let now = SystemTime::now() + Duration::from_secs(payload.exp * 60);
|
||||||
let a = now.duration_since(UNIX_EPOCH).unwrap().as_secs();
|
let expire = now.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();
|
||||||
let claim = crate::utils::jwt::Claims { user: payload.owner, exp: a };
|
|
||||||
|
let claim = Claims {
|
||||||
|
master_key: String::new(),
|
||||||
|
owner: payload.owner,
|
||||||
|
exp: expire,
|
||||||
|
random: payload.random,
|
||||||
|
};
|
||||||
match encode(&Header::default(), &claim, &EncodingKey::from_secret(payload.master_key.as_ref())) {
|
match encode(&Header::default(), &claim, &EncodingKey::from_secret(payload.master_key.as_ref())) {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
let tok = OutToken { token: t };
|
let tok = OutToken { token: t };
|
||||||
info!("Generating token: {:?}", tok);
|
info!("Generating token: {:?}", tok.token);
|
||||||
(StatusCode::CREATED, Json(tok))
|
(StatusCode::CREATED, Json(tok))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user