From 1a239c55bb2955af607e2784eec63431ec5b607d Mon Sep 17 00:00:00 2001 From: KiekerJan Date: Sat, 23 Mar 2024 16:16:40 +0100 Subject: [PATCH] More robust reading of sshd configuration (#2330) Use sshd -T instead of directly reading the configuration files --- management/dns_update.py | 15 +++++-------- management/status_checks.py | 44 +++++++++---------------------------- management/utils.py | 28 +++++++++++++++++++++++ 3 files changed, 44 insertions(+), 43 deletions(-) diff --git a/management/dns_update.py b/management/dns_update.py index 597df243..599f27b1 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -9,7 +9,7 @@ import ipaddress import rtyaml import dns.resolver -from utils import shell, load_env_vars_from_file, safe_domain_name, sort_domains +from utils import shell, load_env_vars_from_file, safe_domain_name, sort_domains, get_ssh_port from ssl_certificates import get_ssl_certificates, check_certificate import contextlib @@ -448,14 +448,11 @@ def build_sshfp_records(): # if SSH has been configured to listen on a nonstandard port, we must # specify that port to sshkeyscan. - port = 22 - with open('/etc/ssh/sshd_config', encoding="utf-8") as f: - for line in f: - s = line.rstrip().split() - if len(s) == 2 and s[0] == 'Port': - with contextlib.suppress(ValueError): - port = int(s[1]) - break + port = get_ssh_port() + + # If nothing returned, SSH is probably not installed. + if not port: + return keys = shell("check_output", ["ssh-keyscan", "-4", "-t", "rsa,dsa,ecdsa,ed25519", "-p", str(port), "localhost"]) keys = sorted(keys.split("\n")) diff --git a/management/status_checks.py b/management/status_checks.py index 77019a4b..51f8e631 100755 --- a/management/status_checks.py +++ b/management/status_checks.py @@ -17,7 +17,7 @@ from web_update import get_web_domains, get_domains_with_a_records from ssl_certificates import get_ssl_certificates, get_domain_ssl_files, check_certificate from mailconfig import get_mail_domains, get_mail_aliases -from utils import shell, sort_domains, load_env_vars_from_file, load_settings +from utils import shell, sort_domains, load_env_vars_from_file, load_settings, get_ssh_port, get_ssh_config_value def get_services(): return [ @@ -65,24 +65,6 @@ def run_checks(rounded_values, env, output, pool, domains_to_check=None): run_network_checks(env, output) run_domain_checks(rounded_values, env, output, pool, domains_to_check=domains_to_check) -def get_ssh_port(): - # Returns ssh port - try: - output = shell('check_output', ['sshd', '-T']) - except FileNotFoundError: - # sshd is not installed. That's ok. - return None - - returnNext = False - for e in output.split(): - if returnNext: - return int(e) - if e == "port": - returnNext = True - - # Did not find port! - return None - def run_services_checks(env, output, pool): # Check that system services are running. all_running = True @@ -206,21 +188,15 @@ def is_port_allowed(ufw, port): return any(re.match(str(port) +"[/ \t].*", item) for item in ufw) def check_ssh_password(env, output): - # Check that SSH login with password is disabled. The openssh-server - # package may not be installed so check that before trying to access - # the configuration file. - if not os.path.exists("/etc/ssh/sshd_config"): - return - with open("/etc/ssh/sshd_config", encoding="utf-8") as f: - sshd = f.read() - if re.search("\nPasswordAuthentication\\s+yes", sshd) \ - or not re.search("\nPasswordAuthentication\\s+no", sshd): - output.print_error("""The SSH server on this machine permits password-based login. A more secure - way to log in is using a public key. Add your SSH public key to $HOME/.ssh/authorized_keys, check - that you can log in without a password, set the option 'PasswordAuthentication no' in - /etc/ssh/sshd_config, and then restart the openssh via 'sudo service ssh restart'.""") - else: - output.print_ok("SSH disallows password-based login.") + config_value = get_ssh_config_value("passwordauthentication") + if config_value: + if config_value == "no": + output.print_ok("SSH disallows password-based login.") + else: + output.print_error("""The SSH server on this machine permits password-based login. A more secure + way to log in is using a public key. Add your SSH public key to $HOME/.ssh/authorized_keys, check + that you can log in without a password, set the option 'PasswordAuthentication no' in + /etc/ssh/sshd_config, and then restart the openssh via 'sudo service ssh restart'.""") def is_reboot_needed_due_to_package_installation(): return os.path.exists("/var/run/reboot-required") diff --git a/management/utils.py b/management/utils.py index 929544d1..397f124d 100644 --- a/management/utils.py +++ b/management/utils.py @@ -179,6 +179,34 @@ def wait_for_service(port, public, env, timeout): return False time.sleep(min(timeout/4, 1)) +def get_ssh_port(): + port_value = get_ssh_config_value("port") + + if port_value: + return int(port_value) + + return None + +def get_ssh_config_value(parameter_name): + # Returns ssh configuration value for the provided parameter + try: + output = shell('check_output', ['sshd', '-T']) + except FileNotFoundError: + # sshd is not installed. That's ok. + return None + except subprocess.CalledProcessError: + # error while calling shell command + return None + + for line in output.split("\n"): + if " " not in line: continue # there's a blank line at the end + key, values = line.split(" ", 1) + if key == parameter_name: + return values # space-delimited if there are multiple values + + # Did not find the parameter! + return None + if __name__ == "__main__": from web_update import get_web_domains env = load_environment()