diff options
author | Christian Breunig <christian@breunig.cc> | 2025-04-28 22:06:40 +0200 |
---|---|---|
committer | Christian Breunig <christian@breunig.cc> | 2025-04-28 22:10:08 +0200 |
commit | b93427874a0e502f83c3cc450663e079af214ea9 (patch) | |
tree | 6de0866fa14d42fe4d0c2b97761abc21697e9540 /src | |
parent | b433f9d48141496926f9499808cb57067352e432 (diff) | |
download | vyos-1x-b93427874a0e502f83c3cc450663e079af214ea9.tar.gz vyos-1x-b93427874a0e502f83c3cc450663e079af214ea9.zip |
pki: T7122: place certbot behind reverse-proxy if cert used by haproxy
If we detect that an ACME issued certificate is consumed by haproxy service,
we will move the certbot webserver to localhost and a highport, to proxy the
request via haproxy which is already using port 80.
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/pki.py | 37 |
1 files changed, 35 insertions, 2 deletions
diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py index 521af61df..c1ff80d8a 100755 --- a/src/conf_mode/pki.py +++ b/src/conf_mode/pki.py @@ -27,6 +27,7 @@ from vyos.configdict import node_changed from vyos.configdiff import Diff from vyos.configdiff import get_config_diff from vyos.defaults import directories +from vyos.defaults import internal_ports from vyos.pki import encode_certificate from vyos.pki import is_ca_certificate from vyos.pki import load_certificate @@ -42,6 +43,7 @@ from vyos.utils.dict import dict_search from vyos.utils.dict import dict_search_args from vyos.utils.dict import dict_search_recursive from vyos.utils.file import read_file +from vyos.utils.network import check_port_availability from vyos.utils.process import call from vyos.utils.process import cmd from vyos.utils.process import is_systemd_service_active @@ -128,8 +130,13 @@ def certbot_request(name: str, config: dict, dry_run: bool=True): f'--standalone --agree-tos --no-eff-email --expand --server {config["url"]} '\ f'--email {config["email"]} --key-type rsa --rsa-key-size {config["rsa_key_size"]} '\ f'{domains}' - if 'listen_address' in config: + # When ACME is used behind a reverse proxy, we always bind to localhost + # whatever the CLI listen-address is configured for. + if 'used_by' in config and 'haproxy' in config['used_by']: + tmp += f' --http-01-address 127.0.0.1 --http-01-port {internal_ports["certbot_haproxy"]}' + elif 'listen_address' in config: tmp += f' --http-01-address {config["listen_address"]}' + # verify() does not need to actually request a cert but only test for plausability if dry_run: tmp += ' --dry-run' @@ -223,7 +230,7 @@ def get_config(config=None): continue path = search['path'] - path_str = ' '.join(path + found_path) + path_str = ' '.join(path + found_path).replace('_','-') #print(f'PKI: Updating config: {path_str} {item_name}') if path[0] == 'interfaces': @@ -234,6 +241,24 @@ def get_config(config=None): if not D.node_changed_presence(path): set_dependents(path[1], conf) + # Check PKI certificates if they are generated by ACME. If they are, traverse + # the current configutration and determine the service where the certificate + # is used by. This is needed to check if we might need to start ACME behing + # a reverse proxy. + if 'certificate' in pki: + for name, cert_config in pki['certificate'].items(): + if 'acme' not in cert_config: + continue + if not dict_search('system.load_balancing.haproxy', pki): + continue + used_by = [] + for cert_list, cli_path in dict_search_recursive( + pki['system']['load_balancing']['haproxy'], 'certificate'): + if name in cert_list: + used_by.append('haproxy') + if used_by: + pki['certificate'][name]['acme'].update({'used_by': used_by}) + return pki def is_valid_certificate(raw_data): @@ -325,6 +350,14 @@ def verify(pki): raise ConfigError(f'An email address is required to request '\ f'certificate for "{name}" via ACME!') + listen_address = None + if 'listen_address' in cert_conf['acme']: + listen_address = cert_conf['acme']['listen_address'] + + if 'used_by' not in cert_conf['acme']: + if not check_port_availability(listen_address, 80): + raise ConfigError(f'Port 80 is not available for ACME challenge for certificate "{name}"!') + if 'certbot_renew' not in pki: # Only run the ACME command if something on this entity changed, # as this is time intensive |