summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2025-04-28 22:06:40 +0200
committerChristian Breunig <christian@breunig.cc>2025-04-28 22:10:08 +0200
commitb93427874a0e502f83c3cc450663e079af214ea9 (patch)
tree6de0866fa14d42fe4d0c2b97761abc21697e9540 /src
parentb433f9d48141496926f9499808cb57067352e432 (diff)
downloadvyos-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-xsrc/conf_mode/pki.py37
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