diff --git a/Dockerfile b/Dockerfile index 09a6064..1aad856 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,19 @@ -FROM ubuntu:trusty -MAINTAINER Dave P +FROM ubuntu:bionic -# Create nexus user +# Nexus user for application usage RUN useradd --create-home nexus && \ - echo "nexus:nexus" | chpasswd && \ - apt-get update && \ - apt-get install -y nginx-light fcgiwrap supervisor openssh-server cron rsync && \ - mkdir /start.d /nexus /var/run/sshd && \ + echo "nexus:nexus" | chpasswd + +# Packages +RUN apt-get update && \ + apt-get install -y nginx-light fcgiwrap supervisor openssh-server cron rsync python3-pip + +# Misc conf +RUN mkdir /start.d /nexus /var/run/sshd && \ chown nexus /nexus && \ - cp /usr/share/doc/fcgiwrap/examples/nginx.conf /etc/nginx/fcgiwrap.conf && \ rm /etc/ssh/ssh_host_* && \ mkdir /etc/ssh/keys && \ - sed -i -E 's/HostKey \/etc\/ssh\//HostKey \/etc\/ssh\/keys\//' /etc/ssh/sshd_config && \ + sed -i -E 's/#?HostKey \/etc\/ssh\//HostKey \/data\/keys\//' /etc/ssh/sshd_config && \ rm -rf /var/lib/apt/lists/* # Supervisor confs @@ -23,16 +25,25 @@ ADD supervisor-cron.conf /etc/supervisor/conf.d/cron.conf # nginx confs ADD nginx.conf /etc/nginx/nginx.conf -ADD nginx-default /etc/nginx/sites-available/default +ADD default /etc/nginx/sites-available/default +ADD fastcgi_params /etc/nginx/fastcgi_params + +# scripts +ADD scripts/nexus /tmp/nexus +RUN cd /tmp/nexus && python3 setup.py install && rm -rf /tmp/nexus # Startup tasks ADD clear-sockets /start.d/clear-sockets ADD gen-ssh /start.d/gen-ssh +ADD dir-setup /start.d/dir-setup ADD start /start -RUN chmod +x /start.d/clear-sockets /start +RUN chmod +x /start.d/clear-sockets /start.d/gen-ssh /start.d/dir-setup /start ENTRYPOINT ["/start"] EXPOSE 80 EXPOSE 22 + + +RUN sed -i -E 's/error_log .+/error_log \/var\/log\/nginx\/error.log debug;/' /etc/nginx/nginx.conf diff --git a/README.md b/README.md index 5e55a6d..2536794 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # docker-nexus -**A nginx/cgi/sshd server for prototyping services or data hubs.** +**A nginx + cgi + sshd server for prototyping services or data hubs.** + ## Quick start -* Clone: `git clone ssh://git@gitlab.xmopx.net:222/dave/docker-nexus.git` +* Clone: `git clone ssh://git@git.davepedu.com:222/dave/docker-nexus.git` * Build: `cd docker-nexus ; docker build -t nexus .` * Run: `docker run nexus` @@ -13,28 +14,47 @@ Nexus offers a couple services: + ### SSHD For shell related activities, an sshd daemon runs on the standard port. Username and password, by default, is `nexus`. +Mount `/data/keys` to persist host keys. + + ### Nginx -For accessing data or calling CGI scripts, nginx runs on the standard port. The document root is `/nexus/`. +For accessing data or calling CGI scripts via nginx on port 80. + +The document root is `/data/data/`. + ### CGI -Standard CGI scripts can be placed in `/nexus/cgi-bin/`. Some sample scripts exist in `./examples/cgi-scripts/`. +CGI scripts can be placed in `/data/scripts/`. Some sample scripts exist in `./examples/cgi-scripts/`. + +The library in `scripts/nexus/cgi.py` can be imported like: + +``` +>>> from nexus.cgi import * +>>> start_response() +Status: 200 OK +Content-Type: text/html + +>>> +``` + ### Cron -Cron is present in the container. +Cron is present in the container. Place tabs in `/etc/cron.d`. + ## Protips -* Drop executable scripts into `/startup.d/` for effortless startup tasks -* Persistance? You want to mount these files/dirs outside the container: - * `/nexus/` - webroot and recommended data store - * `/etc/ssh/keys/` - sshd key file directory +* Drop executable scripts into `/startup.d/` for startup tasks +* Persistance - mount `/data/` somewhere persistent. + ## TODO diff --git a/nginx-default b/default similarity index 54% rename from nginx-default rename to default index 4271454..72f93b8 100644 --- a/nginx-default +++ b/default @@ -1,26 +1,33 @@ server { listen 80 default_server; - #listen [::]:80 default_server ipv6only=on; + listen [::]:80 default_server ipv6only=on; - root /nexus/; + root /data/data/; index index.html index.htm; server_name localhost; + #client_body_temp_path /data/tmp/; #auth_basic "Restricted"; - #auth_basic_user_file /etc/nginx/htpasswd; + #auth_basic_user_file /data/htpasswd; location / { autoindex on; try_files $uri $uri/ =404; } + location /cgi-bin/ { + alias /data/scripts/; gzip off; fastcgi_pass unix:/tmp/fcgiwrap.socket; include /etc/nginx/fastcgi_params; - fastcgi_param SCRIPT_FILENAME /nexus$fastcgi_script_name; + fastcgi_param SCRIPT_FILENAME /data/scripts$fastcgi_script_name; fastcgi_read_timeout 600s; fastcgi_send_timeout 600s; - client_max_body_size 4096m; + client_max_body_size 0; + } + + location /api/ { + rewrite ^/api/(.*) /cgi-bin/$1; } } diff --git a/dir-setup b/dir-setup new file mode 100644 index 0000000..4572fb0 --- /dev/null +++ b/dir-setup @@ -0,0 +1,6 @@ +#!/bin/sh + +# Make /data/ dirs + +mkdir -p /data/data +mkdir -p /data/scripts diff --git a/fastcgi_params b/fastcgi_params new file mode 100644 index 0000000..cdc550a --- /dev/null +++ b/fastcgi_params @@ -0,0 +1,25 @@ +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; + +fastcgi_param SCRIPT_FILENAME $request_filename; +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; + +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +fastcgi_param HTTPS $https if_not_empty; + +# PHP only, required if PHP was built with --enable-force-cgi-redirect +fastcgi_param REDIRECT_STATUS 200; diff --git a/gen-ssh b/gen-ssh index c755009..d2a34db 100755 --- a/gen-ssh +++ b/gen-ssh @@ -7,7 +7,8 @@ if [ ! -f "/etc/ssh/keys/ssh_host_rsa_key" ]; then ssh-keygen -A # Move keys to keys dir - mv /etc/ssh/ssh_host_* /etc/ssh/keys/ + mkdir -p /data/keys/ + mv /etc/ssh/ssh_host_* /data/keys/ fi rm /start.d/gen-ssh diff --git a/scripts/nexus/nexus/__init__.py b/scripts/nexus/nexus/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/nexus/nexus/cgi.py b/scripts/nexus/nexus/cgi.py new file mode 100755 index 0000000..be1b2a8 --- /dev/null +++ b/scripts/nexus/nexus/cgi.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +import os +import sys +from urllib.parse import parse_qs as _parse_qs +from base64 import b64decode + + +def start_response(content_type="text/html", status_code=("200", "OK",), extra_headers=[], flush=True): + """ + Begin the response by sending the beginning of an http response + """ + print('Status: %s %s' % (status_code)) + print("Content-Type: %s" % content_type) + for line in extra_headers: + print(line) + print() + if flush: + sys.stdout.flush() + + +def parse_qs(): + """ + Parse the request's query string into a dict + TODO parse arrays + """ + GET = {} + if "QUERY_STRING" in os.environ: + GET = _parse_qs(os.environ["QUERY_STRING"]) + GET = {k: v[0] for k, v in GET.items()} + return GET + + +class HTTPBasicAuth: + username = None + password = None + + def __str__(self): + return "" % (self.username, self.password) + + +def parse_auth(): + """ + Parse the basic auth information from http headers. Returns None if not present + :return HTTPBasicAuth: + """ + if "HTTP_AUTHORIZATION" in os.environ: + authtype, value = os.environ["HTTP_AUTHORIZATION"].split(' ') + if authtype == "Basic": + auth = HTTPBasicAuth() + auth.username, auth.password = b64decode(value).decode().split(":") + return auth + +# cgi.print_environ() diff --git a/scripts/nexus/setup.py b/scripts/nexus/setup.py new file mode 100644 index 0000000..32219ab --- /dev/null +++ b/scripts/nexus/setup.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +from setuptools import setup + + +__version__ = "0.0.0" + + +setup(name='nexus', + version=__version__, + description='Helper function library for python cgi scripts', + url='', + author='dpedu', + author_email='dave@davepedu.com', + packages=['nexus'], + install_requires=[], + # include_package_data=True, + # package_data={'photoapp': ['../templates/*.html', + # '../templates/fragments/*.html', + # '../styles/dist/*']}, + zip_safe=False)