From c0992c717ccda5a98cecfcd7ad528b57a3427254 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 22 Feb 2019 13:55:24 -0800 Subject: [PATCH] Add example of a python client Fixes smallstep/ca-component#139 --- .../hello-mtls/py-gunicorn/Dockerfile.client | 9 +++ .../examples/hello-mtls/py-gunicorn/client.py | 79 +++++++++++++++++++ .../py-gunicorn/client.requirements.txt | 1 + .../hello-mtls/py-gunicorn/gunicorn.conf | 1 + .../py-gunicorn/hello-mtls.client.yaml | 22 ++++++ 5 files changed, 112 insertions(+) create mode 100644 autocert/examples/hello-mtls/py-gunicorn/Dockerfile.client create mode 100644 autocert/examples/hello-mtls/py-gunicorn/client.py create mode 100644 autocert/examples/hello-mtls/py-gunicorn/client.requirements.txt create mode 100644 autocert/examples/hello-mtls/py-gunicorn/hello-mtls.client.yaml diff --git a/autocert/examples/hello-mtls/py-gunicorn/Dockerfile.client b/autocert/examples/hello-mtls/py-gunicorn/Dockerfile.client new file mode 100644 index 00000000..dd6dbf08 --- /dev/null +++ b/autocert/examples/hello-mtls/py-gunicorn/Dockerfile.client @@ -0,0 +1,9 @@ +FROM python:alpine + +RUN mkdir /src + +ADD client.py /src +ADD client.requirements.txt /src +RUN pip3 install -r /src/client.requirements.txt + +CMD ["python", "/src/client.py"] diff --git a/autocert/examples/hello-mtls/py-gunicorn/client.py b/autocert/examples/hello-mtls/py-gunicorn/client.py new file mode 100644 index 00000000..c8ba3da3 --- /dev/null +++ b/autocert/examples/hello-mtls/py-gunicorn/client.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +import os +import sys +import ssl +import signal +import time +import logging +import threading +import http.client +from watchdog.events import FileSystemEventHandler +from watchdog.observers import Observer +from urllib.parse import urlparse + +ca_certs = '/var/run/autocert.step.sm/root.crt' +cert_file = '/var/run/autocert.step.sm/site.crt' +key_file = '/var/run/autocert.step.sm/site.key' + +# RenewHandler is an even file system event handler that reloads the certs in +# the context when a file is modified. +class RenewHandler(FileSystemEventHandler): + def __init__(self, ctx): + self.ctx = ctx + super() + + def on_modified(self, event): + logging.info("reloading certs ...") + ctx.load_cert_chain(cert_file, key_file) + +# Monitor is a thread that watches for changes in a path and calls to the +# RenewHandler when a file is modified. +class Monitor(threading.Thread): + def __init__(self, handler, path): + threading.Thread.__init__(self) + self.handler = handler + self.path = path + + def run(self): + observer = Observer() + observer.schedule(self.handler, self.path) + observer.start() + +# Signal handler +def handler(signum, frame): + print("exiting ...") + sys.exit(0) + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s') + + # Start signal handler to exit + signal.signal(signal.SIGTERM, handler) + + # url from the environment + url = urlparse(os.environ['HELLO_MTLS_URL']) + + # ssl context + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + ctx.set_ciphers('ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256') + ctx.load_verify_locations(ca_certs) + ctx.load_cert_chain(cert_file, key_file) + + # initialize the renewer with the ssl context + renewer = RenewHandler(ctx) + + # start file monitor + monitor = Monitor(renewer, os.path.dirname(cert_file)) + monitor.start() + + # Do requests + while True: + try: + conn = http.client.HTTPSConnection(url.netloc, context=ctx) + conn.request("GET", url.path) + r = conn.getresponse() + data = r.read() + logging.info("%d - %s - %s", r.status, r.reason, data) + except Exception as err: + print('Something went wrong:', err) + time.sleep(5) diff --git a/autocert/examples/hello-mtls/py-gunicorn/client.requirements.txt b/autocert/examples/hello-mtls/py-gunicorn/client.requirements.txt new file mode 100644 index 00000000..c1cb094b --- /dev/null +++ b/autocert/examples/hello-mtls/py-gunicorn/client.requirements.txt @@ -0,0 +1 @@ +watchdog \ No newline at end of file diff --git a/autocert/examples/hello-mtls/py-gunicorn/gunicorn.conf b/autocert/examples/hello-mtls/py-gunicorn/gunicorn.conf index 1b6b7490..cab27a43 100644 --- a/autocert/examples/hello-mtls/py-gunicorn/gunicorn.conf +++ b/autocert/examples/hello-mtls/py-gunicorn/gunicorn.conf @@ -1,5 +1,6 @@ bind = '0.0.0.0:443' workers = 2 +accesslog = '-' # mTLS configuration with TLSv1.2 and requiring and validating client # certificates diff --git a/autocert/examples/hello-mtls/py-gunicorn/hello-mtls.client.yaml b/autocert/examples/hello-mtls/py-gunicorn/hello-mtls.client.yaml new file mode 100644 index 00000000..370c2634 --- /dev/null +++ b/autocert/examples/hello-mtls/py-gunicorn/hello-mtls.client.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hello-mtls-client + labels: {app: hello-mtls-client} +spec: + replicas: 1 + selector: {matchLabels: {app: hello-mtls-client}} + template: + metadata: + annotations: + autocert.step.sm/name: hello-mtls-client.default.pod.cluster.local + labels: {app: hello-mtls-client} + spec: + containers: + - name: hello-mtls-client + image: hello-mtls-client-py-gunicorn:latest + imagePullPolicy: Never + resources: {requests: {cpu: 10m, memory: 20Mi}} + env: + - name: HELLO_MTLS_URL + value: https://hello-mtls.default.svc.cluster.local