Per-host HTTP basic auth (#57)
Adds opt-in basic auth per Host, following the existing per-host controls
pattern:
- Host fields basicauth_enabled / basicauth_realm / basicauth_users
({user: base64(sha1(pw))}). Credentials are parsed to plaintext by the pure
host_features normalizer and hashed at the route layer (utils/basicauth.js),
so plaintext never reaches Redis.
- ops/nginx_conf/hostfeatures.lua enforces it in access phase: verifies the
Authorization header against base64(sha1(password)), fails closed with a 401
WWW-Authenticate challenge.
- hosts.ejs gains an enable toggle, realm, and a username:password textarea
(passwords never echoed back; blank keeps the current set).
Unit tests cover hashing (matches the htpasswd {SHA} vector), credential
parsing, and normalization. Note: the Lua path needs verification on a live
OpenResty box.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* Server-only hashing for per-host basic-auth credentials. Kept out of the pure,
|
||||
* browser-mirrored utils/host_features.js because it needs Node crypto.
|
||||
*
|
||||
* Passwords are stored as base64(SHA-1(password)) — the Apache htpasswd "{SHA}"
|
||||
* scheme — so plaintext never lands in Redis. OpenResty verifies with the same
|
||||
* hash (ops/nginx_conf/hostfeatures.lua): base64(sha1(password)).
|
||||
*
|
||||
* SHA-1 is weak for password storage in general, but this is a lightweight proxy
|
||||
* gate (not the app's own accounts) and matches htpasswd; upgrading the scheme is
|
||||
* a follow-up. Enforce strong passwords operationally.
|
||||
*/
|
||||
function hashPassword(password){
|
||||
return crypto.createHash('sha1').update(String(password)).digest('base64');
|
||||
}
|
||||
|
||||
// { username: plaintext } -> { username: base64sha1 }. Skips empty passwords.
|
||||
function hashBasicAuthUsers(users){
|
||||
let out = {};
|
||||
if(!users || typeof users !== 'object') return out;
|
||||
for(let user of Object.keys(users)){
|
||||
let pass = users[user];
|
||||
if(pass === undefined || pass === null || pass === '') continue;
|
||||
out[user] = hashPassword(pass);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
module.exports = {hashPassword, hashBasicAuthUsers};
|
||||
Reference in New Issue
Block a user