Skip to content

Instantly share code, notes, and snippets.

@lamw
Created February 12, 2025 15:35
Show Gist options
  • Save lamw/94214b58fa89d111219023344e34dcf5 to your computer and use it in GitHub Desktop.
Save lamw/94214b58fa89d111219023344e34dcf5 to your computer and use it in GitHub Desktop.
Python script to serve directory via HTTP or HTTPS w/basic authentication
# Extended python -m http.serve with --username and --password parameters for
# basic auth, based on https://gist.github.com/fxsjy/5465353
# Further extended https://gist.github.com/mauler/593caee043f5fe4623732b4db5145a82 (with help from ChatGPT) to add support for HTTPS
#
# Example:
# python3 http_server_auth.py --bind 192.168.30.4 --user vcf --password vcf123! --port 443 --directory /Volumes/Storage/Software/depot --certfile ~/cert.crt --keyfile ~/key.pem
from functools import partial
from http.server import HTTPServer, SimpleHTTPRequestHandler, test
import base64
import os
import ssl
import argparse
class AuthHTTPRequestHandler(SimpleHTTPRequestHandler):
""" Main class to present webpages and authentication. """
def __init__(self, *args, **kwargs):
username = kwargs.pop("username")
password = kwargs.pop("password")
self._auth = base64.b64encode(f"{username}:{password}".encode()).decode()
super().__init__(*args, **kwargs)
def do_AUTHHEAD(self):
self.send_response(401)
self.send_header("WWW-Authenticate", 'Basic realm="Test"')
self.send_header("Content-type", "text/html")
self.end_headers()
def do_GET(self):
""" Present front page with user authentication. """
if self.headers.get("Authorization") is None:
self.do_AUTHHEAD()
self.wfile.write(b"no auth header received")
elif self.headers.get("Authorization") == "Basic " + self._auth:
SimpleHTTPRequestHandler.do_GET(self)
else:
self.do_AUTHHEAD()
self.wfile.write(self.headers.get("Authorization").encode())
self.wfile.write(b"not authenticated")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--cgi", action="store_true", help="Run as CGI Server")
parser.add_argument(
"--bind",
"-b",
metavar="ADDRESS",
default="127.0.0.1",
help="Specify bind address [default: 127.0.0.1]",
)
parser.add_argument(
"--directory",
"-d",
default=os.getcwd(),
help="Specify alternative directory [default: current directory]",
)
parser.add_argument(
"--port",
"-p",
type=int,
default=8000,
help="Specify alternate port [default: 8000]",
)
parser.add_argument("--username", "-u", required=True, metavar="USERNAME")
parser.add_argument("--password", "-P", required=True, metavar="PASSWORD")
# New TLS arguments
parser.add_argument("--certfile", metavar="CERTFILE", help="Path to TLS certificate file")
parser.add_argument("--keyfile", metavar="KEYFILE", help="Path to TLS key file")
args = parser.parse_args()
handler_class = partial(
AuthHTTPRequestHandler,
username=args.username,
password=args.password,
directory=args.directory,
)
# Create HTTP Server
httpd = HTTPServer((args.bind, args.port), handler_class)
# Enable TLS if certificate and key files are provided
if args.certfile and args.keyfile:
httpd.socket = ssl.wrap_socket(
httpd.socket,
keyfile=args.keyfile,
certfile=args.certfile,
server_side=True,
)
print(f"๐Ÿ”’ Serving HTTPS on https://{args.bind}:{args.port} ...")
else:
print(f"๐ŸŒ Serving HTTP on http://{args.bind}:{args.port} ...")
httpd.serve_forever()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment