mirror of
https://github.com/sadoyan/aralez.git
synced 2026-04-29 22:38:36 +08:00
JWT Authentication and token generation
This commit is contained in:
139
Cargo.lock
generated
139
Cargo.lock
generated
@@ -165,6 +165,7 @@ dependencies = [
|
|||||||
"dashmap",
|
"dashmap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
|
"jsonwebtoken",
|
||||||
"log",
|
"log",
|
||||||
"notify",
|
"notify",
|
||||||
"pingora",
|
"pingora",
|
||||||
@@ -513,6 +514,15 @@ version = "2.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
|
checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||||
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derivative"
|
name = "derivative"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@@ -774,8 +784,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1229,6 +1241,21 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonwebtoken"
|
||||||
|
version = "9.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"js-sys",
|
||||||
|
"pem",
|
||||||
|
"ring",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"simple_asn1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kqueue"
|
name = "kqueue"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
@@ -1433,6 +1460,31 @@ version = "2.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
|
checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -1546,6 +1598,16 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pem"
|
||||||
|
version = "3.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@@ -1837,6 +1899,12 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
@@ -1891,7 +1959,7 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2263,9 +2331,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.137"
|
version = "1.0.140"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -2333,6 +2401,18 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple_asn1"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-traits",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@@ -2496,7 +2576,16 @@ version = "1.0.69"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2510,6 +2599,17 @@ dependencies = [
|
|||||||
"syn 2.0.100",
|
"syn 2.0.100",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.100",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.8"
|
version = "1.1.8"
|
||||||
@@ -2530,6 +2630,37 @@ dependencies = [
|
|||||||
"trackable 0.2.24",
|
"trackable 0.2.24",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"itoa",
|
||||||
|
"num-conv",
|
||||||
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
|
||||||
|
dependencies = [
|
||||||
|
"num-conv",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
|
|||||||
@@ -17,12 +17,13 @@ log = "0.4.27"
|
|||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
notify = "8.0.0"
|
notify = "8.0.0"
|
||||||
axum = { version = "0.8.3" }
|
axum = { version = "0.8.3" }
|
||||||
#axum-server = { version = "0.8.3", features = ["tls-rustls"] }
|
|
||||||
|
|
||||||
reqwest = { version = "0.12.15", features = ["json"] }
|
reqwest = { version = "0.12.15", features = ["json"] }
|
||||||
serde_yaml = "0.8.26"
|
serde_yaml = "0.8.26"
|
||||||
#hickory-client = "0.25.1"
|
#hickory-client = "0.25.1"
|
||||||
rand = "0.9.0"
|
rand = "0.9.0"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
|
jsonwebtoken = "9.3.1"
|
||||||
|
#hmac = "0.12.1"
|
||||||
|
#sha2 = "0.10.8"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
52
README.md
52
README.md
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
Is a Reverse proxy, service mesh based on Cloudflare's Pingora
|
Is a Reverse proxy, service mesh based on Cloudflare's Pingora
|
||||||
|
|
||||||
**Why Gazan ?** Roots and meaning (Gazan = Գազան = beast / wild animal in Armenian).
|
**What Gazan means?**
|
||||||
|
<ins>Gazan = Գազան = beast / wild animal in Armenian / Often used as a synonym to something great.</ins>.
|
||||||
|
|
||||||
Built on Rust, on top of **Cloudflare’s Pingora engine**, **Gazan** delivers world-class performance, security, and scalability — right out of the box.
|
Built on Rust, on top of **Cloudflare’s Pingora engine**, **Gazan** delivers world-class performance, security, and scalability — right out of the box.
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ Built on Rust, on top of **Cloudflare’s Pingora engine**, **Gazan** delivers w
|
|||||||
- Optional request headers
|
- Optional request headers
|
||||||
- Optional TLS for upstreams
|
- Optional TLS for upstreams
|
||||||
- Global headers (e.g., CORS) apply to all proxied responses
|
- Global headers (e.g., CORS) apply to all proxied responses
|
||||||
- Optional authentication (Basic, API Key) — currently commented for example
|
- Optional authentication (Basic, API Key, JWT) — currently commented for example
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -134,9 +135,54 @@ curl -XPOST --data-binary @./etc/upstreams.txt 127.0.0.1:3000/conf
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🔐 Authentication (Optional)
|
||||||
|
|
||||||
|
- Adds authentication to all requests.
|
||||||
|
- Only one method can be active at a time.
|
||||||
|
- `basic` : Standard HTTP Basic Authentication requests.
|
||||||
|
- `apikey` : Authentication via `x-api-key` header, which should match the value in config.
|
||||||
|
- `jwt`: JWT authentication implemented vi `x-jwt-token` header.
|
||||||
|
- To obtain JWT token, you should send **generate** request to built in api server's `/jwt` endpoint.
|
||||||
|
- `masterkey`: should match configured `masterkey` in `main.yaml` and `upstreams.yaml`.
|
||||||
|
- `owner` : Just a placeholder, can be anything.
|
||||||
|
- `valid` : Time in minutes during which the generated token will be valid.
|
||||||
|
|
||||||
|
**Example JWT token generateion request**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PAYLOAD='{
|
||||||
|
"masterkey": "910517d9-f9a1-48de-8826-dbadacbd84af-cb6f830e-ab16-47ec-9d8f-0090de732774",
|
||||||
|
"owner": "valod",
|
||||||
|
"valid": 1
|
||||||
|
}'
|
||||||
|
|
||||||
|
TOK=`curl -s -XPOST -H "Content-Type: application/json" -d "$PAYLOAD" http://127.0.0.1:3000/jwt | cut -d '"' -f4`
|
||||||
|
echo $TOK
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Request with JWT token**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "x-jwt-token: ${TOK}" -H 'Host: myip.mydomain.com' http://127.0.0.1:6193/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Request with API Key**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "x-api-key: ${APIKEY}" --header 'Host: myip.mydomain.com' http://127.0.0.1:6193/
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Request with Basic Auth**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -u username:password -H 'Host: myip.mydomain.com' http://127.0.0.1:6193/
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
## 📃 License
|
## 📃 License
|
||||||
|
|
||||||
The product is distributed under [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
[Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -17,4 +17,4 @@ upstreams_conf: etc/upstreams.yaml # the location of upstreams file
|
|||||||
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)
|
hc_method: HEAD # Healthcheck method (HEAD, GET, POST are supported)
|
||||||
hc_interval: 2 #Intervak for Healthcheck in seconds
|
hc_interval: 2 #Intervak for Healthcheck in seconds
|
||||||
|
master_key: 910517d9-f9a1-48de-8826-dbadacbd84af-cb6f830e-ab16-47ec-9d8f-0090de732774
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ globals:
|
|||||||
- "Access-Control-Allow-Origin:*"
|
- "Access-Control-Allow-Origin:*"
|
||||||
- "Access-Control-Allow-Methods:POST, GET, OPTIONS"
|
- "Access-Control-Allow-Methods:POST, GET, OPTIONS"
|
||||||
- "Access-Control-Max-Age:86400"
|
- "Access-Control-Max-Age:86400"
|
||||||
# authorization: # if enabled
|
authorization: # if enabled
|
||||||
# - "basic"
|
# - "basic"
|
||||||
# - "zangag:Anhnazand1234"
|
# - "zangag:Anhnazand1234"
|
||||||
# - "apikey"
|
# - "apikey"
|
||||||
# - "qweasdqweadhbk"
|
# - "5a28cc4c-ce10-4ff1-824e-743c38835f5c"
|
||||||
|
- "jwt"
|
||||||
|
- "910517d9-f9a1-48de-8826-dbadacbd84af-cb6f830e-ab16-47ec-9d8f-0090de732774"
|
||||||
consul: # read only if provider is consul
|
consul: # read only if provider is consul
|
||||||
servers:
|
servers:
|
||||||
- "http://master1:8500"
|
- "http://master1:8500"
|
||||||
|
|||||||
@@ -3,5 +3,6 @@ pub mod consul;
|
|||||||
pub mod discovery;
|
pub mod discovery;
|
||||||
mod filewatch;
|
mod filewatch;
|
||||||
pub mod healthcheck;
|
pub mod healthcheck;
|
||||||
|
pub mod jwt;
|
||||||
pub mod parceyaml;
|
pub mod parceyaml;
|
||||||
pub mod tools;
|
pub mod tools;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::utils::jwt::check_jwt;
|
||||||
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;
|
||||||
@@ -7,6 +8,7 @@ trait AuthValidator {
|
|||||||
}
|
}
|
||||||
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);
|
||||||
|
|
||||||
impl AuthValidator for BasicAuth<'_> {
|
impl AuthValidator for BasicAuth<'_> {
|
||||||
fn validate(&self, session: &Session) -> bool {
|
fn validate(&self, session: &Session) -> bool {
|
||||||
@@ -30,6 +32,16 @@ impl AuthValidator for ApiKeyAuth<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AuthValidator for JwtAuth<'_> {
|
||||||
|
fn validate(&self, session: &Session) -> bool {
|
||||||
|
let jwtsecret = self.0;
|
||||||
|
if let Some(header) = session.get_header("x-jwt-token") {
|
||||||
|
let tok = header.to_str().ok().unwrap();
|
||||||
|
return check_jwt(tok, jwtsecret);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
fn validate(auth: &dyn AuthValidator, session: &Session) -> bool {
|
fn validate(auth: &dyn AuthValidator, session: &Session) -> bool {
|
||||||
auth.validate(session)
|
auth.validate(session)
|
||||||
}
|
}
|
||||||
@@ -44,6 +56,10 @@ pub fn authenticate(c: &[String], session: &Session) -> bool {
|
|||||||
let auth = ApiKeyAuth(c[1].as_str().into());
|
let auth = ApiKeyAuth(c[1].as_str().into());
|
||||||
validate(&auth, session)
|
validate(&auth, session)
|
||||||
}
|
}
|
||||||
|
"jwt" => {
|
||||||
|
let auth = JwtAuth(c[1].as_str().into());
|
||||||
|
validate(&auth, session)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Unsupported authentication mechanism : {}", c[0]);
|
println!("Unsupported authentication mechanism : {}", c[0]);
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub struct FromFileProvider {
|
|||||||
}
|
}
|
||||||
pub struct APIUpstreamProvider {
|
pub struct APIUpstreamProvider {
|
||||||
pub address: String,
|
pub address: String,
|
||||||
|
pub masterkey: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConsulProvider {
|
pub struct ConsulProvider {
|
||||||
@@ -24,7 +25,7 @@ pub trait Discovery {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Discovery for APIUpstreamProvider {
|
impl Discovery for APIUpstreamProvider {
|
||||||
async fn start(&self, toreturn: Sender<Configuration>) {
|
async fn start(&self, toreturn: Sender<Configuration>) {
|
||||||
webserver::run_server(self.address.clone(), toreturn).await;
|
webserver::run_server(self.address.clone(), self.masterkey.clone(), toreturn).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
src/utils/jwt.rs
Normal file
16
src/utils/jwt.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub(crate) struct Claims {
|
||||||
|
pub(crate) user: String,
|
||||||
|
pub(crate) exp: u64,
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,12 +52,13 @@ impl BackgroundService for LB {
|
|||||||
error!("Can't read config file");
|
error!("Can't read config file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let config_address = self.config.get("config_address");
|
let config_address = self.config.get("config_address");
|
||||||
|
let masterkey = self.config.get("master_key").unwrap();
|
||||||
match config_address {
|
match config_address {
|
||||||
Some(config_address) => {
|
Some(config_address) => {
|
||||||
let api_load = APIUpstreamProvider {
|
let api_load = APIUpstreamProvider {
|
||||||
address: config_address.to_string(),
|
address: config_address.to_string(),
|
||||||
|
masterkey: masterkey.value().to_string(),
|
||||||
};
|
};
|
||||||
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 });
|
||||||
@@ -248,13 +249,13 @@ impl ProxyHttp for LB {
|
|||||||
let authenticated = authenticate(&auth.value(), &session);
|
let authenticated = authenticate(&auth.value(), &session);
|
||||||
if !authenticated {
|
if !authenticated {
|
||||||
let _ = session.respond_error(401).await;
|
let _ = session.respond_error(401).await;
|
||||||
info!("Forbidden: {:?}, {}", session.client_addr(), session.req_header().uri.path().to_string());
|
warn!("Forbidden: {:?}, {}", session.client_addr(), session.req_header().uri.path().to_string());
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if session.req_header().uri.path().starts_with("/denied") {
|
if session.req_header().uri.path().starts_with("/denied") {
|
||||||
let _ = session.respond_error(403).await;
|
let _ = session.respond_error(403).await;
|
||||||
info!("Forbidden: {:?}, {}", session.client_addr(), session.req_header().uri.path().to_string());
|
warn!("Forbidden: {:?}, {}", session.client_addr(), session.req_header().uri.path().to_string());
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
};
|
};
|
||||||
Ok(false)
|
Ok(false)
|
||||||
|
|||||||
@@ -1,23 +1,41 @@
|
|||||||
use crate::utils::parceyaml::Configuration;
|
use crate::utils::parceyaml::Configuration;
|
||||||
use axum::body::Body;
|
use axum::body::Body;
|
||||||
|
use axum::extract::State;
|
||||||
use axum::http::{Response, StatusCode};
|
use axum::http::{Response, StatusCode};
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum::routing::{delete, get, head, post, put};
|
use axum::routing::{delete, get, head, post, put};
|
||||||
use axum::Router;
|
use axum::{Json, Router};
|
||||||
use futures::channel::mpsc::Sender;
|
use futures::channel::mpsc::Sender;
|
||||||
use futures::SinkExt;
|
use futures::SinkExt;
|
||||||
use log::info;
|
use jsonwebtoken::{encode, EncodingKey, Header};
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct InputKey {
|
||||||
|
masterkey: String,
|
||||||
|
owner: String,
|
||||||
|
valid: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
struct OutToken {
|
||||||
|
token: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
pub async fn run_server(bindaddress: String, mut toreturn: Sender<Configuration>) {
|
pub async fn run_server(bindaddress: String, masterkey: String, mut toreturn: Sender<Configuration>) {
|
||||||
let mut tr = toreturn.clone();
|
let mut tr = toreturn.clone();
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/{*wildcard}", get(getconfig))
|
.route("/{*wildcard}", get(senderror))
|
||||||
.route("/{*wildcard}", post(getconfig))
|
.route("/{*wildcard}", post(senderror))
|
||||||
.route("/{*wildcard}", put(getconfig))
|
.route("/{*wildcard}", put(senderror))
|
||||||
.route("/{*wildcard}", head(getconfig))
|
.route("/{*wildcard}", head(senderror))
|
||||||
.route("/{*wildcard}", delete(getconfig))
|
.route("/{*wildcard}", delete(senderror))
|
||||||
|
.route("/jwt", post(jwt_gen))
|
||||||
|
.with_state(masterkey.clone())
|
||||||
.route(
|
.route(
|
||||||
"/conf",
|
"/conf",
|
||||||
post(|up: String| async move {
|
post(|up: String| async move {
|
||||||
@@ -42,26 +60,32 @@ pub async fn run_server(bindaddress: String, mut toreturn: Sender<Configuration>
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
async fn getconfig() -> impl IntoResponse {
|
async fn senderror() -> impl IntoResponse {
|
||||||
"Hello from Axum API inside Pingora!\n".to_string();
|
|
||||||
Response::builder().status(StatusCode::BAD_GATEWAY).body(Body::from("No live upstream found!\n")).unwrap()
|
Response::builder().status(StatusCode::BAD_GATEWAY).body(Body::from("No live upstream found!\n")).unwrap()
|
||||||
}
|
}
|
||||||
// curl -XPOST -H 'Content-Type: application/json' --data-binary @./push.json 127.0.0.1:3000/json
|
|
||||||
// curl -XPOST --data-binary @./etc/upstreams.txt 127.0.0.1:3000/conf
|
|
||||||
|
|
||||||
/*
|
async fn jwt_gen(State(masterkey): State<String>, Json(payload): Json<InputKey>) -> (StatusCode, Json<OutToken>) {
|
||||||
async fn config(Json(payload): Json<HashMap<String, UpstreamData>>) -> impl IntoResponse {
|
if payload.masterkey == masterkey {
|
||||||
let upstreams = DashMap::new();
|
let now = SystemTime::now() + Duration::from_secs(payload.valid * 60);
|
||||||
for (key, value) in payload {
|
let a = now.duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||||
upstreams.insert(key, (value.servers, AtomicUsize::new(value.counter)));
|
let claim = crate::utils::jwt::Claims { user: payload.owner, exp: a };
|
||||||
|
match encode(&Header::default(), &claim, &EncodingKey::from_secret(payload.masterkey.as_ref())) {
|
||||||
|
Ok(t) => {
|
||||||
|
let tok = OutToken { token: t };
|
||||||
|
info!("Generating token: {:?}", tok);
|
||||||
|
(StatusCode::CREATED, Json(tok))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let tok = OutToken { token: "ERROR".to_string() };
|
||||||
|
error!("Failed to generate token: {:?}", e);
|
||||||
|
(StatusCode::INTERNAL_SERVER_ERROR, Json(tok))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let tok = OutToken {
|
||||||
|
token: "Unauthorised".to_string(),
|
||||||
|
};
|
||||||
|
warn!("Unauthorised JWT generate request: {:?}", tok);
|
||||||
|
(StatusCode::FORBIDDEN, Json(tok))
|
||||||
}
|
}
|
||||||
println!("{:?}", upstreams);
|
|
||||||
Response::builder().status(StatusCode::CREATED).body(Body::from("Config updated!\n")).unwrap()
|
|
||||||
}
|
}
|
||||||
async fn parse_upstreams(up: String) -> impl IntoResponse {
|
|
||||||
println!("Parsing: {}", up);
|
|
||||||
let serverlist = read_upstreams_from_file(up.as_str());
|
|
||||||
println!("{:?}", serverlist);
|
|
||||||
Response::builder().status(StatusCode::CREATED).body(Body::from("Config updated!\n")).unwrap()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
Reference in New Issue
Block a user