Add htpasswd password support
This commit is contained in:
parent
0c86a78d51
commit
1dbf7ddb60
|
@ -33,7 +33,12 @@
|
|||
default = let
|
||||
pythonEnv = pkgs.python38.withPackages (pypkgs: with pypkgs; [ flask black pytest ]);
|
||||
in pkgs.mkShell {
|
||||
packages = [ pythonEnv pkgs.nixos-shell ];
|
||||
packages = [
|
||||
pythonEnv
|
||||
pkgs.nixos-shell
|
||||
# We only take this dependency for htpasswd, which is a little unfortunate
|
||||
pkgs.apacheHttpd
|
||||
];
|
||||
shellHook = ''
|
||||
PS1="(develop) $PS1"
|
||||
'';
|
||||
|
|
|
@ -41,19 +41,21 @@ def auth_check(route):
|
|||
"""
|
||||
Checks the HTTP Basic Auth header against the stored credential.
|
||||
"""
|
||||
|
||||
@wraps(route)
|
||||
def _route(*args, **kwargs):
|
||||
data_path = intake_data_dir()
|
||||
auth_path = data_path / "credentials.json"
|
||||
if auth_path.exists():
|
||||
if not request.authorization:
|
||||
abort(403)
|
||||
abort(401)
|
||||
auth = json.load(auth_path.open(encoding="utf8"))
|
||||
if request.authorization.username != auth["username"]:
|
||||
abort(403)
|
||||
if request.authorization.password != auth["secret"]:
|
||||
abort(403)
|
||||
return route(*args, **kwargs)
|
||||
|
||||
return _route
|
||||
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@ from datetime import datetime
|
|||
from pathlib import Path
|
||||
from shutil import get_terminal_size
|
||||
import argparse
|
||||
import getpass
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
import pwd
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
@ -263,6 +265,44 @@ def cmd_feed(cmd_args):
|
|||
print()
|
||||
|
||||
|
||||
def cmd_passwd(cmd_args):
|
||||
"""Update password for the web interface."""
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="intake passwd",
|
||||
description=cmd_passwd.__doc__,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--data",
|
||||
"-d",
|
||||
default=intake_data_dir(),
|
||||
help="Path to the intake data directory",
|
||||
)
|
||||
args = parser.parse_args(cmd_args)
|
||||
|
||||
command_exists = subprocess.run(["command", "-v" "htpasswd"], shell=True)
|
||||
if command_exists.returncode:
|
||||
print("Could not find htpasswd, cannot update password")
|
||||
return 1
|
||||
|
||||
creds = Path(args.data) / "credentials.json"
|
||||
if not creds.parent.exists():
|
||||
creds.parent.mkdir(parents=True)
|
||||
|
||||
user = pwd.getpwuid(os.getuid()).pw_name
|
||||
password = getpass.getpass(f"intake password for {user}: ")
|
||||
update_pwd = subprocess.run(
|
||||
["htpasswd", "-b", "/etc/intake/htpasswd", user, password]
|
||||
)
|
||||
if update_pwd.returncode:
|
||||
print("Could not update password file")
|
||||
return 1
|
||||
|
||||
new_creds = {"username": user, "secret": password}
|
||||
creds.write_text(json.dumps(new_creds, indent=2))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_run(cmd_args):
|
||||
"""Run the default Flask server."""
|
||||
parser = argparse.ArgumentParser(
|
||||
|
|
43
module.nix
43
module.nix
|
@ -9,19 +9,22 @@ in {
|
|||
listen.addr = mkOption {
|
||||
type = types.str;
|
||||
default = "0.0.0.0";
|
||||
description = "The listen address for the entry point to intake services. This endpoint will redirect to a local port based on the request's HTTP Basic Auth credentials.";
|
||||
description = "The listen address for the entry point to intake services. This endpoint will redirect to a "
|
||||
"local port based on the request's HTTP Basic Auth credentials.";
|
||||
};
|
||||
|
||||
listen.port = mkOption {
|
||||
type = types.port;
|
||||
default = 80;
|
||||
description = "The listen port for the entry point to intake services. This endpoint will redirect to a local port based on the request's HTTP Basic Auth credentials.";
|
||||
description = "The listen port for the entry point to intake services. This endpoint will redirect to a local "
|
||||
"port based on the request's HTTP Basic Auth credentials.";
|
||||
};
|
||||
|
||||
internalPortStart = mkOption {
|
||||
type = types.port;
|
||||
default = 24130;
|
||||
description = "The first port to use for internal service endpoints. A number of ports will be continguously allocated equal to the number of users with enabled intake services.";
|
||||
description = "The first port to use for internal service endpoints. A number of ports will be continguously "
|
||||
"allocated equal to the number of users with enabled intake services.";
|
||||
};
|
||||
|
||||
users = mkOption {
|
||||
|
@ -53,7 +56,39 @@ in {
|
|||
enabledUserNames = mapAttrsToList (userName: userCfg: userName) enabledUsers;
|
||||
userPortList = imap1 (i: userName: { ${userName} = i + intakeCfg.internalPortStart; }) enabledUserNames;
|
||||
userPort = foldl (acc: val: acc // val) {} userPortList;
|
||||
|
||||
# To avoid polluting PATH with httpd programs, define an htpasswd wrapper
|
||||
htpasswdWrapper = pkgs.writeShellScriptBin "htpasswd" ''
|
||||
${pkgs.apacheHttpd}/bin/htpasswd $@
|
||||
'';
|
||||
|
||||
# File locations
|
||||
intakeDir = "/etc/intake";
|
||||
intakePwd = "${intakeDir}/htpasswd";
|
||||
in {
|
||||
# Define a user group for access to the htpasswd file.
|
||||
users.groups.intake.members = mkIf (enabledUsers != {}) (enabledUserNames ++ [ "nginx" ]);
|
||||
|
||||
# Define an activation script that ensures that the htpasswd file exists.
|
||||
system.activationScripts.etc-intake = ''
|
||||
if [ ! -e ${intakeDir} ]; then
|
||||
${pkgs.coreutils}/bin/mkdir -p ${intakeDir};
|
||||
fi
|
||||
${pkgs.coreutils}/bin/chown root:root ${intakeDir}
|
||||
${pkgs.coreutils}/bin/chmod 755 ${intakeDir}
|
||||
if [ ! -e ${intakePwd} ]; then
|
||||
${pkgs.coreutils}/bin/touch ${intakePwd}
|
||||
fi
|
||||
${pkgs.coreutils}/bin/chown root:intake ${intakePwd}
|
||||
${pkgs.coreutils}/bin/chmod 660 ${intakePwd}
|
||||
'';
|
||||
|
||||
# Give the htpasswd wrapper to every intake user
|
||||
users.users =
|
||||
let
|
||||
addWrapperToUser = userName: { ${userName}.packages = [ htpasswdWrapper ]; };
|
||||
in mkMerge (map addWrapperToUser enabledUserNames);
|
||||
|
||||
# Define a user service for each configured user
|
||||
systemd.services =
|
||||
let
|
||||
|
@ -84,7 +119,7 @@ in {
|
|||
listen = [ intakeCfg.listen ];
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:$target_port";
|
||||
basicAuth = { alice = "alpha"; bob = "beta"; };
|
||||
basicAuthFile = intakePwd;
|
||||
};
|
||||
extraConfig = foldl (acc: val: acc + val) "" (mapAttrsToList (userName: port: ''
|
||||
if ($remote_user = "${userName}") {
|
||||
|
|
Loading…
Reference in New Issue