Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ Look at the `examples` directory in the repository or the below details section
| `RATE_LIMITER_MAX_RETRIES` | Max retries allowed within `RATE_LIMITER_FIND_TIME` | integer | `3` | `5`
| `RATE_LIMITER_FIND_TIME` | Time in seconds to keep track of login attempts | integer | `120` | `60`
| `RATE_LIMITER_BAN_TIME` | Time in seconds to ban clients after reaching `RATE_LIMITER_MAX_RETRIES` | integer | `300` | `600`
| `PASSWD_FILE` | The passwd file location to read for auth requests | string | `/passwd` | `/mnt/nforwardauth_passwd`
| `DISABLED_USERS` | Deny access for specific users, env variable delimeted by `,` | string | - | `user1,user2`

## Roadmap

Expand Down
19 changes: 19 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use hmac::{Hmac, Mac};
use once_cell::sync::OnceCell;
use regex::Regex;
use sha2::Sha256;
use std::collections::HashSet;
use std::env;

#[derive(Debug)]
Expand All @@ -17,6 +18,8 @@ pub struct Config {
pub rate_limiter_max_retries: u32,
pub rate_limiter_find_time: u32,
pub rate_limiter_ban_time: u32,
pub passwd_file: String,
pub disabled_users: HashSet<String>,
}

/* Config Singleton Instance and Implementation */
Expand Down Expand Up @@ -112,6 +115,20 @@ impl Config {
Err(..) => 300,
};

// passwd_file: The passwd file location to read for auth requests
let passwd_file: String = match env::var("PASSWD_FILE") {
Ok(value) => value,
Err(..) => "/passwd".to_string(),
};

// disabled_users: Deny access for specific users, env variable delimeted by `,`
let disabled_users: HashSet<String> = env::var("DISABLED_USERS")
.unwrap_or_default()
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect();

// Create config instance with initialized values
let config = Config {
port,
Expand All @@ -125,6 +142,8 @@ impl Config {
rate_limiter_max_retries,
rate_limiter_find_time,
rate_limiter_ban_time,
passwd_file,
disabled_users,
};

// Initialize config in instance
Expand Down
19 changes: 11 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ static FORWARDED_USER: &str = "X-Forwarded-User";
// Static file paths
static INDEX_DOCUMENT: &str = "/public/index.html";
static LOGOUT_DOCUMENT: &str = "/public/logout.html";
static PASSWD_FILE: &str = "/passwd";

// HTTP response body content
static NOT_FOUND: &[u8] = b"Not Found";
Expand Down Expand Up @@ -76,10 +75,8 @@ async fn api_forward_auth(
.unwrap_or(false);

// Get token from request headers and check if cookie exists, otherwise serve login page
let user = validate_cookie(headers);
if user.is_some() {
if let Some(user) = validate_cookie(headers) {
// User is authenticated via cookie
let user = user.unwrap();
if is_forwarded {
// AUTHORIZED response on forward
let mut response = Response::builder().status(StatusCode::OK);
Expand Down Expand Up @@ -409,11 +406,17 @@ async fn api_serve_file(filename: &str, status_code: StatusCode) -> Result<Respo

// Verify user credentials against the password file
async fn authenticate_user(user: &str, password: &str) -> Result<bool> {
if let Ok(passwd) = fs::read_to_string(PASSWD_FILE).await {
if Config::global().disabled_users.contains(user) {
return Ok(false);
}
if let Ok(passwd) = fs::read_to_string(&Config::global().passwd_file).await {
for line in passwd.lines() {
if let Some((stored_user, stored_hash)) = line.split_once(':') {
if stored_user == user && pwhash::unix::verify(password, stored_hash) {
return Ok(true);
if let Some((stored_user, stored_hash_rest)) = line.split_once(':') {
if stored_user == user {
let stored_hash = stored_hash_rest.split(':').next().unwrap();
if pwhash::unix::verify(password, stored_hash) {
return Ok(true);
}
}
}
}
Expand Down
Loading