diff --git a/core/nginx/Dockerfile b/core/nginx/Dockerfile index 7181487e..37b8580b 100644 --- a/core/nginx/Dockerfile +++ b/core/nginx/Dockerfile @@ -7,7 +7,7 @@ RUN apk add --no-cache \ RUN pip3 install jinja2 # Image specific layers under this line RUN apk add --no-cache certbot nginx nginx-mod-mail openssl curl \ - && pip3 install idna requests + && pip3 install idna requests watchdog COPY conf /conf COPY *.py / diff --git a/core/nginx/certwatcher.py b/core/nginx/certwatcher.py new file mode 100755 index 00000000..3a8682f9 --- /dev/null +++ b/core/nginx/certwatcher.py @@ -0,0 +1,63 @@ +#!/usr/bin/python3 +""" +Certificate watcher which reloads nginx or reconfigures it, depending on what +happens to externally supplied certificates. Only executed by start.py in case +of TLS_FLAVOR=[mail, cert] +""" + +from os.path import exists, split as path_split +from os import system +import time +from watchdog.observers import Observer +from watchdog.events import FileSystemEventHandler, FileDeletedEvent, \ + FileCreatedEvent, FileModifiedEvent, FileMovedEvent + +class ChangeHandler(FileSystemEventHandler): + "watchdog-handler listening on any event, executing the correct configuration/reload steps" + @staticmethod + def reload_nginx(): + "merely reload nginx without re-configuring everything" + if exists("/var/run/nginx.pid"): + print("Reloading a running nginx") + system("nginx -s reload") + + @staticmethod + def reexec_config(): + "execute a reconfiguration of the system, which also reloads" + print("Reconfiguring system") + system("/config.py") + + def on_any_event(self, event): + "event-listener checking if the affected files are the cert-files we're interested in" + if event.is_directory: + return + + filename = path_split(event.src_path)[-1] + if isinstance(event, FileMovedEvent): + filename = path_split(event.dest_path)[-1] + + if filename in ['cert.pem', 'key.pem']: + # all cases except for FileModified need re-configure + if isinstance(event, (FileCreatedEvent, FileMovedEvent, FileDeletedEvent)): + ChangeHandler.reexec_config() + # file modification needs only a nginx reload without config.py + elif isinstance(event, FileModifiedEvent): + ChangeHandler.reload_nginx() + # cert files have been moved away, re-configure + elif isinstance(event, FileMovedEvent) and path_split(event.src_path)[-1] in ['cert.pem', 'key.pem']: + ChangeHandler.reexec_config() + + +if __name__ == '__main__': + observer = Observer() + handler = ChangeHandler() + observer.schedule(handler, "/certs", recursive=False) + observer.start() + + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + observer.stop() + + observer.join() diff --git a/core/nginx/start.py b/core/nginx/start.py index 7c5fa71e..8673f148 100755 --- a/core/nginx/start.py +++ b/core/nginx/start.py @@ -9,6 +9,8 @@ if os.path.exists("/var/run/nginx.pid"): if os.environ["TLS_FLAVOR"] in [ "letsencrypt","mail-letsencrypt" ]: subprocess.Popen(["/letsencrypt.py"]) +elif os.environ["TLS_FLAVOR"] in [ "mail", "cert" ]: + subprocess.Popen(["/certwatcher.py"]) subprocess.call(["/config.py"]) os.execv("/usr/sbin/nginx", ["nginx", "-g", "daemon off;"])