diff --git a/.travis/dispatch.sh b/.travis/dispatch.sh index 374c8f107..a434be475 100755 --- a/.travis/dispatch.sh +++ b/.travis/dispatch.sh @@ -141,6 +141,7 @@ else ./test.py sls.servo-build-dependencies.android fi + ./test.py sls.admin # Salt doesn't support timezone.system on OSX # See https://github.com/saltstack/salt/issues/31345 if [[ ! "${SALT_NODE_ID}" =~ servo-mac.* ]]; then diff --git a/admin/files/sshd_config b/admin/files/sshd_config new file mode 100644 index 000000000..9a14c3d3a --- /dev/null +++ b/admin/files/sshd_config @@ -0,0 +1,25 @@ +HostKey /etc/ssh/{{ hostkey }} +KexAlgorithms curve25519-sha256@libssh.org +Ciphers chacha20-poly1305@openssh.com + +# Only pubkey authentication is enabled, i.e. password logins are disabled +PasswordAuthentication no +ChallengeResponseAuthentication no +PubkeyAuthentication yes +AuthenticationMethods publickey +# TODO: Disable root login after creating per-user accounts +PermitRootLogin yes + +MaxAuthTries 2 +LoginGraceTime 1m + +{% if grains['kernel'] == 'Linux' %} +UsePAM yes +# PAM does this +PrintMotd no +{% endif %} + +UsePrivilegeSeparation sandbox +# LogLevel VERBOSE logs user's key fingerprint on login. +# Needed to have a clear audit track of which key was using to log in. +LogLevel VERBOSE diff --git a/admin/init.sls b/admin/init.sls index 721982f2c..b9520f010 100644 --- a/admin/init.sls +++ b/admin/init.sls @@ -1,5 +1,5 @@ {% from 'common/map.jinja' import root %} -{% from tpldir ~ '/map.jinja' import admin %} +{% from tpldir ~ '/map.jinja' import admin, hostkey %} admin-packages: pkg.installed: @@ -7,7 +7,8 @@ admin-packages: - tmux - mosh {% if grains['os'] != 'MacOS' %} - - screen # Installed by default on OS X + - openssh-server # Use default macOS version, not Homebrew's + - screen # Installed by default on macOS {% endif %} {% if grains['os'] != 'MacOS' and grains.get('virtual_subtype', '') != 'Docker' %} @@ -22,6 +23,22 @@ UTC: - mode: 644 - source: salt://{{ tpldir }}/files/hosts +sshd_config: + file.managed: + - name: /etc/ssh/sshd_config + - user: {{ root.user }} + - group: {{ root.group }} + - mode: 644 + - template: jinja + - source: salt://{{ tpldir }}/files/sshd_config + - defaults: + hostkey: "{{ hostkey }}" + cmd.run: + - name: ssh-keygen -A + - runas: {{ root.user }} + - creates: + - /etc/ssh/{{ hostkey }} + sshkeys-dir: file.directory: - name: {{ root.home }}/.ssh @@ -41,3 +58,14 @@ sshkeys: {% endfor %} - require: - file: sshkeys-dir + +{% if grains['os'] != 'MacOS' %} +sshd: + service.running: + - name: ssh + - enable: True + - require: + - file: sshkeys + - watch: + - file: sshd_config +{% endif %} diff --git a/admin/map.jinja b/admin/map.jinja index 2869236d5..7a4eed7c6 100644 --- a/admin/map.jinja +++ b/admin/map.jinja @@ -1,3 +1,6 @@ +{% + set hostkey = 'ssh_host_ed25519_key' +%} {% set admin = { 'ssh_users': [ diff --git a/tests/sls/admin/__init__.py b/tests/sls/admin/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/sls/admin/valid_sshd_config.py b/tests/sls/admin/valid_sshd_config.py new file mode 100644 index 000000000..3ac894ad7 --- /dev/null +++ b/tests/sls/admin/valid_sshd_config.py @@ -0,0 +1,20 @@ +import subprocess + +from tests.util import Failure, Success + + +def run(): + proc = subprocess.Popen( + ['sshd', '-T', '-f', '/etc/ssh/sshd_config'], + stdout=subprocess.DEVNULL, + stderr=subprocess.PIPE, + universal_newlines=True + ) + _, stderr = proc.communicate() + + if proc.returncode != 0: + return Failure( + 'Invalid sshd_config file:', stderr + ) + + return Success('SSHD config file is valid')