use Caddy instead of NGINX for vhosts

This commit is contained in:
Max Erenberg 2021-11-28 15:21:48 -05:00
parent 0d55f01bfc
commit bd50f4142f
11 changed files with 41 additions and 60 deletions

View File

@ -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:

View File

@ -1 +1 @@
1.0.11
1.0.12

View File

@ -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')

View File

@ -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<ip_address>[\d.]+);$')
VHOST_FILENAME_RE = re.compile(r'^member_(?P<username>[0-9a-z-]+)_(?P<domain>[0-9a-z.-]+)$')
REVERSE_PROXY_IP_RE = re.compile(r'^\s+reverse_proxy\s+http://(?P<ip_address>[\d.]+)$')
VHOST_FILENAME_RE = re.compile(r'^(?P<username>[0-9a-z-]+)_(?P<domain>[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')

View File

@ -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
}
}
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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},

View File

@ -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)

View File

@ -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

View File

@ -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