mirror of
https://github.com/sadoyan/aralez.git
synced 2026-05-30 03:44:06 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0b6de9759 | ||
|
|
a70eb53bc1 | ||
|
|
bee307793c | ||
|
|
6e83775127 | ||
|
|
baded40e6e |
152
Cargo.lock
generated
152
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",
|
||||||
@@ -129,9 +130,11 @@ dependencies = [
|
|||||||
"dashmap",
|
"dashmap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
|
"instant-acme",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"log",
|
"log",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
|
"moka",
|
||||||
"notify",
|
"notify",
|
||||||
"pingora",
|
"pingora",
|
||||||
"pingora-core",
|
"pingora-core",
|
||||||
@@ -140,7 +143,8 @@ dependencies = [
|
|||||||
"pingora-proxy",
|
"pingora-proxy",
|
||||||
"privdrop",
|
"privdrop",
|
||||||
"prometheus 0.14.0",
|
"prometheus 0.14.0",
|
||||||
"rand 0.10.0",
|
"rand 0.10.1",
|
||||||
|
"rcgen",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -239,6 +243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc"
|
checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-sys",
|
"aws-lc-sys",
|
||||||
|
"untrusted 0.7.1",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -256,9 +261,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 +473,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 +488,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 +650,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"
|
||||||
@@ -1476,7 +1499,9 @@ dependencies = [
|
|||||||
"hyper",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"rustls",
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
"rustls-platform-verifier",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@@ -1677,6 +1702,32 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant-acme"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f05ad37c421b962354c358d347d4a6130151df9407978372d3ad7f0c8f71a64"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"aws-lc-rs",
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"httpdate",
|
||||||
|
"hyper",
|
||||||
|
"hyper-rustls",
|
||||||
|
"hyper-util",
|
||||||
|
"rcgen",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.12.0"
|
version = "2.12.0"
|
||||||
@@ -1867,12 +1918,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 +2015,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 +2066,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 +2160,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 +3027,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",
|
||||||
@@ -3013,6 +3080,21 @@ version = "0.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rcgen"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10b99e0098aa4082912d4c649628623db6aba77335e4f4569ff5083a6448b32e"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
|
"pem",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"time",
|
||||||
|
"x509-parser",
|
||||||
|
"yasna",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.18"
|
version = "0.5.18"
|
||||||
@@ -3115,7 +3197,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom 0.2.17",
|
"getrandom 0.2.17",
|
||||||
"libc",
|
"libc",
|
||||||
"untrusted",
|
"untrusted 0.9.0",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3279,7 +3361,7 @@ dependencies = [
|
|||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"untrusted",
|
"untrusted 0.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3673,6 +3755,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"
|
||||||
@@ -3790,9 +3878,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",
|
||||||
@@ -4047,6 +4135,12 @@ version = "0.2.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -4083,6 +4177,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"
|
||||||
@@ -4645,11 +4750,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
|
checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"asn1-rs",
|
"asn1-rs",
|
||||||
|
"aws-lc-rs",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"der-parser",
|
"der-parser",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"nom",
|
"nom",
|
||||||
"oid-registry",
|
"oid-registry",
|
||||||
|
"ring",
|
||||||
"rusticata-macros",
|
"rusticata-macros",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"time",
|
"time",
|
||||||
@@ -4661,6 +4768,15 @@ version = "0.8.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
|
checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yasna"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
|
||||||
|
dependencies = [
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
|||||||
12
Cargo.toml
12
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", "blocking"] }
|
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"
|
||||||
@@ -46,3 +46,5 @@ serde_json = "1.0.149"
|
|||||||
subtle = "2.6.1"
|
subtle = "2.6.1"
|
||||||
moka = { version = "0.12.1", features = ["sync"] }
|
moka = { version = "0.12.1", features = ["sync"] }
|
||||||
ahash = "0.8.12"
|
ahash = "0.8.12"
|
||||||
|
instant-acme = "0.8.5"
|
||||||
|
rcgen = "0.14.7"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Main configuration file, applied on startup
|
# Main configuration file, applied on startup
|
||||||
threads: 12 # Number of daemon threads default setting
|
threads: 12 # Number of daemon threads default setting
|
||||||
#runuser: pastor # Username for running aralez after dropping root privileges, requires program to start as root
|
runuser: aralez # Username for running aralez after dropping root privileges, requires program to start as root
|
||||||
#rungroup: pastor # Group for running aralez after dropping root privileges, requires program to start as root
|
rungroup: aralez # Group for running aralez after dropping root privileges, requires program to start as root
|
||||||
daemon: false # Run in background
|
daemon: false # Run in background
|
||||||
upstream_keepalive_pool_size: 500 # Pool size for upstream keepalive connections
|
upstream_keepalive_pool_size: 500 # Pool size for upstream keepalive connections
|
||||||
pid_file: /tmp/aralez.pid # Path to PID file
|
pid_file: /tmp/aralez.pid # Path to PID file
|
||||||
@@ -9,17 +9,14 @@ error_log: /tmp/aralez_err.log # Path to error log
|
|||||||
upgrade_sock: /tmp/aralez.sock # Path to socket file
|
upgrade_sock: /tmp/aralez.sock # Path to socket file
|
||||||
config_api_enabled: true # Boolean to enable/disable remote config push capability.
|
config_api_enabled: true # Boolean to enable/disable remote config push capability.
|
||||||
config_address: 0.0.0.0:3000 # HTTP API address for pushing upstreams.yaml from remote location
|
config_address: 0.0.0.0:3000 # HTTP API address for pushing upstreams.yaml from remote location
|
||||||
config_tls_address: 0.0.0.0:3001 # HTTP TLS API address for pushing upstreams.yaml from remote location
|
|
||||||
config_tls_certificate: /etc/server.crt # Mandatory if config_tls_address is set
|
|
||||||
config_tls_key_file: /etc/key.pem # Mandatory if config_tls_address is set
|
|
||||||
proxy_address_http: 0.0.0.0:6193 # Proxy HTTP bind address
|
proxy_address_http: 0.0.0.0:6193 # Proxy HTTP bind address
|
||||||
proxy_address_tls: 0.0.0.0:6194 # Optional, Proxy TLS bind address
|
proxy_address_tls: 0.0.0.0:6194 # Optional, Proxy TLS bind address
|
||||||
proxy_certificates: /etc/certs # Mandatory if proxy_address_tls set, should contain a certificate and key files strictly in a format {NAME}.crt, {NAME}.key.
|
proxy_configs: /opt/aralez/etc # Mandatory if proxy_address_tls set, should contain a certificate and key files strictly in a format {NAME}.crt, {NAME}.key.
|
||||||
proxy_tls_grade: a+ # Grade of TLS suite for proxy (a+, a, b, c, unsafe), matching grades of Qualys SSL Labs
|
proxy_tls_grade: a+ # Grade of TLS suite for proxy (a+, a, b, c, unsafe), matching grades of Qualys SSL Labs
|
||||||
upstreams_conf: /etc/upstreams.yaml # the location of upstreams file
|
upstreams_conf: /opt/aralez/etc/upstreams.yaml # the location of upstreams file
|
||||||
file_server_folder: /opt/storage # Optional, local folder to serve
|
file_server_folder: /opt/storage # Optional, local folder to serve
|
||||||
file_server_address: 127.0.0.1:3002 # Optional, Local address for file server. Can set as upstream for public access.
|
file_server_address: 127.0.0.1:3002 # Optional, Local address for file server. Can set as upstream for public access.
|
||||||
log_level: info # info, warn, error, debug, trace, off
|
log_level: info # info, warn, error, debug, trace, off
|
||||||
hc_method: HEAD # Healthcheck method (HEAD, GET, POST are supported) UPPERCASE
|
hc_method: HEAD # Healthcheck method (HEAD, GET, POST are supported) UPPERCASE
|
||||||
hc_interval: 2 #Interval for health checks in seconds
|
hc_interval: 2 #Interval for health checks in seconds
|
||||||
master_key: 910517d9-f9a1-48de-8826-dbadacbd84af-cb6f830e-ab16-47ec-9d8f-0090de732774 # Mater key for working with API server and JWT Secret
|
master_key: 910517d9-f9a1-48de-8826-dbadacbd84af-cb6f830e-ab16-47ec-9d8f-0090de732774 # Mater key for working with API server and JWT Secret
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
mod tls;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
|
|||||||
3
src/tls.rs
Normal file
3
src/tls.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod acme;
|
||||||
|
pub mod grades;
|
||||||
|
pub mod load;
|
||||||
2
src/tls/acme.rs
Normal file
2
src/tls/acme.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod account;
|
||||||
|
pub mod order;
|
||||||
58
src/tls/acme/account.rs
Normal file
58
src/tls/acme/account.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
use instant_acme::{Account, AccountCredentials, LetsEncrypt, NewAccount};
|
||||||
|
use log::info;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
static ACCOUNT: OnceLock<Account> = OnceLock::new();
|
||||||
|
|
||||||
|
pub async fn get_account(file: &str) -> Result<&'static Account, Box<dyn std::error::Error>> {
|
||||||
|
if let Some(account) = ACCOUNT.get() {
|
||||||
|
return Ok(account);
|
||||||
|
}
|
||||||
|
if let Some(credentials) = load_credentials(file) {
|
||||||
|
let acc_builder = Account::builder()?;
|
||||||
|
let account = acc_builder.from_credentials(credentials).await?;
|
||||||
|
let _ = ACCOUNT.set(account);
|
||||||
|
info!("Loaded existing ACME account");
|
||||||
|
} else {
|
||||||
|
info!("No existing credentials found, creating new account");
|
||||||
|
create_account(file).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ACCOUNT.get().ok_or("Failed to initialize account".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_account(file: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let new_account = NewAccount {
|
||||||
|
contact: &[],
|
||||||
|
terms_of_service_agreed: true,
|
||||||
|
only_return_existing: false,
|
||||||
|
};
|
||||||
|
let acc_builder = Account::builder()?;
|
||||||
|
let (account, credentials) = acc_builder.create(&new_account, LetsEncrypt::Production.url().to_string(), None).await?;
|
||||||
|
// let (account, credentials) = acc_builder.create(&new_account, LetsEncrypt::Staging.url().to_string(), None).await?;
|
||||||
|
info!("Account created: {:?}", account.id());
|
||||||
|
save_credentials(&credentials, file)?;
|
||||||
|
let _ = ACCOUNT.set(account);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load_or_create(file: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let account = get_account(file).await?;
|
||||||
|
Ok(account.id().to_string() + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_credentials(credentials: &AccountCredentials, file: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let json = serde_json::to_string_pretty(credentials)?;
|
||||||
|
fs::write(file, json)?;
|
||||||
|
info!("ACME credentials saved to {}", file);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn load_credentials(file: &str) -> Option<AccountCredentials> {
|
||||||
|
if !Path::new(file).exists() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let json = fs::read_to_string(file).ok()?;
|
||||||
|
serde_json::from_str(&json).ok()
|
||||||
|
}
|
||||||
94
src/tls/acme/order.rs
Normal file
94
src/tls/acme/order.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
use crate::tls::acme::account::get_account;
|
||||||
|
use crate::utils::parceyaml::DOMAINS;
|
||||||
|
use instant_acme::{ChallengeType, Identifier, NewOrder, RetryPolicy};
|
||||||
|
use log::{error, info};
|
||||||
|
use pingora::prelude::sleep;
|
||||||
|
use rcgen::{CertificateParams, DistinguishedName, KeyPair};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::sync::{LazyLock, RwLock};
|
||||||
|
use std::time::Duration;
|
||||||
|
use x509_parser::prelude::*;
|
||||||
|
|
||||||
|
pub static CHALLENGES: LazyLock<RwLock<HashMap<String, String>>> = LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
|
|
||||||
|
pub async fn refresh_order(certs_dir: String, autoconf_dir: String) {
|
||||||
|
let credsfile = autoconf_dir + "/acme_credentials.json";
|
||||||
|
loop {
|
||||||
|
for item in DOMAINS.iter() {
|
||||||
|
let _what = order(item.key(), credsfile.as_str(), certs_dir.clone()).await;
|
||||||
|
}
|
||||||
|
sleep(Duration::from_secs(12 * 3600)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn order(domain: &str, credsfile: &str, certs_dir: String) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let crt = certs_dir.clone() + "/" + domain + ".crt";
|
||||||
|
let key = certs_dir.clone() + "/" + domain + ".key";
|
||||||
|
|
||||||
|
if let None = DOMAINS.get(domain) {
|
||||||
|
DOMAINS.insert(domain.to_string(), true);
|
||||||
|
let mut newlist: Vec<String> = Vec::new();
|
||||||
|
for item in DOMAINS.iter() {
|
||||||
|
newlist.push(item.key().to_string());
|
||||||
|
}
|
||||||
|
if let Ok(json_content) = serde_json::to_string_pretty(&newlist) {
|
||||||
|
let autocfg_file = credsfile.replace("/acme_credentials.json", "/domains.json");
|
||||||
|
if let Err(err) = std::fs::write(&autocfg_file, json_content) {
|
||||||
|
error!("Error Updating domains for certificates: {} : {}", domain, err);
|
||||||
|
return Err(Box::from(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = match cert_expiry(crt.as_str()) {
|
||||||
|
Ok(expiry) => {
|
||||||
|
let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_secs();
|
||||||
|
if expiry > now + 30 * 24 * 3600 {
|
||||||
|
// println!("Fresh certificate exists. Not renewing !");
|
||||||
|
return Ok("Fresh certificate exists. Not renewing ! \n".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let account = get_account(credsfile).await?;
|
||||||
|
let mut order = account.new_order(&NewOrder::new(&[Identifier::Dns(domain.to_string())])).await?;
|
||||||
|
|
||||||
|
let mut authorizations = order.authorizations();
|
||||||
|
while let Some(auth) = authorizations.next().await {
|
||||||
|
let mut auth = auth?;
|
||||||
|
let mut challenge_handle = auth.challenge(ChallengeType::Http01).ok_or("no http01 challenge found")?;
|
||||||
|
let key_auth = challenge_handle.key_authorization();
|
||||||
|
let key_auth_str = key_auth.as_str().to_string();
|
||||||
|
let token = key_auth_str.split('.').next().ok_or("invalid key authorization")?.to_string();
|
||||||
|
CHALLENGES.write().unwrap().insert(token, key_auth_str);
|
||||||
|
challenge_handle.set_ready().await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = order.poll_ready(&RetryPolicy::default()).await?;
|
||||||
|
info!("ACME poll_ready status: {:?}", status);
|
||||||
|
|
||||||
|
let mut params = CertificateParams::new(vec![domain.to_owned()])?;
|
||||||
|
params.distinguished_name = DistinguishedName::new();
|
||||||
|
let private_key = KeyPair::generate()?;
|
||||||
|
let signing_request = params.serialize_request(&private_key)?;
|
||||||
|
let csr_der = signing_request.der();
|
||||||
|
order.finalize_csr(&csr_der).await?;
|
||||||
|
|
||||||
|
// poll for certificate
|
||||||
|
let cert_chain_pem = order.poll_certificate(&RetryPolicy::default()).await?;
|
||||||
|
CHALLENGES.write().unwrap().clear();
|
||||||
|
let private_key_pem = private_key.serialize_pem();
|
||||||
|
|
||||||
|
fs::write(crt, cert_chain_pem)?;
|
||||||
|
fs::write(key, private_key_pem)?;
|
||||||
|
Ok("Certificate is successfully generated \n".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cert_expiry(path: &str) -> Result<u64, Box<dyn std::error::Error>> {
|
||||||
|
let pem = fs::read(path)?;
|
||||||
|
let (_, pem) = parse_x509_pem(&pem)?;
|
||||||
|
let (_, cert) = parse_x509_certificate(&pem.contents)?;
|
||||||
|
let expiry = cert.validity().not_after.timestamp() as u64;
|
||||||
|
Ok(expiry)
|
||||||
|
}
|
||||||
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,
|
||||||
@@ -18,14 +17,14 @@ pub struct CertificateConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct CertificateInfo {
|
pub struct CertificateInfo {
|
||||||
common_names: Vec<String>,
|
pub common_names: Vec<String>,
|
||||||
alt_names: Vec<String>,
|
pub alt_names: Vec<String>,
|
||||||
ssl_context: SslContext,
|
pub ssl_context: SslContext,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
cert_path: String, // Only used for logging
|
pub cert_path: String, // Only used for logging
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
key_path: String, // Only used for logging
|
pub key_path: String, // Only used for logging
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -106,7 +105,7 @@ impl Certificates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_cert_info(cert_path: &str, key_path: &str, _grade: &str) -> Option<CertificateInfo> {
|
pub fn load_cert_info(cert_path: &str, key_path: &str, _grade: &str) -> Option<CertificateInfo> {
|
||||||
let mut common_names = HashSet::new();
|
let mut common_names = HashSet::new();
|
||||||
let mut alt_names = HashSet::new();
|
let mut alt_names = HashSet::new();
|
||||||
|
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod discovery;
|
pub mod discovery;
|
||||||
pub mod dnsclient;
|
|
||||||
mod filewatch;
|
mod filewatch;
|
||||||
pub mod fordebug;
|
pub mod fordebug;
|
||||||
pub mod healthcheck;
|
pub mod healthcheck;
|
||||||
@@ -11,6 +10,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;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ pub struct APIUpstreamProvider {
|
|||||||
pub config_api_enabled: bool,
|
pub config_api_enabled: bool,
|
||||||
pub address: String,
|
pub address: String,
|
||||||
pub masterkey: String,
|
pub masterkey: String,
|
||||||
|
pub certs_dir: String,
|
||||||
|
pub config_dir: String,
|
||||||
// pub tls_address: Option<String>,
|
// pub tls_address: Option<String>,
|
||||||
// pub tls_certificate: Option<String>,
|
// pub tls_certificate: Option<String>,
|
||||||
// pub tls_key_file: Option<String>,
|
// pub tls_key_file: Option<String>,
|
||||||
|
|||||||
@@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
use crate::utils::structs::InnerMap;
|
|
||||||
use dashmap::DashMap;
|
|
||||||
use hickory_client::client::{Client, ClientHandle};
|
|
||||||
use hickory_client::proto::rr::{DNSClass, Name, RecordType};
|
|
||||||
use hickory_client::proto::runtime::TokioRuntimeProvider;
|
|
||||||
use hickory_client::proto::udp::UdpClientStream;
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::atomic::AtomicUsize;
|
|
||||||
use std::time::Duration;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
type DnsError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
|
||||||
|
|
||||||
pub struct DnsClientPool {
|
|
||||||
clients: Vec<Mutex<DnsClient>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DnsClient {
|
|
||||||
client: Client,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn start2(mut toreturn: Sender<Configuration>, config: Arc<Configuration>) {
|
|
||||||
let k8s = config.kubernetes.clone();
|
|
||||||
match k8s {
|
|
||||||
Some(k8s) => {
|
|
||||||
let dnserver = k8s.servers.unwrap_or(vec!["127.0.0.1:53".to_string()]);
|
|
||||||
let headers = DashMap::new();
|
|
||||||
let end = dnserver.len() - 1;
|
|
||||||
let mut num = 0;
|
|
||||||
if end > 0 {
|
|
||||||
num = rand::rng().random_range(0..end);
|
|
||||||
}
|
|
||||||
let srv = dnserver.get(num).unwrap().to_string();
|
|
||||||
let pool = DnsClientPool::new(5, srv.clone()).await;
|
|
||||||
let u = UpstreamsDashMap::new();
|
|
||||||
if let Some(whitelist) = k8s.services {
|
|
||||||
loop {
|
|
||||||
let upstreams = UpstreamsDashMap::new();
|
|
||||||
for service in whitelist.iter() {
|
|
||||||
let ret = pool.query_srv(service.real.as_str(), srv.clone()).await;
|
|
||||||
match ret {
|
|
||||||
Ok(r) => {
|
|
||||||
upstreams.insert(service.proxy.clone(), r);
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("DNS query failed for {:?}: {:?}", service, e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !compare_dashmaps(&u, &upstreams) {
|
|
||||||
headers.clear();
|
|
||||||
for (k, v) in config.headers.clone() {
|
|
||||||
headers.insert(k.to_string(), v);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tosend: Configuration = Configuration {
|
|
||||||
upstreams: Default::default(),
|
|
||||||
headers: Default::default(),
|
|
||||||
consul: None,
|
|
||||||
kubernetes: None,
|
|
||||||
typecfg: "".to_string(),
|
|
||||||
extraparams: config.extraparams.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
clone_dashmap_into(&upstreams, &u);
|
|
||||||
clone_dashmap_into(&upstreams, &tosend.upstreams);
|
|
||||||
tosend.headers = headers.clone();
|
|
||||||
tosend.extraparams.authentication = config.extraparams.authentication.clone();
|
|
||||||
tosend.typecfg = config.typecfg.clone();
|
|
||||||
tosend.consul = config.consul.clone();
|
|
||||||
print_upstreams(&tosend.upstreams);
|
|
||||||
toreturn.send(tosend).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DnsClient {
|
|
||||||
pub async fn new(server: String) -> Result<Self, DnsError> {
|
|
||||||
let server_details = server;
|
|
||||||
let server: SocketAddr = server_details.parse().expect("Unable to parse socket address");
|
|
||||||
let conn = UdpClientStream::builder(server, TokioRuntimeProvider::default()).build();
|
|
||||||
let (client, bg) = Client::connect(conn).await.unwrap();
|
|
||||||
tokio::spawn(bg);
|
|
||||||
Ok(Self { client })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn query_srv(&mut self, name: &str) -> Result<DashMap<String, (Vec<InnerMap>, AtomicUsize)>, DnsError> {
|
|
||||||
let upstreams: DashMap<String, (Vec<InnerMap>, AtomicUsize)> = DashMap::new();
|
|
||||||
let mut values = Vec::new();
|
|
||||||
match tokio::time::timeout(Duration::from_secs(5), self.client.query(Name::from_str(name)?, DNSClass::IN, RecordType::SRV)).await {
|
|
||||||
Ok(Ok(response)) => {
|
|
||||||
for answer in response.answers() {
|
|
||||||
if let hickory_client::proto::rr::RData::SRV(srv) = answer.data() {
|
|
||||||
let to_add = InnerMap {
|
|
||||||
address: srv.target().to_string(),
|
|
||||||
port: srv.port(),
|
|
||||||
is_ssl: false,
|
|
||||||
is_http2: false,
|
|
||||||
to_https: false,
|
|
||||||
sticky_sessions: false,
|
|
||||||
rate_limit: None,
|
|
||||||
};
|
|
||||||
values.push(to_add);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
upstreams.insert("/".to_string(), (values, AtomicUsize::new(0)));
|
|
||||||
Ok(upstreams)
|
|
||||||
}
|
|
||||||
Ok(Err(e)) => Err(Box::new(e)),
|
|
||||||
Err(_) => Err("DNS query timed out".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DnsClientPool {
|
|
||||||
pub async fn new(pool_size: usize, server: String) -> Self {
|
|
||||||
let mut clients = Vec::with_capacity(pool_size);
|
|
||||||
for _ in 0..pool_size {
|
|
||||||
if let Ok(client) = DnsClient::new(server.clone()).await {
|
|
||||||
clients.push(Mutex::new(client));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self { clients }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn query_srv(&self, name: &str, server: String) -> Result<DashMap<String, (Vec<InnerMap>, AtomicUsize)>, DnsError> {
|
|
||||||
// Try to get an available client
|
|
||||||
for client_mutex in &self.clients {
|
|
||||||
if let Ok(mut client) = client_mutex.try_lock() {
|
|
||||||
let vay = client.query_srv(name).await;
|
|
||||||
match vay {
|
|
||||||
Ok(_) => return vay,
|
|
||||||
Err(_) => {
|
|
||||||
// If query fails, drop this client and create a new one
|
|
||||||
*client = match DnsClient::new(server).await {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
};
|
|
||||||
// Retry with the new client
|
|
||||||
return client.query_srv(name).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all clients are busy, wait for the first one with a timeout
|
|
||||||
match tokio::time::timeout(Duration::from_secs(2), self.clients[0].lock()).await {
|
|
||||||
Ok(mut client) => client.query_srv(name).await,
|
|
||||||
Err(_) => Err("All DNS clients are busy and timeout reached".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -1,37 +1,82 @@
|
|||||||
use ahash::AHasher;
|
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::sync::Cache;
|
||||||
|
use moka::Expiry;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::LazyLock;
|
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_CACHE: LazyLock<Cache<u64, bool>> = LazyLock::new(|| Cache::builder().max_capacity(100_000).time_to_live(std::time::Duration::from_secs(60)).build());
|
|
||||||
static JWT_VALIDATION: LazyLock<Validation> = LazyLock::new(|| Validation::new(Algorithm::HS256));
|
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());
|
||||||
pub fn check_jwt(input: &str, secret: &str) -> bool {
|
struct JwtExpiry;
|
||||||
let validation = Validation::new(Algorithm::HS256);
|
impl Expiry<u64, u64> for JwtExpiry {
|
||||||
let token_data = decode::<Claims>(&input, &DecodingKey::from_secret(secret.as_ref()), &validation);
|
fn expire_after_create(&self, _key: &u64, value: &u64, _current_time: Instant) -> Option<Duration> {
|
||||||
token_data.is_ok()
|
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 {
|
pub fn check_jwt(token: &str, secret: &str) -> bool {
|
||||||
let key = hash_token(token, secret);
|
let key = hash_token(token, secret);
|
||||||
if let Some(v) = JWT_CACHE.get(&key) {
|
let now = SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_secs();
|
||||||
return v;
|
if let Some(exp) = JWT_CACHE.get(&key) {
|
||||||
|
if exp < now {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
let result = decode::<Claims>(token, &DecodingKey::from_secret(secret.as_ref()), &JWT_VALIDATION).is_ok();
|
match is_expired(token, now) {
|
||||||
if result {
|
Ok(true) => return false,
|
||||||
JWT_CACHE.insert(key, true);
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_token(token: &str, secret: &str) -> u64 {
|
fn hash_token(token: &str, secret: &str) -> u64 {
|
||||||
|
|||||||
@@ -7,16 +7,35 @@ use log::{error, info, warn};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, LazyLock};
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
|
pub static DOMAINS: LazyLock<DashMap<String, bool>> = LazyLock::new(|| DashMap::new());
|
||||||
|
|
||||||
pub async fn load_configuration(d: &str, kind: &str) -> (Option<Configuration>, String) {
|
pub async fn load_configuration(d: &str, kind: &str) -> (Option<Configuration>, String) {
|
||||||
let mut conf_files = Vec::new();
|
let mut conf_files = Vec::new();
|
||||||
let yaml_data = match kind {
|
let yaml_data = match kind {
|
||||||
"filepath" => match fs::read_to_string(d) {
|
"filepath" => match fs::read_to_string(d) {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
let mut confdir = Path::new(d).parent().unwrap().to_path_buf();
|
let mut confdir = Path::new(d).parent().unwrap().to_path_buf();
|
||||||
|
let mut autocfg = Path::new(d).parent().unwrap().to_path_buf();
|
||||||
|
|
||||||
|
autocfg.push("autoconfigs");
|
||||||
|
if !fs::metadata(autocfg.clone()).is_ok() {
|
||||||
|
fs::create_dir_all(autocfg.clone()).ok();
|
||||||
|
}
|
||||||
|
autocfg.push("domains.json");
|
||||||
|
if autocfg.exists() {
|
||||||
|
let json: Option<Vec<String>> = fs::read_to_string(autocfg).ok().and_then(|s| serde_json::from_str(&s).ok());
|
||||||
|
if let Some(domains) = json {
|
||||||
|
for domain in domains {
|
||||||
|
DOMAINS.insert(domain, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
confdir.push("conf.d");
|
confdir.push("conf.d");
|
||||||
|
|
||||||
if let Ok(entries) = fs::read_dir(&confdir) {
|
if let Ok(entries) = fs::read_dir(&confdir) {
|
||||||
let mut paths: Vec<_> = entries
|
let mut paths: Vec<_> = entries
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ pub struct AppConfig {
|
|||||||
pub proxy_port_tls: Option<String>,
|
pub proxy_port_tls: Option<String>,
|
||||||
pub proxy_port: Option<String>,
|
pub proxy_port: Option<String>,
|
||||||
pub local_server: Option<(String, u16)>,
|
pub local_server: Option<(String, u16)>,
|
||||||
pub proxy_certificates: Option<String>,
|
pub proxy_configs: Option<String>,
|
||||||
pub proxy_tls_grade: Option<String>,
|
pub proxy_tls_grade: Option<String>,
|
||||||
pub file_server_address: Option<String>,
|
pub file_server_address: Option<String>,
|
||||||
pub file_server_folder: Option<String>,
|
pub file_server_folder: Option<String>,
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::tls::acme::order::refresh_order;
|
||||||
use crate::utils::discovery::{APIUpstreamProvider, ConsulProvider, Discovery, FromFileProvider, KubernetesProvider};
|
use crate::utils::discovery::{APIUpstreamProvider, ConsulProvider, Discovery, FromFileProvider, KubernetesProvider};
|
||||||
use crate::utils::parceyaml::load_configuration;
|
use crate::utils::parceyaml::load_configuration;
|
||||||
use crate::utils::structs::Configuration;
|
use crate::utils::structs::Configuration;
|
||||||
@@ -50,10 +51,16 @@ impl BackgroundService for LB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let confdir = self.config.proxy_configs.clone().unwrap_or_else(|| "/tmp".to_string()) + "/autoconfigs";
|
||||||
|
let certdir = self.config.proxy_configs.clone().unwrap_or_else(|| "/tmp".to_string()) + "/certificates";
|
||||||
|
|
||||||
let api_load = APIUpstreamProvider {
|
let api_load = APIUpstreamProvider {
|
||||||
address: self.config.config_address.clone(),
|
address: self.config.config_address.clone(),
|
||||||
masterkey: self.config.master_key.clone(),
|
masterkey: self.config.master_key.clone(),
|
||||||
config_api_enabled: self.config.config_api_enabled.clone(),
|
config_api_enabled: self.config.config_api_enabled.clone(),
|
||||||
|
// certs_dir: self.config.proxy_certificates.clone().unwrap_or_else(|| "/tmp".to_string()),
|
||||||
|
config_dir: confdir.clone(),
|
||||||
|
certs_dir: certdir.clone(),
|
||||||
// tls_address: self.config.config_tls_address.clone(),
|
// tls_address: self.config.config_tls_address.clone(),
|
||||||
// tls_certificate: self.config.config_tls_certificate.clone(),
|
// tls_certificate: self.config.config_tls_certificate.clone(),
|
||||||
// tls_key_file: self.config.config_tls_key_file.clone(),
|
// tls_key_file: self.config.config_tls_key_file.clone(),
|
||||||
@@ -62,6 +69,7 @@ impl BackgroundService for LB {
|
|||||||
current_upstreams: self.ump_upst.clone(),
|
current_upstreams: self.ump_upst.clone(),
|
||||||
full_upstreams: self.ump_full.clone(),
|
full_upstreams: self.ump_full.clone(),
|
||||||
};
|
};
|
||||||
|
// let crtdir = api_load.certs_dir.clone();
|
||||||
// let tx_api = tx.clone();
|
// let tx_api = tx.clone();
|
||||||
let _ = tokio::spawn(async move { api_load.start(tx_api).await });
|
let _ = tokio::spawn(async move { api_load.start(tx_api).await });
|
||||||
|
|
||||||
@@ -70,6 +78,7 @@ impl BackgroundService for LB {
|
|||||||
let im = self.ump_byid.clone();
|
let im = self.ump_byid.clone();
|
||||||
let (hc_method, hc_interval) = (self.config.hc_method.clone(), self.config.hc_interval);
|
let (hc_method, hc_interval) = (self.config.hc_method.clone(), self.config.hc_interval);
|
||||||
let _ = tokio::spawn(async move { healthcheck::hc2(uu, ff, im, (&*hc_method.to_string(), hc_interval.to_string().parse().unwrap())).await });
|
let _ = tokio::spawn(async move { healthcheck::hc2(uu, ff, im, (&*hc_method.to_string(), hc_interval.to_string().parse().unwrap())).await });
|
||||||
|
let _ = tokio::spawn(async move { refresh_order(certdir, confdir).await });
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -63,32 +64,33 @@ pub fn run() {
|
|||||||
Some(bind_address_tls) => {
|
Some(bind_address_tls) => {
|
||||||
check_priv(bind_address_tls.as_str());
|
check_priv(bind_address_tls.as_str());
|
||||||
let (tx, rx): (Sender<Vec<CertificateConfig>>, Receiver<Vec<CertificateConfig>>) = channel();
|
let (tx, rx): (Sender<Vec<CertificateConfig>>, Receiver<Vec<CertificateConfig>>) = channel();
|
||||||
let certs_path = cfg.proxy_certificates.clone().unwrap();
|
// let certs_path = cfg.proxy_certificates.clone().unwrap();
|
||||||
|
let certs_path = cfg.proxy_configs.clone().unwrap() + "/certificates";
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
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,34 +1,30 @@
|
|||||||
|
// use std::net::SocketAddr;
|
||||||
|
use crate::tls::acme::order::CHALLENGES;
|
||||||
|
// use axum_server::tls_openssl::OpenSSLConfig;
|
||||||
|
use crate::tls::acme::{account, order};
|
||||||
use crate::utils::discovery::APIUpstreamProvider;
|
use crate::utils::discovery::APIUpstreamProvider;
|
||||||
|
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;
|
||||||
use axum::extract::{Query, State};
|
use axum::extract::{Query, State};
|
||||||
use axum::http::{header::HeaderMap, Response, StatusCode};
|
use axum::http::{header::HeaderMap, Response, StatusCode};
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum::routing::{get, post};
|
use axum::routing::{any, get, post};
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
// use axum_server::tls_openssl::OpenSSLConfig;
|
|
||||||
use futures::channel::mpsc::Sender;
|
use futures::channel::mpsc::Sender;
|
||||||
use futures::SinkExt;
|
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,
|
||||||
@@ -37,6 +33,8 @@ struct OutToken {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
struct AppState {
|
||||||
master_key: String,
|
master_key: String,
|
||||||
|
cert_creds: String,
|
||||||
|
certs_dir: String,
|
||||||
config_sender: Sender<Configuration>,
|
config_sender: Sender<Configuration>,
|
||||||
config_api_enabled: bool,
|
config_api_enabled: bool,
|
||||||
current_upstreams: Arc<UpstreamsDashMap>,
|
current_upstreams: Arc<UpstreamsDashMap>,
|
||||||
@@ -45,8 +43,11 @@ struct AppState {
|
|||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
pub async fn run_server(config: &APIUpstreamProvider, mut to_return: Sender<Configuration>, upstreams_curr: Arc<UpstreamsDashMap>, upstreams_full: Arc<UpstreamsDashMap>) {
|
pub async fn run_server(config: &APIUpstreamProvider, mut to_return: Sender<Configuration>, upstreams_curr: Arc<UpstreamsDashMap>, upstreams_full: Arc<UpstreamsDashMap>) {
|
||||||
|
let credsfile = config.config_dir.clone() + "/acme_credentials.json";
|
||||||
let app_state = AppState {
|
let app_state = AppState {
|
||||||
master_key: config.masterkey.clone(),
|
master_key: config.masterkey.clone(),
|
||||||
|
cert_creds: credsfile,
|
||||||
|
certs_dir: config.certs_dir.clone(),
|
||||||
config_sender: to_return.clone(),
|
config_sender: to_return.clone(),
|
||||||
config_api_enabled: config.config_api_enabled.clone(),
|
config_api_enabled: config.config_api_enabled.clone(),
|
||||||
current_upstreams: upstreams_curr,
|
current_upstreams: upstreams_curr,
|
||||||
@@ -60,6 +61,9 @@ pub async fn run_server(config: &APIUpstreamProvider, mut to_return: Sender<Conf
|
|||||||
// .route("/{*wildcard}", delete(senderror))
|
// .route("/{*wildcard}", delete(senderror))
|
||||||
// .nest_service("/static", static_files)
|
// .nest_service("/static", static_files)
|
||||||
.route("/jwt", post(jwt_gen))
|
.route("/jwt", post(jwt_gen))
|
||||||
|
.route("/acme_create", any(acme_create))
|
||||||
|
.route("/acme_order/{*domain}", any(acme_order))
|
||||||
|
.route("/.well-known/acme-challenge/{*token}", any(http01_challenge))
|
||||||
.route("/conf", post(conf))
|
.route("/conf", post(conf))
|
||||||
.route("/metrics", get(metrics))
|
.route("/metrics", get(metrics))
|
||||||
.route("/status", get(status))
|
.route("/status", get(status))
|
||||||
@@ -93,19 +97,18 @@ async fn conf(State(st): State<AppState>, Query(params): Query<HashMap<String, S
|
|||||||
if !st.config_api_enabled {
|
if !st.config_api_enabled {
|
||||||
return Response::builder().status(StatusCode::FORBIDDEN).body(Body::from("Config API is disabled !\n")).unwrap();
|
return Response::builder().status(StatusCode::FORBIDDEN).body(Body::from("Config API is disabled !\n")).unwrap();
|
||||||
}
|
}
|
||||||
if let Some(s) = headers.get("x-api-key").and_then(|v| v.to_str().ok()).or(params.get("key").map(|s| s.as_str())) {
|
// if let Some(s) = headers.get("x-api-key").and_then(|v| v.to_str().ok()).or(params.get("key").map(|s| s.as_str())) {
|
||||||
if s.as_bytes().ct_eq(st.master_key.as_bytes()).into() {
|
if key_authorization(&headers, ¶ms, &st.master_key) {
|
||||||
let strcontent = content.as_str();
|
let strcontent = content.as_str();
|
||||||
let parsed = serde_yml::from_str::<Config>(strcontent);
|
let parsed = serde_yml::from_str::<Config>(strcontent);
|
||||||
match parsed {
|
match parsed {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let _ = tokio::spawn(async move { apply_config(content.as_str(), st).await });
|
let _ = tokio::spawn(async move { apply_config(content.as_str(), st).await });
|
||||||
return Response::builder().status(StatusCode::OK).body(Body::from("Accepted! Applying in background\n")).unwrap();
|
return Response::builder().status(StatusCode::OK).body(Body::from("Accepted! Applying in background\n")).unwrap();
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to parse upstreams file: {}", err);
|
error!("Failed to parse upstreams file: {}", err);
|
||||||
return Response::builder().status(StatusCode::BAD_GATEWAY).body(Body::from(format!("Failed: {}\n", err))).unwrap();
|
return Response::builder().status(StatusCode::BAD_GATEWAY).body(Body::from(format!("Failed: {}\n", err))).unwrap();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,15 +122,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) => {
|
||||||
@@ -192,6 +201,85 @@ async fn status(State(st): State<AppState>, Query(params): Query<HashMap<String,
|
|||||||
}
|
}
|
||||||
Response::builder()
|
Response::builder()
|
||||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
.body(Body::from(format!("Parameter mismatch")))
|
.body(Body::from(format!("{}", "Parameter mismatch")))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn acme_create(State(state): State<AppState>, Query(params): Query<HashMap<String, String>>, headers: HeaderMap) -> impl IntoResponse {
|
||||||
|
if !key_authorization(&headers, ¶ms, &state.master_key) {
|
||||||
|
return Response::builder().status(StatusCode::FORBIDDEN).body(Body::from("Access Denied !\n")).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = match account::load_or_create(state.cert_creds.as_str()).await {
|
||||||
|
Ok(txt) => {
|
||||||
|
return Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.header("Content-Type", "text/plain")
|
||||||
|
.body(Body::from(txt))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Response::builder()
|
||||||
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
.body(Body::from(format!("Failed to create account: {}", e)))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async fn acme_order(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
axum::extract::Path(domain): axum::extract::Path<String>,
|
||||||
|
Query(params): Query<HashMap<String, String>>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
if !key_authorization(&headers, ¶ms, &state.master_key) {
|
||||||
|
return Response::builder().status(StatusCode::FORBIDDEN).body(Body::from("Access Denied !\n")).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let domain_clean = domain.trim_matches('/');
|
||||||
|
let _ = match order::order(domain_clean, state.cert_creds.as_str(), state.certs_dir).await {
|
||||||
|
Ok(txt) => {
|
||||||
|
return Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.header("Content-Type", "text/plain")
|
||||||
|
.body(Body::from(txt))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Response::builder()
|
||||||
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
.body(Body::from(format!("Failed to order a certificate: {}", e)))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn http01_challenge(axum::extract::Path(token): axum::extract::Path<String>) -> impl IntoResponse {
|
||||||
|
if let Ok(challenges) = CHALLENGES.read() {
|
||||||
|
// for k in challenges.iter() {
|
||||||
|
// println!(" ==> {} : {}", k.0, k.1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if let Some(key_authorization) = challenges.get(&token) {
|
||||||
|
return Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.header("Content-Type", "text/plain")
|
||||||
|
.body(Body::from(key_authorization.clone()))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Response::builder()
|
||||||
|
.status(StatusCode::NOT_FOUND)
|
||||||
|
.header("Content-Type", "text/plain")
|
||||||
|
.body(Body::from("Not found"))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_authorization(headers: &HeaderMap, params: &HashMap<String, String>, masterkey: &str) -> bool {
|
||||||
|
if let Some(s) = headers.get("x-api-key").and_then(|v| v.to_str().ok()).or(params.get("key").map(|s| s.as_str())) {
|
||||||
|
if s.as_bytes().ct_eq(masterkey.as_bytes()).into() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user