From ef5a6e8519908310773b14d5ba9fd6d2704abd03 Mon Sep 17 00:00:00 2001 From: Quentin Duchemin <quentinduchemin@tuta.io> Date: Fri, 4 Sep 2020 14:59:31 +0200 Subject: [PATCH] Remove unused folder from merge --- acme-copy-certs/.gitignore | 123 --------------- acme-copy-certs/Dockerfile | 10 -- acme-copy-certs/README.md | 17 --- acme-copy-certs/app/cert_updater.py | 200 ------------------------- acme-copy-certs/app/docker_actions.py | 39 ----- acme-copy-certs/app/docker_monitor.py | 120 --------------- acme-copy-certs/app/file_watcher.py | 33 ---- acme-copy-certs/app/service_manager.py | 139 ----------------- acme-copy-certs/requirements.txt | 2 - acme-copy-certs/tests/test.py | 30 ---- 10 files changed, 713 deletions(-) delete mode 100644 acme-copy-certs/.gitignore delete mode 100644 acme-copy-certs/Dockerfile delete mode 100644 acme-copy-certs/README.md delete mode 100644 acme-copy-certs/app/cert_updater.py delete mode 100644 acme-copy-certs/app/docker_actions.py delete mode 100644 acme-copy-certs/app/docker_monitor.py delete mode 100644 acme-copy-certs/app/file_watcher.py delete mode 100644 acme-copy-certs/app/service_manager.py delete mode 100644 acme-copy-certs/requirements.txt delete mode 100755 acme-copy-certs/tests/test.py diff --git a/acme-copy-certs/.gitignore b/acme-copy-certs/.gitignore deleted file mode 100644 index 38ce4278..00000000 --- a/acme-copy-certs/.gitignore +++ /dev/null @@ -1,123 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don’t work, or not -# install all needed dependencies. -#Pipfile.lock - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ diff --git a/acme-copy-certs/Dockerfile b/acme-copy-certs/Dockerfile deleted file mode 100644 index 337bea60..00000000 --- a/acme-copy-certs/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM alpine:3.9 - -WORKDIR /usr/src/app -COPY requirements.txt ./ - -RUN apk add --no-cache python3 && \ - python3 -m ensurepip && \ - rm -r /usr/lib/python*/ensurepip && \ - pip3 install --upgrade pip setuptools -r requirements.txt && \ - rm -r /root/.cache diff --git a/acme-copy-certs/README.md b/acme-copy-certs/README.md deleted file mode 100644 index fa579594..00000000 --- a/acme-copy-certs/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# ACME Copy certs - -Ce service permet l'exploitation des certificats TLS générés par Traefik -par d'autres serveurs pour leur propres besoins de chiffrement de connexions. - -Traefik a la capacité d'automatiser la demande et le renouvellement de certificats -TLS auprès de services en ligne tels que [Let's Encrypt](https://letsencrypt.org) -pour assurer le chiffrement des connexions HTTPS aux sites web dont il a la charge. - -L'idée est de déléguer la gestion de certificats utilisés par d'autres services dans -d'autres conteneurs à Traefik, ce qui présente les avantages suivants : - * Les certificats générés sont signés par une autorité de certification publique dont les - certificats sont déjà présents sur la plupart des systèmes, ce qui facilite la - vérification de l'authenticité des certificats par rapport à des certificats auto-signés (pas - de certificats de signature à déployer) - * A leur expiration, les certificats sont automatiquement renouvelés, ce qui élimine au tâche d'administration - et garantit la disponibilité continue des services \ No newline at end of file diff --git a/acme-copy-certs/app/cert_updater.py b/acme-copy-certs/app/cert_updater.py deleted file mode 100644 index da5a24ae..00000000 --- a/acme-copy-certs/app/cert_updater.py +++ /dev/null @@ -1,200 +0,0 @@ -""" Traefik Certificate extraction and update module - -Provides tools to extract TLS certificates from Treafik acme.json files. - -Classes -------- - - CertUpdater: - This class handles acme files decoding and stores or updates the resulting - certificates in separate folders. -""" - -import os -import errno -import json -from base64 import b64decode - -class CertUpdater: - """Decodes acme files and extact the resulting certificates if needed. - - This class keeps flat certificates for specified domain names - in sync with the contents of an acme.json file. The certificates are stored in separated - subdirectories named after the main domain name they are valid for. - If the certificates specify x509 SAN records, only the main CN is used (a single - directory is created, regardless of the number of alternate names.) - - Attributes - ---------- - acme_file : str - Name of the acme.json file containing the certificates - names : list - List of domain names to keep updated - certs_root_path : str - Directory where the certificates will be stored. The certificates are - stored in subdirectories of this directory - - Methods - ------- - add(name) - Adds a domain name to the updater - - remove(name) - Removes a domain name from the updater - - update() - Decodes the acme.json file and updates the certicates that need to be updated. - - - - """ - def __init__(self, acme_file, certs_root_path): - """ - Parameters - ---------- - acme_file : str - path of the acme file containing the certificates - certs_root_path : str - root directory where certificates will be extracted. - They are extracted in subdirectories named after the CN stored in - the certificate. - """ - self.acme_file = acme_file - self.names = [] - self.certs_root_path = certs_root_path + '/' - - def add(self, name): - """Adds a domain name to the updater. - - Parameters - ---------- - name : str - domain name to add - """ - - self.names.append(name) - - # Create directories etc if they do not exist - try: - os.makedirs(self.certs_root_path + name + '/') - except OSError as error: - if error.errno != errno.EEXIST: - raise - - def remove(self, name): - """Removes a domain name from the updater. - - Parameters - ---------- - name : str - domain name to remove - """ - - try: - self.names.remove(name) - except ValueError: - pass - - def update(self): - """Decodes the acme.json file and updates the certicates that need to be updated. - - Returns - ------- - list - a list of domain names that have been updated. An empty list if no certificate - has been updated. - """ - - # Open and decode the acme file - try: - with open(self.acme_file) as acme: - data = json.loads(acme.read()) - except FileNotFoundError: - print('ACME file {0} not found when trying to decode.'.format(self.acme_file)) - return [] - except json.JSONDecodeError: - print('File {0} does not look like an ACME file.'.format(self.acme_file)) - return [] - - # Get the acme file version - try: - acme_ver = 2 if 'acme-v02' in data['Account']['Registration']['uri'] else 1 - except TypeError: - if 'DomainsCertificate' in data: - acme_ver = 1 - else: - acme_ver = 2 - - # Get the certificates - if acme_ver == 1: - certs = data['DomainsCertificate']['Certs'] - elif acme_ver == 2: - certs = data['Certificates'] - - # Iterate over certificates. If a certificate has been updated, - # add its name to the updated_names. - updated_names = [] - - for c in certs: - if acme_ver == 1: - name = c['Certificate']['Domain'] - key = c['Certificate']['PrivateKey'] - fullchain = c['Certificate']['Certificate'] - elif acme_ver == 2: - name = c['Domain']['Main'] - key = c['Key'] - fullchain = c['Certificate'] - - if name in self.names: - key = b64decode(key).decode('utf-8') - fullchain = b64decode(fullchain).decode('utf-8') - chain_start = fullchain.find('-----BEGIN CERTIFICATE-----', 1) - cert = fullchain[0:chain_start] - chain = fullchain[chain_start:] - - print('Updating certificates for {0}'.format(name)) - if self._needs_updating(name, fullchain): - path = self.certs_root_path + name + '/' - with open(path + 'privkey.pem', 'w') as f: - f.write(key) - - with open(path + 'cert.pem', 'w') as f: - f.write(cert) - - with open(path + 'chain.pem', 'w') as f: - f.write(chain) - - with open(path + 'fullchain.pem', 'w') as f: - f.write(fullchain) - - print('Certificates updated') - - updated_names.append(name) - else: - print('Cetificates are already up-to-date') - - return updated_names - - def _needs_updating(self, name, fullchain): - """Checks if a certificate has changed - - Parameters - ---------- - name : str - Name of the directory containing the certificates - fullchain : str - Full certificates extracted from the acme.json file - - Returns - ------- - bool - True if the contents of the current certificates are - different from the fullchain. False otherwise. - """ - - path = self.certs_root_path + name + '/fullchain.pem' - try: - with open(path, 'r') as f: - return f.read() != fullchain - except FileNotFoundError: - return True \ No newline at end of file diff --git a/acme-copy-certs/app/docker_actions.py b/acme-copy-certs/app/docker_actions.py deleted file mode 100644 index 06735e64..00000000 --- a/acme-copy-certs/app/docker_actions.py +++ /dev/null @@ -1,39 +0,0 @@ -import docker - -class DockerAction: - def __init__(self, docker_client, container_id): - self.client = docker_client - self.container_id = container_id - pass - - def exec(self, id): - c = self.client.container.get(self.container_id) - - if c.status == 'running': - self.action(c) - - def action(self, container): - pass - -class RestartDockerAction(DockerAction): - def __init__(self, docker_client, container_id): - super().__init__(docker_client, container_id) - - def action(self, container): - try: - container.restart() - except docker.errors.APIError: - print('Unable to restart container {0}({1})'.format(container.name, - container.id)) - -class KillDockerAction(DockerAction): - def __init__(self, docker_client, container_id, signal='SIGKILL'): - super().__init__(docker_client, container_id) - self.signal = signal - - def action(self, container): - try: - container.kill(self.signal) - except docker.errors.APIError: - print('Unable to kill container {0}({1}) with signal {2}' - .format(container.name, self.container_id, self.signal)) \ No newline at end of file diff --git a/acme-copy-certs/app/docker_monitor.py b/acme-copy-certs/app/docker_monitor.py deleted file mode 100644 index 13e41ca6..00000000 --- a/acme-copy-certs/app/docker_monitor.py +++ /dev/null @@ -1,120 +0,0 @@ -"""Docker event monitoring - -Provides a monitor for docker events that add/remove services when -they occur according to the configuration provided in the labels of these -services. - -Classes -------- - DockerMonitor - A monitor for docker events. -""" - -import docker -import threading -import requests - -from service_manager import Service -from docker_actions import RestartDockerAction -from docker_actions import KillDockerAction - -class DockerMonitor(threading.Thread): - """Implements a docker events monitoring thread. - - This class implements a thread that listen to the docker socket to intercept - events on specific containers to launch actions on these when start and stop events occur. - Containers that should be monitored are identified by labels. - - """ - def __init__(self, services_mgr): - self.docker_client = docker.from_env() - self.events = None - self.services_mgr = services_mgr - super().__init__() - - def run(self): - print('Starting monitor') - # Identify all running containers and get dependent services - # at startup - try: - containers = self.docker_client.containers.list() - - for c in containers: - if c.status == 'running': - self._add_service(c) - - # Then, wait for docker events and identify new dependent services - # or services that exit the system. Do an update each time a new - # service is added / restarted etc. or a service is changed. - self.events = self.docker_client.events(decode = True) - for event in self.events: - if 'status' not in event: - continue - - try: - c = self.docker_client.containers.get(event['id']) - if event['status'] == 'stop': - self._remove_service(c) - elif event['status'] == 'start': - self._add_service(c) - - except docker.errors.NotFound: - pass - except docker.errors.APIError as error: - print('Docker error while looking up container {0}: {1} '.format(event.id, error.strerror)) - except requests.exceptions.ConnectionError as error: - print('Connection to docker socket refused.') - - print('No more events to handle: stopping monitor') - - def stop(self): - if self.events is not None: - self.events.close() - self.join() - - def _get_host_from_traefik_rule(self, container): - if 'traefik.frontend.rule' in container.labels: - try: - return container.labels['traefik.frontend.rule'].split('Host:')[1].split(',')[0].strip() - except IndexError: - return '' - - def _get_action_from_label(self, container): - if 'acme_copy_certs.action' not in container.labels: - return None - - try: - action = container.label('acme_copy_certs.action') - if action[0].strip() == 'kill': - if len(action) == 1: - return KillDockerAction(self.docker_client, container.id, 'SIGHUP') - else: - return KillDockerAction(self.docker_client, container.id, action[1].strip()) - elif action[0].strip() == 'restart': - return RestartDockerAction(self.docker_client, container.id) - else: - return None - - except IndexError: - print('Invalid action') - return None - - def _add_service(self, container): - if 'acme_copy_certs.enable' in container.labels: - if container.labels['acme_copy_certs.enable'] == 'true': - host = self._get_host_from_traefik_rule(container) - elif container.labels['acme_copy_certs.enable'] == 'false': - pass - else: - print('Invalid acme_copy_certs.enable value for {0}' - .format(container.name)) - - if host: - print('Handling container {0}({1})'.format(container.name, container.id)) - s = Service(container.id, host, self._get_action_from_label(container)) - self.services_mgr.add(s) - else: - print('Not handling container {0}({1})'.format(container.name, container.id)) - - def _remove_service(self, container): - self.services_mgr.remove(container.id) \ No newline at end of file diff --git a/acme-copy-certs/app/file_watcher.py b/acme-copy-certs/app/file_watcher.py deleted file mode 100644 index 580041be..00000000 --- a/acme-copy-certs/app/file_watcher.py +++ /dev/null @@ -1,33 +0,0 @@ -import os -from watchdog.observers import Observer -from watchdog.events import PatternMatchingEventHandler - -class FileWatcher: - def __init__(self, file_path, handler): - self.observer = Observer() - abs_path, file_name = os.path.split(os.path.abspath(file_path)) - self.observed_path = abs_path - self.observed_file = file_name - self.handler = handler - - self.event_handler = PatternMatchingEventHandler( - patterns=['*/' + file_name], - ignore_patterns=[], - ignore_directories=True) - - self.event_handler.on_any_event = self._on_any_event - - self.observer.schedule(self.event_handler, - self.observed_path, - recursive=False) - - def _on_any_event(self, event): - self.handler() - - def start(self): - print('Starting observing file {0} in directory {1}'.format(self.observed_file, self.observed_path)) - self.observer.start() - - def stop(self): - self.observer.stop() - self.observer.join() \ No newline at end of file diff --git a/acme-copy-certs/app/service_manager.py b/acme-copy-certs/app/service_manager.py deleted file mode 100644 index b20e84dc..00000000 --- a/acme-copy-certs/app/service_manager.py +++ /dev/null @@ -1,139 +0,0 @@ -"""Handling of docker services - -This module provides a ServicesManager class implementing a collection of -services whose certificates are to be updated by the ssytem. - -Classes -------- - Service - A class representing a single service - - ServiceManager - A class handling a collection of services -""" - -from threading import Lock - -class Service: - """ - A service wrapper This object represents a docker service - - Attributes - ---------- - - id : int - The id of the container running the service - host : str - The hostname associated with the service used for CN in TLS certs - action : DockerAction - An action to perform on the service when the run_action method is - invoked. - - Methods - ------- - - run_action() - Runs the action bound to this service - """ - - def __init__(self, id, host, action): - """ - Parameters - ---------- - id : int - The id of the container running the service - host : str - The hostname associated with the service used for CN in TLS certs - action : DockerAction - An action to perform on the service when the run_action method is invoked. - """ - - self.id = id - self.host = host - self.action = action - - def run_action(self): - """Executes the action on the container associated to the service.""" - if self.action is not None: - self.action.exec(self.id) - -class ServicesManager: - """ - A ServicesManager handles a collection of services and forwards certicate update - requests to a CertUpdater. If a service certificate is updated (either after adding a - service or after an explicit update request), the docker action associated - with the service is executed. - - Attribures - ---------- - services_by_id : dict - A dictionnary of the managed services identified by their container id. - services_by_host : dict - A dictionnary of the managed services identified by the CN of their - certificates. - updater : CertUpdater object - The certificates updater in charge of handling the certificates for the managed - services - - Methods - ------- - add(service) - Adds a new service to the collection - - remove(id) - Removes a service from the collection based on its container id - - update() - Checks if any certificate for a managed service needs to be updated. If - so, performs the docker action associated with the service on its - container - """ - - def __init__(self, updater): - """ - Parameters - ---------- - updater : CertUpdater - The CertUpdater used to handle certificates for the services - """ - self.services_by_id = {} - self.services_by_host = {} - self.updater = updater - self.lock = Lock() - - def add(self, service): - """ Adds a service to the collection and enables handling of its - certificates - """ - with self.lock: - self.services_by_id[service.id] = service - if service.host not in self.services_by_host: - self.services_by_host[service.host] = [service] - else: - self.services_by_host[service.host].append(service) - - self.updater.add(service.host) - self.update() - - def remove(self, id): - """ Removes a service by its id from the collection and disables handling - of its certificates - """ - with self.lock: - if id in self.services_by_id: - service = self.services_by_id[id] - host = service.host - self.services_by_host[host].remove(service) - del self.services_by_id[id] - if len(self.services_by_host[host]) == 0: - del self.services_by_host[host] - self.updater.remove(host) - - def update(self): - """ Updates the certificates of the services if needed. If a certificate - has changed, exectute the associated docker action on the service container. - """ - with self.lock: - for h in self.updater.update(): - for s in self.services_by_host[h]: - s.run_action() \ No newline at end of file diff --git a/acme-copy-certs/requirements.txt b/acme-copy-certs/requirements.txt deleted file mode 100644 index c546145a..00000000 --- a/acme-copy-certs/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker -watchdog diff --git a/acme-copy-certs/tests/test.py b/acme-copy-certs/tests/test.py deleted file mode 100755 index 9772682d..00000000 --- a/acme-copy-certs/tests/test.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python3 - -import sys, os -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../app') - -import time -import cert_updater -import file_watcher -import docker_monitor -import service_manager - - -if __name__ == "__main__": - u = cert_updater.CertUpdater('./acme.json', '.') - - - sm = service_manager.ServicesManager(u) - - f = file_watcher.FileWatcher('./acme.json', sm.update) - dm = docker_monitor.DockerMonitor(sm) - - f.start() - dm.start() - - try: - while True: - time.sleep(1) - except KeyboardInterrupt: - dm.stop() - f.stop() -- GitLab