diff --git a/Cargo.lock b/Cargo.lock index 6ecd12f..41ca8d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,6 +180,7 @@ dependencies = [ "sha2", "tokio", "tonic", + "urlencoding", ] [[package]] @@ -3001,6 +3002,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf16_iter" version = "1.0.5" diff --git a/Cargo.toml b/Cargo.toml index 581ce8b..98e8aac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,4 +29,5 @@ tonic = "0.13.0" #uuid = { version = "1.16.0", features = ["v4"] } sha2 = { version = "0.10.8", default-features = false } base16ct = { version = "0.2.0", features = ["alloc"] } +urlencoding = "2.1.3" diff --git a/README.md b/README.md index be01b7f..33b9fcf 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,8 @@ curl -XPOST --data-binary @./etc/upstreams.txt 127.0.0.1:3000/conf - 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. +- `jwt`: JWT authentication implemented via `gazantoken=` url parameter. `/some/url?gazantoken=TOKEN` +- `jwt`: JWT authentication implemented via `Authorization: Bearer ` 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. @@ -173,7 +174,7 @@ curl -XPOST --data-binary @./etc/upstreams.txt 127.0.0.1:3000/conf PAYLOAD='{ "masterkey": "910517d9-f9a1-48de-8826-dbadacbd84af-cb6f830e-ab16-47ec-9d8f-0090de732774", "owner": "valod", - "valid": 1 + "valid": 10 }' TOK=`curl -s -XPOST -H "Content-Type: application/json" -d "$PAYLOAD" http://127.0.0.1:3000/jwt | cut -d '"' -f4` diff --git a/etc/upstreams.yaml b/etc/upstreams.yaml index 2b5a4a3..da93f18 100644 --- a/etc/upstreams.yaml +++ b/etc/upstreams.yaml @@ -6,13 +6,13 @@ globals: - "Access-Control-Allow-Methods:POST, GET, OPTIONS" - "Access-Control-Max-Age:86400" - "X-Custom-Header:Something Special" -# authorization: # Optional, only one of auth methods below can be active at a time -# - "basic" -# - "zangag:Anhnazand1234" -# - "apikey" -# - "5a28cc4c-ce10-4ff1-824e-743c38835f5c" -# - "jwt" -# - "910517d9-f9a1-48de-8826-dbadacbd84af-cb6f830e-ab16-47ec-9d8f-0090de732774" + authorization: # Optional, only one of auth methods below can be active at a time + # - "basic" + # - "zangag:Anhnazand1234" + # - "apikey" + # - "5a28cc4c-ce10-4ff1-824e-743c38835f5c" + - "jwt" + - "910517d9-f9a1-48de-8826-dbadacbd84af-cb6f830e-ab16-47ec-9d8f-0090de732774" consul: # If the provider is consul. Otherwise ignored servers: - "http://master1:8500" @@ -57,7 +57,12 @@ upstreams: # If provider is files. Otherwise ignored headers: - "X-Some-Thing:Yaaaaaaaaaaaaaaa" servers: + - "192.168.1.1:8000" - "192.168.1.10:8000" + - "127.0.0.1:8000" + - "127.0.0.2:8000" + - "127.0.0.3:8000" + - "127.0.0.4:8000" glop.netangels.net: paths: "/": diff --git a/src/utils/auth.rs b/src/utils/auth.rs index fcfed2b..d807e4f 100644 --- a/src/utils/auth.rs +++ b/src/utils/auth.rs @@ -2,6 +2,8 @@ use crate::utils::jwt::check_jwt; use base64::engine::general_purpose::STANDARD; use base64::Engine; use pingora_proxy::Session; +use std::collections::HashMap; +use urlencoding::decode; trait AuthValidator { fn validate(&self, session: &Session) -> bool; @@ -35,9 +37,27 @@ 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); + if let Some(tok) = get_query_param(session, "gazantoken") { + return check_jwt(tok.as_str(), jwtsecret); + } + + // if let Some(header) = session.get_header("authorization") { + // let h = header.to_str().ok().unwrap().split(" ").collect::>(); + // match h.len() { + // n => { + // return check_jwt(h[n - 1], jwtsecret); + // } + // } + // } + + if let Some(auth_header) = session.get_header("authorization") { + if let Ok(header_str) = auth_header.to_str() { + if let Some((scheme, token)) = header_str.split_once(' ') { + if scheme.eq_ignore_ascii_case("bearer") { + return check_jwt(token, jwtsecret); + } + } + } } false } @@ -66,3 +86,19 @@ pub fn authenticate(c: &[String], session: &Session) -> bool { } } } + +pub fn get_query_param(session: &Session, key: &str) -> Option { + let query = session.req_header().uri.query()?; + + let params: HashMap<_, _> = query + .split('&') + .filter_map(|pair| { + let mut parts = pair.splitn(2, '='); + let k = parts.next()?; + let v = parts.next().unwrap_or(""); // Some params might have no value + Some((k, v)) + }) + .collect(); + + params.get(key).map(|v| decode(v).ok()).flatten().map(|s| s.to_string()) +}