From bd50f4142f0a3ee5274858d5897ed63d2c80366d Mon Sep 17 00:00:00 2001 From: Max Erenberg <> Date: Sun, 28 Nov 2021 15:21:48 -0500 Subject: [PATCH] use Caddy instead of NGINX for vhosts --- README.md | 2 +- VERSION.txt | 2 +- ceod/model/CloudService.py | 2 -- ceod/model/VHostManager.py | 28 +++++++------------ .../templates/caddy_cloud_vhost_config.j2 | 22 +++++++++++++++ .../templates/nginx_cloud_vhost_config.j2 | 25 ----------------- etc/ceod.ini | 6 ++-- tests/ceod/api/test_cloud.py | 2 +- tests/ceod/model/test_vhosts.py | 4 +-- tests/ceod_dev.ini | 4 +-- tests/ceod_test_local.ini | 4 +-- 11 files changed, 41 insertions(+), 60 deletions(-) create mode 100644 ceod/model/templates/caddy_cloud_vhost_config.j2 delete mode 100644 ceod/model/templates/nginx_cloud_vhost_config.j2 diff --git a/README.md b/README.md index c9c846e..70e426d 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Docker containers instead, which are much easier to work with than the VM. First, make sure you create the virtualenv: ```sh -docker run --rm -v "$PWD:$PWD" -w "$PWD" -u $(id -u):$(id -g) python:3.7-buster \ +docker run --rm -v "$PWD:$PWD" -w "$PWD" python:3.7-buster \ sh -c 'python -m venv venv && . venv/bin/activate && pip install -r requirements.txt -r dev-requirements.txt' ``` Then bring up the containers: diff --git a/VERSION.txt b/VERSION.txt index 59e9e60..bb83058 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.0.11 +1.0.12 diff --git a/ceod/model/CloudService.py b/ceod/model/CloudService.py index fd64e3d..85c5819 100644 --- a/ceod/model/CloudService.py +++ b/ceod/model/CloudService.py @@ -36,8 +36,6 @@ class CloudService: self.members_domain = 'Members' self.vhost_mgr = VHostManager( vhost_dir=cfg.get('cloud vhosts_config_dir'), - ssl_cert_path=cfg.get('cloud vhosts_ssl_cert_path'), - ssl_key_path=cfg.get('cloud vhosts_ssl_key_path'), ) self.max_vhosts_per_account = cfg.get('cloud vhosts_max_vhosts_per_account') self.vhost_domain = cfg.get('cloud vhosts_members_domain') diff --git a/ceod/model/VHostManager.py b/ceod/model/VHostManager.py index a4a86a9..b28062d 100644 --- a/ceod/model/VHostManager.py +++ b/ceod/model/VHostManager.py @@ -10,22 +10,15 @@ from zope.interface import implementer from ceo_common.logger_factory import logger_factory from ceo_common.interfaces import IVHostManager -PROXY_PASS_IP_RE = re.compile(r'^\s+proxy_pass\s+http://(?P[\d.]+);$') -VHOST_FILENAME_RE = re.compile(r'^member_(?P[0-9a-z-]+)_(?P[0-9a-z.-]+)$') +REVERSE_PROXY_IP_RE = re.compile(r'^\s+reverse_proxy\s+http://(?P[\d.]+)$') +VHOST_FILENAME_RE = re.compile(r'^(?P[0-9a-z-]+)_(?P[0-9a-z.-]+)$') logger = logger_factory(__name__) @implementer(IVHostManager) class VHostManager: - def __init__( - self, - vhost_dir: str, - ssl_cert_path: str, - ssl_key_path: str, - ): + def __init__(self, vhost_dir: str): self.vhost_dir = vhost_dir - self.ssl_cert_path = ssl_cert_path - self.ssl_key_path = ssl_key_path self.jinja_env = jinja2.Environment( loader=jinja2.PackageLoader('ceod.model'), ) @@ -35,7 +28,7 @@ class VHostManager: """Generate a filename for the vhost record""" # sanity check... assert '..' not in domain and '/' not in domain - return 'member' + '_' + username + '_' + domain + return username + '_' + domain def _vhost_filepath(self, username: str, domain: str) -> str: """Generate an absolute path for the vhost record""" @@ -43,17 +36,16 @@ class VHostManager: def _vhost_files(self, username: str) -> List[str]: """Return a list of all vhost files for this user.""" - return glob.glob(os.path.join(self.vhost_dir, 'member_' + username + '_*')) + return glob.glob(os.path.join(self.vhost_dir, username + '_*')) def _reload_web_server(self): - logger.debug('Reloading nginx') - subprocess.run(['systemctl', 'reload', 'nginx'], check=True) + logger.debug('Reloading Caddy') + subprocess.run(['systemctl', 'reload', 'caddy'], check=True) def create_vhost(self, username: str, domain: str, ip_address: str): - template = self.jinja_env.get_template('nginx_cloud_vhost_config.j2') + template = self.jinja_env.get_template('caddy_cloud_vhost_config.j2') body = template.render( - username=username, domain=domain, ip_address=ip_address, - ssl_cert_path=self.ssl_cert_path, ssl_key_path=self.ssl_key_path) + username=username, domain=domain, ip_address=ip_address) filepath = self._vhost_filepath(username, domain) logger.info(f'Writing a new vhost ({domain} -> {ip_address}) to {filepath}') with open(filepath, 'w') as fo: @@ -78,7 +70,7 @@ class VHostManager: domain = match.group('domain') ip_address = None for line in open(filepath): - match = PROXY_PASS_IP_RE.match(line) + match = REVERSE_PROXY_IP_RE.match(line) if match is None: continue ip_address = match.group('ip_address') diff --git a/ceod/model/templates/caddy_cloud_vhost_config.j2 b/ceod/model/templates/caddy_cloud_vhost_config.j2 new file mode 100644 index 0000000..200b4df --- /dev/null +++ b/ceod/model/templates/caddy_cloud_vhost_config.j2 @@ -0,0 +1,22 @@ +# This file is automatically managed by ceod. +# DO NOT EDIT THIS FILE MANUALLY UNLESS YOU KNOW WHAT YOU ARE DOING. + +{{ domain }} { + reverse_proxy http://{{ ip_address }} + log { + output file /var/log/caddy/member_{{ username }}.log { + roll_size 5MiB + roll_keep 2 + } + format filter { + wrap json + fields { + request>headers delete + request>tls delete + resp_headers delete + user_id delete + common_log delete + } + } + } +} diff --git a/ceod/model/templates/nginx_cloud_vhost_config.j2 b/ceod/model/templates/nginx_cloud_vhost_config.j2 deleted file mode 100644 index 869606d..0000000 --- a/ceod/model/templates/nginx_cloud_vhost_config.j2 +++ /dev/null @@ -1,25 +0,0 @@ -# This file is automatically managed by ceod. -# DO NOT EDIT THIS FILE MANUALLY. -# If you want to modify it, please move it to another directory. - -server { - listen 80; - listen [::]:80; - server_name {{ domain }}; - return 301 https://$host$request_uri; -} - -server { - listen 443 ssl; - listen [::]:443 ssl; - server_name {{ domain }}; - ssl_certificate {{ ssl_cert_path }}; - ssl_certificate_key {{ ssl_key_path }}; - - location / { - proxy_pass http://{{ ip_address }}; - } - - access_log /var/log/nginx/member-{{ username }}-access.log; - error_log /var/log/nginx/member-{{ username }}-error.log; -} diff --git a/etc/ceod.ini b/etc/ceod.ini index d1f02a0..174ddc1 100644 --- a/etc/ceod.ini +++ b/etc/ceod.ini @@ -81,10 +81,8 @@ secret_key = REPLACE_ME base_url = http://localhost:8080/client/api [cloud vhosts] -config_dir = /etc/nginx/ceod-member-vhosts -ssl_cert_path = /etc/ssl/private/csclub.cloud.chain -ssl_key_path = /etc/ssl/private/csclub.cloud.key +config_dir = /etc/caddy/ceod-member-vhosts max_vhosts_per_account = 10 -members_domain = m.csclub.cloud +members_domain = csclub.cloud ip_range_min = 172.19.134.10 ip_range_max = 172.19.134.160 diff --git a/tests/ceod/api/test_cloud.py b/tests/ceod/api/test_cloud.py index 9787489..4d4600b 100644 --- a/tests/ceod/api/test_cloud.py +++ b/tests/ceod/api/test_cloud.py @@ -102,7 +102,7 @@ def test_cloud_vhosts(cfg, client, new_user, ldap_conn): assert data == {'vhosts': [{'domain': domain1, 'ip_address': ip1}]} # invalid domain name - domain2 = uid + 'm.cloud.' + cfg.get('base_domain') + domain2 = uid + 'cloud.' + cfg.get('base_domain') ip2 = ip1 status, _ = client.put( f'/api/cloud/vhosts/{domain2}', json={'ip_address': ip2}, diff --git a/tests/ceod/model/test_vhosts.py b/tests/ceod/model/test_vhosts.py index 441a6a9..d01bb6c 100644 --- a/tests/ceod/model/test_vhosts.py +++ b/tests/ceod/model/test_vhosts.py @@ -4,8 +4,8 @@ import os def test_vhost_mgr(cloud_srv): vhost_mgr = cloud_srv.vhost_mgr username = 'test1' - domain = username + '.m.csclub.cloud' - filename = f'member_{username}_{domain}' + domain = username + '.csclub.cloud' + filename = f'{username}_{domain}' ip_address = '172.19.134.11' vhost_mgr.create_vhost(username, domain, ip_address) path = os.path.join(vhost_mgr.vhost_dir, filename) diff --git a/tests/ceod_dev.ini b/tests/ceod_dev.ini index 9fe19b5..9af5be7 100644 --- a/tests/ceod_dev.ini +++ b/tests/ceod_dev.ini @@ -76,9 +76,7 @@ base_url = http://localhost:8080/client/api [cloud vhosts] config_dir = /run/ceod/member-vhosts -ssl_cert_path = /etc/ssl/private/csclub.cloud.chain -ssl_key_path = /etc/ssl/private/csclub.cloud.key max_vhosts_per_account = 10 -members_domain = m.csclub.cloud +members_domain = csclub.cloud ip_range_min = 172.19.134.10 ip_range_max = 172.19.134.160 diff --git a/tests/ceod_test_local.ini b/tests/ceod_test_local.ini index ff1b340..036ccfd 100644 --- a/tests/ceod_test_local.ini +++ b/tests/ceod_test_local.ini @@ -75,9 +75,7 @@ base_url = http://localhost:8080/client/api [cloud vhosts] config_dir = /run/ceod/member-vhosts -ssl_cert_path = /etc/ssl/private/csclub.cloud.chain -ssl_key_path = /etc/ssl/private/csclub.cloud.key max_vhosts_per_account = 10 -members_domain = m.csclub.cloud +members_domain = csclub.cloud ip_range_min = 172.19.134.10 ip_range_max = 172.19.134.160