mirror of
https://github.com/sadoyan/aralez.git
synced 2026-05-30 03:44:06 +08:00
Removed authentication from API server, JWT master key as environment variable
This commit is contained in:
@@ -1,22 +1,17 @@
|
|||||||
use crate::utils::jwt::check_jwt;
|
use crate::utils::jwt::check_jwt;
|
||||||
// use reqwest::Client;
|
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use base64::engine::general_purpose::STANDARD;
|
use base64::engine::general_purpose::STANDARD;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
|
use pingora::http::RequestHeader;
|
||||||
|
use pingora_core::connectors::http::Connector;
|
||||||
|
use pingora_core::upstreams::peer::HttpPeer;
|
||||||
|
use pingora_http::ResponseHeader;
|
||||||
use pingora_proxy::Session;
|
use pingora_proxy::Session;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, LazyLock};
|
use std::sync::{Arc, LazyLock};
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
use urlencoding::decode;
|
use urlencoding::decode;
|
||||||
|
|
||||||
// use pingora::http::{RequestHeader, ResponseHeader, StatusCode};
|
|
||||||
use pingora::http::RequestHeader;
|
|
||||||
// --------------------------------- //
|
|
||||||
use pingora_core::connectors::http::Connector;
|
|
||||||
use pingora_core::upstreams::peer::HttpPeer;
|
|
||||||
use pingora_http::ResponseHeader;
|
|
||||||
// --------------------------------- //
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
trait AuthValidator {
|
trait AuthValidator {
|
||||||
async fn validate(&self, session: &mut Session) -> bool;
|
async fn validate(&self, session: &mut Session) -> bool;
|
||||||
@@ -182,6 +177,7 @@ impl AuthValidator for ApiKeyAuth<'_> {
|
|||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl AuthValidator for JwtAuth<'_> {
|
impl AuthValidator for JwtAuth<'_> {
|
||||||
async fn validate(&self, session: &mut Session) -> bool {
|
async fn validate(&self, session: &mut Session) -> bool {
|
||||||
|
println!("{:?}", self.0);
|
||||||
let jwtsecret = self.0;
|
let jwtsecret = self.0;
|
||||||
if let Some(tok) = get_query_param(session, "araleztoken") {
|
if let Some(tok) = get_query_param(session, "araleztoken") {
|
||||||
return check_jwt(tok.as_str(), jwtsecret);
|
return check_jwt(tok.as_str(), jwtsecret);
|
||||||
|
|||||||
@@ -9,13 +9,10 @@ use std::sync::Arc;
|
|||||||
pub struct APIUpstreamProvider {
|
pub struct APIUpstreamProvider {
|
||||||
pub config_api_enabled: bool,
|
pub config_api_enabled: bool,
|
||||||
pub address: String,
|
pub address: String,
|
||||||
pub masterkey: String,
|
pub masterkey: Option<String>,
|
||||||
pub certs_dir: String,
|
pub certs_dir: String,
|
||||||
pub config_dir: String,
|
pub config_dir: String,
|
||||||
pub upstreams_file: String,
|
pub upstreams_file: String,
|
||||||
// pub tls_address: Option<String>,
|
|
||||||
// pub tls_certificate: Option<String>,
|
|
||||||
// pub tls_key_file: 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>,
|
||||||
pub current_upstreams: Arc<UpstreamsDashMap>,
|
pub current_upstreams: Arc<UpstreamsDashMap>,
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ use log4rs::{
|
|||||||
encode::pattern::PatternEncoder,
|
encode::pattern::PatternEncoder,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::{Arc, LazyLock};
|
use std::sync::{Arc, LazyLock};
|
||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
pub static DOMAINS: LazyLock<DashMap<String, bool>> = LazyLock::new(DashMap::new);
|
pub static DOMAINS: LazyLock<DashMap<String, bool>> = LazyLock::new(DashMap::new);
|
||||||
|
|
||||||
@@ -236,6 +236,11 @@ pub fn parce_main_config(path: &str) -> AppConfig {
|
|||||||
let reply = DashMap::new();
|
let reply = DashMap::new();
|
||||||
let cfg: HashMap<String, String> = serde_yml::from_str(&data).expect("Failed to parse main config file");
|
let cfg: HashMap<String, String> = serde_yml::from_str(&data).expect("Failed to parse main config file");
|
||||||
let mut cfo: AppConfig = serde_yml::from_str(&data).expect("Failed to parse main config file");
|
let mut cfo: AppConfig = serde_yml::from_str(&data).expect("Failed to parse main config file");
|
||||||
|
|
||||||
|
if let Ok(jwt_key) = env::var("JWT_KEY") {
|
||||||
|
cfo.master_key = Some(jwt_key);
|
||||||
|
};
|
||||||
|
|
||||||
log_builder(&cfo, &cfo.log_file);
|
log_builder(&cfo, &cfo.log_file);
|
||||||
cfo.hc_method = cfo.hc_method.to_uppercase();
|
cfo.hc_method = cfo.hc_method.to_uppercase();
|
||||||
for (k, v) in cfg {
|
for (k, v) in cfg {
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ pub struct AppConfig {
|
|||||||
pub hc_method: String,
|
pub hc_method: String,
|
||||||
pub upstreams_conf: String,
|
pub upstreams_conf: String,
|
||||||
pub log_level: String,
|
pub log_level: String,
|
||||||
pub master_key: String,
|
pub master_key: Option<String>,
|
||||||
pub config_address: String,
|
pub config_address: String,
|
||||||
pub proxy_address_http: String,
|
pub proxy_address_http: String,
|
||||||
pub config_api_enabled: bool,
|
pub config_api_enabled: bool,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ 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::{Response, StatusCode};
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum::routing::{any, get, post};
|
use axum::routing::{any, get, post};
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
@@ -21,7 +21,6 @@ use serde::Serialize;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
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 tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
|
|
||||||
@@ -32,7 +31,7 @@ struct OutToken {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
struct AppState {
|
||||||
master_key: String,
|
master_key: Option<String>,
|
||||||
cert_creds: String,
|
cert_creds: String,
|
||||||
certs_dir: String,
|
certs_dir: String,
|
||||||
upstreams_file: String,
|
upstreams_file: String,
|
||||||
@@ -95,13 +94,11 @@ pub async fn run_server(config: &APIUpstreamProvider, mut to_return: Sender<Conf
|
|||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn conf(State(st): State<AppState>, Query(params): Query<HashMap<String, String>>, headers: HeaderMap, content: String) -> impl IntoResponse {
|
async fn conf(State(st): State<AppState>, Query(params): Query<HashMap<String, String>>, content: String) -> impl IntoResponse {
|
||||||
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 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 {
|
||||||
@@ -111,16 +108,13 @@ async fn conf(State(st): State<AppState>, Query(params): Query<HashMap<String, S
|
|||||||
} else {
|
} else {
|
||||||
drop(tokio::spawn(async move { apply_config(content.as_str(), st, false).await }));
|
drop(tokio::spawn(async move { apply_config(content.as_str(), st, false).await }));
|
||||||
}
|
}
|
||||||
// apply_config(content.as_str(), st).await;
|
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();
|
Response::builder().status(StatusCode::BAD_GATEWAY).body(Body::from(format!("Failed: {}\n", err))).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Response::builder().status(StatusCode::FORBIDDEN).body(Body::from("Access Denied !\n")).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn apply_config(content: &str, mut st: AppState, save: bool) {
|
async fn apply_config(content: &str, mut st: AppState, save: bool) {
|
||||||
@@ -137,7 +131,8 @@ async fn apply_config(content: &str, mut st: AppState, save: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn jwt_gen(State(state): State<AppState>, Json(payload): Json<Claims>) -> (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 let Some(master_key) = &state.master_key {
|
||||||
|
if &payload.master_key == master_key {
|
||||||
let now = SystemTime::now() + Duration::from_secs(payload.exp * 60);
|
let now = SystemTime::now() + Duration::from_secs(payload.exp * 60);
|
||||||
let expire = now.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();
|
let expire = now.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();
|
||||||
|
|
||||||
@@ -166,6 +161,13 @@ async fn jwt_gen(State(state): State<AppState>, Json(payload): Json<Claims>) ->
|
|||||||
warn!("Unauthorised JWT generate request: {:?}", tok);
|
warn!("Unauthorised JWT generate request: {:?}", tok);
|
||||||
(StatusCode::FORBIDDEN, Json(tok))
|
(StatusCode::FORBIDDEN, Json(tok))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let tok = OutToken {
|
||||||
|
token: "ERROR Getting JWT_KEY environment variable".to_string(),
|
||||||
|
};
|
||||||
|
error!("ERROR Getting JWT_KEY environment variable");
|
||||||
|
(StatusCode::INTERNAL_SERVER_ERROR, Json(tok))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn metrics() -> impl IntoResponse {
|
async fn metrics() -> impl IntoResponse {
|
||||||
@@ -221,11 +223,7 @@ async fn status(State(st): State<AppState>, Query(params): Query<HashMap<String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_return)]
|
#[allow(clippy::needless_return)]
|
||||||
async fn acme_create(State(state): State<AppState>, Query(params): Query<HashMap<String, String>>, headers: HeaderMap) -> impl IntoResponse {
|
async fn acme_create(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
if !key_authorization(&headers, ¶ms, &state.master_key) {
|
|
||||||
return Response::builder().status(StatusCode::FORBIDDEN).body(Body::from("Access Denied !\n")).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
match account::load_or_create(state.cert_creds.as_str()).await {
|
match account::load_or_create(state.cert_creds.as_str()).await {
|
||||||
Ok(txt) => {
|
Ok(txt) => {
|
||||||
return Response::builder()
|
return Response::builder()
|
||||||
@@ -243,16 +241,7 @@ async fn acme_create(State(state): State<AppState>, Query(params): Query<HashMap
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[allow(clippy::needless_return)]
|
#[allow(clippy::needless_return)]
|
||||||
async fn acme_order(
|
async fn acme_order(State(state): State<AppState>, axum::extract::Path(domain): axum::extract::Path<String>) -> impl IntoResponse {
|
||||||
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 domain_clean = domain.trim_matches('/');
|
||||||
match order::order(domain_clean, state.cert_creds.as_str(), state.certs_dir).await {
|
match order::order(domain_clean, state.cert_creds.as_str(), state.certs_dir).await {
|
||||||
Ok(txt) => {
|
Ok(txt) => {
|
||||||
@@ -292,13 +281,13 @@ pub async fn http01_challenge(axum::extract::Path(token): axum::extract::Path<St
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_authorization(headers: &HeaderMap, params: &HashMap<String, String>, masterkey: &str) -> bool {
|
// 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 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() {
|
// if s.as_bytes().ct_eq(masterkey.as_bytes()).into() {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
false
|
// false
|
||||||
}
|
// }
|
||||||
|
|
||||||
// -- ⚝ by Dave -- in NeoVim ⚝ --
|
// -- ⚝ by Dave -- in NeoVim ⚝ --
|
||||||
|
|||||||
Reference in New Issue
Block a user