use Caddy instead of NGINX for vhosts
This commit is contained in:
parent
0d55f01bfc
commit
bd50f4142f
|
@ -14,7 +14,7 @@ Docker containers instead, which are much easier to work with than the VM.
|
||||||
|
|
||||||
First, make sure you create the virtualenv:
|
First, make sure you create the virtualenv:
|
||||||
```sh
|
```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'
|
sh -c 'python -m venv venv && . venv/bin/activate && pip install -r requirements.txt -r dev-requirements.txt'
|
||||||
```
|
```
|
||||||
Then bring up the containers:
|
Then bring up the containers:
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
1.0.11
|
1.0.12
|
||||||
|
|
|
@ -36,8 +36,6 @@ class CloudService:
|
||||||
self.members_domain = 'Members'
|
self.members_domain = 'Members'
|
||||||
self.vhost_mgr = VHostManager(
|
self.vhost_mgr = VHostManager(
|
||||||
vhost_dir=cfg.get('cloud vhosts_config_dir'),
|
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.max_vhosts_per_account = cfg.get('cloud vhosts_max_vhosts_per_account')
|
||||||
self.vhost_domain = cfg.get('cloud vhosts_members_domain')
|
self.vhost_domain = cfg.get('cloud vhosts_members_domain')
|
||||||
|
|
|
@ -10,22 +10,15 @@ from zope.interface import implementer
|
||||||
from ceo_common.logger_factory import logger_factory
|
from ceo_common.logger_factory import logger_factory
|
||||||
from ceo_common.interfaces import IVHostManager
|
from ceo_common.interfaces import IVHostManager
|
||||||
|
|
||||||
PROXY_PASS_IP_RE = re.compile(r'^\s+proxy_pass\s+http://(?P<ip_address>[\d.]+);$')
|
REVERSE_PROXY_IP_RE = re.compile(r'^\s+reverse_proxy\s+http://(?P<ip_address>[\d.]+)$')
|
||||||
VHOST_FILENAME_RE = re.compile(r'^member_(?P<username>[0-9a-z-]+)_(?P<domain>[0-9a-z.-]+)$')
|
VHOST_FILENAME_RE = re.compile(r'^(?P<username>[0-9a-z-]+)_(?P<domain>[0-9a-z.-]+)$')
|
||||||
logger = logger_factory(__name__)
|
logger = logger_factory(__name__)
|
||||||
|
|
||||||
|
|
||||||
@implementer(IVHostManager)
|
@implementer(IVHostManager)
|
||||||
class VHostManager:
|
class VHostManager:
|
||||||
def __init__(
|
def __init__(self, vhost_dir: str):
|
||||||
self,
|
|
||||||
vhost_dir: str,
|
|
||||||
ssl_cert_path: str,
|
|
||||||
ssl_key_path: str,
|
|
||||||
):
|
|
||||||
self.vhost_dir = vhost_dir
|
self.vhost_dir = vhost_dir
|
||||||
self.ssl_cert_path = ssl_cert_path
|
|
||||||
self.ssl_key_path = ssl_key_path
|
|
||||||
self.jinja_env = jinja2.Environment(
|
self.jinja_env = jinja2.Environment(
|
||||||
loader=jinja2.PackageLoader('ceod.model'),
|
loader=jinja2.PackageLoader('ceod.model'),
|
||||||
)
|
)
|
||||||
|
@ -35,7 +28,7 @@ class VHostManager:
|
||||||
"""Generate a filename for the vhost record"""
|
"""Generate a filename for the vhost record"""
|
||||||
# sanity check...
|
# sanity check...
|
||||||
assert '..' not in domain and '/' not in domain
|
assert '..' not in domain and '/' not in domain
|
||||||
return 'member' + '_' + username + '_' + domain
|
return username + '_' + domain
|
||||||
|
|
||||||
def _vhost_filepath(self, username: str, domain: str) -> str:
|
def _vhost_filepath(self, username: str, domain: str) -> str:
|
||||||
"""Generate an absolute path for the vhost record"""
|
"""Generate an absolute path for the vhost record"""
|
||||||
|
@ -43,17 +36,16 @@ class VHostManager:
|
||||||
|
|
||||||
def _vhost_files(self, username: str) -> List[str]:
|
def _vhost_files(self, username: str) -> List[str]:
|
||||||
"""Return a list of all vhost files for this user."""
|
"""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):
|
def _reload_web_server(self):
|
||||||
logger.debug('Reloading nginx')
|
logger.debug('Reloading Caddy')
|
||||||
subprocess.run(['systemctl', 'reload', 'nginx'], check=True)
|
subprocess.run(['systemctl', 'reload', 'caddy'], check=True)
|
||||||
|
|
||||||
def create_vhost(self, username: str, domain: str, ip_address: str):
|
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(
|
body = template.render(
|
||||||
username=username, domain=domain, ip_address=ip_address,
|
username=username, domain=domain, ip_address=ip_address)
|
||||||
ssl_cert_path=self.ssl_cert_path, ssl_key_path=self.ssl_key_path)
|
|
||||||
filepath = self._vhost_filepath(username, domain)
|
filepath = self._vhost_filepath(username, domain)
|
||||||
logger.info(f'Writing a new vhost ({domain} -> {ip_address}) to {filepath}')
|
logger.info(f'Writing a new vhost ({domain} -> {ip_address}) to {filepath}')
|
||||||
with open(filepath, 'w') as fo:
|
with open(filepath, 'w') as fo:
|
||||||
|
@ -78,7 +70,7 @@ class VHostManager:
|
||||||
domain = match.group('domain')
|
domain = match.group('domain')
|
||||||
ip_address = None
|
ip_address = None
|
||||||
for line in open(filepath):
|
for line in open(filepath):
|
||||||
match = PROXY_PASS_IP_RE.match(line)
|
match = REVERSE_PROXY_IP_RE.match(line)
|
||||||
if match is None:
|
if match is None:
|
||||||
continue
|
continue
|
||||||
ip_address = match.group('ip_address')
|
ip_address = match.group('ip_address')
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -81,10 +81,8 @@ secret_key = REPLACE_ME
|
||||||
base_url = http://localhost:8080/client/api
|
base_url = http://localhost:8080/client/api
|
||||||
|
|
||||||
[cloud vhosts]
|
[cloud vhosts]
|
||||||
config_dir = /etc/nginx/ceod-member-vhosts
|
config_dir = /etc/caddy/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
|
max_vhosts_per_account = 10
|
||||||
members_domain = m.csclub.cloud
|
members_domain = csclub.cloud
|
||||||
ip_range_min = 172.19.134.10
|
ip_range_min = 172.19.134.10
|
||||||
ip_range_max = 172.19.134.160
|
ip_range_max = 172.19.134.160
|
||||||
|
|
|
@ -102,7 +102,7 @@ def test_cloud_vhosts(cfg, client, new_user, ldap_conn):
|
||||||
assert data == {'vhosts': [{'domain': domain1, 'ip_address': ip1}]}
|
assert data == {'vhosts': [{'domain': domain1, 'ip_address': ip1}]}
|
||||||
|
|
||||||
# invalid domain name
|
# invalid domain name
|
||||||
domain2 = uid + 'm.cloud.' + cfg.get('base_domain')
|
domain2 = uid + 'cloud.' + cfg.get('base_domain')
|
||||||
ip2 = ip1
|
ip2 = ip1
|
||||||
status, _ = client.put(
|
status, _ = client.put(
|
||||||
f'/api/cloud/vhosts/{domain2}', json={'ip_address': ip2},
|
f'/api/cloud/vhosts/{domain2}', json={'ip_address': ip2},
|
||||||
|
|
|
@ -4,8 +4,8 @@ import os
|
||||||
def test_vhost_mgr(cloud_srv):
|
def test_vhost_mgr(cloud_srv):
|
||||||
vhost_mgr = cloud_srv.vhost_mgr
|
vhost_mgr = cloud_srv.vhost_mgr
|
||||||
username = 'test1'
|
username = 'test1'
|
||||||
domain = username + '.m.csclub.cloud'
|
domain = username + '.csclub.cloud'
|
||||||
filename = f'member_{username}_{domain}'
|
filename = f'{username}_{domain}'
|
||||||
ip_address = '172.19.134.11'
|
ip_address = '172.19.134.11'
|
||||||
vhost_mgr.create_vhost(username, domain, ip_address)
|
vhost_mgr.create_vhost(username, domain, ip_address)
|
||||||
path = os.path.join(vhost_mgr.vhost_dir, filename)
|
path = os.path.join(vhost_mgr.vhost_dir, filename)
|
||||||
|
|
|
@ -76,9 +76,7 @@ base_url = http://localhost:8080/client/api
|
||||||
|
|
||||||
[cloud vhosts]
|
[cloud vhosts]
|
||||||
config_dir = /run/ceod/member-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
|
max_vhosts_per_account = 10
|
||||||
members_domain = m.csclub.cloud
|
members_domain = csclub.cloud
|
||||||
ip_range_min = 172.19.134.10
|
ip_range_min = 172.19.134.10
|
||||||
ip_range_max = 172.19.134.160
|
ip_range_max = 172.19.134.160
|
||||||
|
|
|
@ -75,9 +75,7 @@ base_url = http://localhost:8080/client/api
|
||||||
|
|
||||||
[cloud vhosts]
|
[cloud vhosts]
|
||||||
config_dir = /run/ceod/member-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
|
max_vhosts_per_account = 10
|
||||||
members_domain = m.csclub.cloud
|
members_domain = csclub.cloud
|
||||||
ip_range_min = 172.19.134.10
|
ip_range_min = 172.19.134.10
|
||||||
ip_range_max = 172.19.134.160
|
ip_range_max = 172.19.134.160
|
||||||
|
|
Loading…
Reference in New Issue