Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More robust reading of sshd configuration #2330

Merged
merged 4 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 6 additions & 9 deletions management/dns_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
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

Expand Down Expand Up @@ -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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think get_ssh_port can return None.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. But I think it should. I'll work on the get_ssh_port function

return

keys = shell("check_output", ["ssh-keyscan", "-4", "-t", "rsa,dsa,ecdsa,ed25519", "-p", str(port), "localhost"])
keys = sorted(keys.split("\n"))
Expand Down
44 changes: 10 additions & 34 deletions management/status_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
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 [
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
28 changes: 28 additions & 0 deletions management/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down