#!/usr/bin/env python3 """ Generate .htpasswd file for Nginx Basic Authentication. Uses NGINX_USERNAME and NGINX_PASSWORD environment variables. Supports bcrypt hashing with fallback mechanisms. """ import os import sys def generate_htpasswd(): """ Generate an .htpasswd file for Nginx Basic Authentication. Creates a password file with the specified username and password, hashed using the most secure available method. The file is saved to the path specified by HTPASSWD_PATH environment variable (default: /etc/nginx/.htpasswd). The function attempts to hash the password in the following order of preference: 1. bcrypt (most secure, recommended for modern Nginx) 2. openssl passwd with SHA-512 (-6 option) 3. crypt module with SHA-512 method The generated file is created with read permissions for owner and group (0640). Environment Variables: NGINX_USERNAME: Username for authentication. Defaults to "admin" if not set. NGINX_PASSWORD: Password for authentication. Must be set and non-empty. HTPASSWD_PATH: Path where the .htpasswd file will be created. Defaults to "/etc/nginx/.htpasswd". Raises: SystemExit: If NGINX_PASSWORD is not set or empty (exit code 1). If password hashing fails (exit code 1). If file creation fails (exit code 1). Returns: None: Prints success/error messages to stdout/stderr. Example: Set environment variables and run: >>> export NGINX_USERNAME=user >>> export NGINX_PASSWORD=secret >>> export HTPASSWD_PATH=/tmp/.htpasswd >>> generate_htpasswd() [SUCCESS] .htpasswd created successfully at /tmp/.htpasswd for user 'user'. """ username = os.getenv("NGINX_USERNAME", "admin").strip() password = os.getenv("NGINX_PASSWORD", "").strip() if not password: print("[ERROR] NGINX_PASSWORD environment variable is not set or empty.", file=sys.stderr) sys.exit(1) target_path = os.getenv("HTPASSWD_PATH", "/etc/nginx/.htpasswd") # Attempt bcrypt hashing first (most secure and standard for modern Nginx) hashed_entry = None try: import bcrypt hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(rounds=12)).decode('utf-8') hashed_entry = f"{username}:{hashed_password}\n" except ImportError: pass # Fallback to crypt module or openssl if bcrypt is not available if not hashed_entry: try: import subprocess res = subprocess.run(["openssl", "passwd", "-6", password], capture_output=True, text=True, check=True) hashed_entry = f"{username}:{res.stdout.strip()}\n" except Exception: try: import crypt hashed_password = crypt.crypt(password, crypt.mksalt(crypt.METHOD_SHA512)) hashed_entry = f"{username}:{hashed_password}\n" except Exception as e: print(f"[ERROR] Could not hash password: {e}", file=sys.stderr) sys.exit(1) try: os.makedirs(os.path.dirname(target_path), exist_ok=True) with open(target_path, "w", encoding="utf-8") as f: f.write(hashed_entry) os.chmod(target_path, 0o640) print(f"[SUCCESS] .htpasswd created successfully at {target_path} for user '{username}'.") except Exception as e: print(f"[ERROR] Failed to write {target_path}: {e}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": generate_htpasswd()