From c4cc9c90b3546e72efbaeb9cb632b9d98288bac1 Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Fri, 7 Jul 2023 10:04:04 +0200 Subject: Add basic https support and format the code using black -S --- README.md | 20 ++++++++-- src/ngus/__main__.py | 103 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 95 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 670720a..b5409c6 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ To my amazement I wasn't able to find any free software matching that criteria. The main purpose of *ngus* is to accept file uploads as POST requests from an HTTP client. -Currently *ngus* does not provide encryption (HTTPS) support. Many different -tools can be employed to serve as an HTTPS proxy. +Currently *ngus* provides only simple encryption (HTTPS) support. Many +different tools can be employed to serve as an HTTPS proxy. See the examples bellow for more details! @@ -49,7 +49,9 @@ See the examples bellow for more details! ### Gentoo ```bash - layman -a sgs + # add sgs' custom repository using app-eselect/eselect-repository + eselect repository add sgs + emerge www-servers/ngus ``` @@ -107,6 +109,16 @@ The default form input field name is *ufile*. That can be changed by using the curl --basic -u uname -F "uploadfile=@myfile.zip" http://158.39.125.240:8080 ``` +Simple encryption (HTTPS) support + + ```bash + # creating a self-signed key / certificate pair valid for 356 days + openssl req -sha256 -newkey rsa:4096 -nodes -x509 -extensions v3_ca -subj "/C=NO/O=MyOrg/CN=uploads.myhostname.net" -days 365 -keyout uploads.myhostname.net.key.pem -out uploads.myhostname.net.cert.pem + python -m ngus -H 0.0.0.0 -p 8080 -b "uname:foo" -k uploads.myhostname.net.key.pem -c uploads.myhostname.net.cert.pem -u /home/s/uploads + # since the certificate is self-signed, it can serve as a CA to itself in this example + curl --basic -u uname -F "ufile=@myfile.zip" --cacert uploads.myhostname.net.cert.pem https://uploads.myhostname.net:8080 + ``` + ### Using *nginx* as a proxy @@ -138,7 +150,7 @@ described in the examples above. location / { # point at the ngus instance running on a private network address - proxy_pass http://192.168.1.240:8080/; + proxy_pass http://158.39.125.240:8080/; } } diff --git a/src/ngus/__main__.py b/src/ngus/__main__.py index efa2c4a..a1f3992 100644 --- a/src/ngus/__main__.py +++ b/src/ngus/__main__.py @@ -18,6 +18,7 @@ import argparse import base64 import logging import pathlib +import ssl import sys import ngus @@ -31,75 +32,129 @@ def eprint(*arg, **kwargs): def main(inargs=None): """main entry point""" parser = argparse.ArgumentParser( - description='The following options are available') + description='The following options are available' + ) parser.add_argument( - '-b', '--basic-auth', + '-b', + '--basic-auth', metavar='', type=str, dest='basic_auth', default='', - help=('Require Basic auth from the client in order to POST a file ' - '(default: No basic auth required)')) + help=( + 'Require Basic auth from the client in order to POST a file ' + '(default: No basic auth required)' + ), + ) parser.add_argument( - '-H', '--hostname', + '-H', + '--hostname', metavar='', type=str, dest='hostname', default='127.0.0.1', - help='ngus server IP / hostname (default: 127.0.0.1)') + help='ngus server IP / hostname (default: 127.0.0.1)', + ) parser.add_argument( - '-i', '--input-name', + '-c', + '--certfile', + metavar='', + type=pathlib.Path, + dest='certfile', + default=None, + help=( + 'Certfile (PEM) for TLS/SSL connections ' + '(default: SSL/TLS disabled)' + ), + ) + parser.add_argument( + '-i', + '--input-name', metavar='', type=str, dest='input_name', default='ufile', - help='The name of the form input field (default: "ufile")') + help='The name of the form input field (default: "ufile")', + ) + parser.add_argument( + '-k', + '--keyfile', + metavar='', + type=pathlib.Path, + dest='keyfile', + default=None, + help='Keyfile for TLS/SSL connections (default: SSL/TLS disabled)', + ) parser.add_argument( - '-p', '--port', + '-p', + '--port', metavar='', type=int, dest='port', default=8080, - help='ngus server port (default: 8080)') + help='ngus server port (default: 8080)', + ) parser.add_argument( - '-u', '--upload-dir', + '-u', + '--upload-dir', metavar='', type=pathlib.Path, dest='upload_dir', default=pathlib.Path.cwd(), - help='ngus server upload dir (default: CWD)') + help='ngus server upload dir (default: CWD)', + ) parser.add_argument( - '-U', '--upload-page', + '-U', + '--upload-page', metavar='', type=argparse.FileType('rb'), dest='upload_page', default=None, - help='Alternative upload page (form) to display (default: None)') + help='Alternative upload page (form) to display (default: None)', + ) parser.add_argument( - '-v', '--version', + '-v', + '--version', action='version', version=f'%(prog)s {ngus.__version__}', - help='Display program-version and exit') + help='Display program-version and exit', + ) args = parser.parse_args(inargs) try: - logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', - level=logging.DEBUG) + logging.basicConfig( + format='%(asctime)s - %(levelname)s - %(message)s', + level=logging.DEBUG, + ) logger = logging.getLogger('ngus') logger.info('ngus starting') server_params = { 'input_name': args.input_name, - 'upload_dir': args.upload_dir + 'upload_dir': args.upload_dir, } if args.upload_page is not None: server_params['upload_page'] = args.upload_page.read() if args.basic_auth and len(args.basic_auth.split(':')) == 2: server_params['basic_auth'] = base64.b64encode( - args.basic_auth.strip().encode('utf-8')).decode() + args.basic_auth.strip().encode('utf-8') + ).decode() logger.info('Requiring basic-auth') - ngus_server = ngus.NgusHTTPServer((args.hostname, args.port), - ngus.NgusBaseHTTPRequestHandler, - **server_params) - logger.info(f'Listening on {args.hostname}:{args.port}') + ngus_server = ngus.NgusHTTPServer( + (args.hostname, args.port), + ngus.NgusBaseHTTPRequestHandler, + **server_params, + ) + if args.certfile and args.keyfile: + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + context.load_cert_chain( + certfile=args.certfile, keyfile=args.keyfile + ) + ngus_server.socket = context.wrap_socket( + ngus_server.socket, server_side=True + ) + logger.info( + f'Listening on {args.hostname}:{args.port}' + f'{" (SSL/TLS)" if args.certfile and args.keyfile else ""}' + ) logger.info('Done!') ngus_server.serve_forever() except KeyboardInterrupt: -- cgit v1.3