Merge pull request #267 from Jakuje/runtime

fix: Review and update service units and socket unit to include distribution defaults
This commit is contained in:
Richard Megginson 2024-01-25 14:25:32 -07:00 committed by GitHub
commit cb8c339a42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 375 additions and 39 deletions

View file

@ -15,6 +15,7 @@ jobs:
group: local
hosts: localhost
targets: "tests/tests_*.yml"
requirements: tests/requirements.yml
debian-bullseye:
runs-on: ubuntu-latest
@ -28,6 +29,7 @@ jobs:
group: local
hosts: localhost
targets: "tests/tests_*.yml"
requirements: tests/requirements.yml
debian-buster:
runs-on: ubuntu-latest
@ -41,3 +43,4 @@ jobs:
group: local
hosts: localhost
targets: "tests/tests_*.yml"
requirements: tests/requirements.yml

View file

@ -16,6 +16,7 @@ jobs:
group: local
hosts: localhost
targets: "tests/tests_*.yml"
requirements: tests/requirements.yml
ubuntu-20:
runs-on: ubuntu-latest
@ -30,3 +31,4 @@ jobs:
group: local
hosts: localhost
targets: "tests/tests_*.yml"
requirements: tests/requirements.yml

View file

@ -10,6 +10,7 @@
group: root
mode: "0644"
notify: reload_sshd
- name: Install instanced service unit file
ansible.builtin.template:
src: "{{ sshd_service_template_at_service }}"
@ -18,6 +19,9 @@
group: root
mode: "0644"
notify: reload_sshd
when:
- __sshd_socket_accept | bool
- name: Install socket unit file
ansible.builtin.template:
src: "{{ sshd_service_template_socket }}"

View file

@ -1,15 +1,37 @@
[Unit]
Description=OpenBSD Secure Shell server
After=network.target{{ (__sshd_service_after is none) | ternary('', ' ' ~ __sshd_service_after) }}
{% if __sshd_service_wants is string %}
Wants={{ __sshd_service_wants }}
{% elif __sshd_service_wants is iterable %}
{% for file in __sshd_service_wants %}
Wants={{ file }}
{% endfor %}
{% endif %}
Documentation=man:sshd(8) man:sshd_config(5)
[Service]
Type=notify
{% if __sshd_environment_file is string %}
EnvironmentFile=-{{ __sshd_environment_file }}
{% elif __sshd_environment_file is iterable %}
{% for file in __sshd_environment_file %}
EnvironmentFile=-{{ file }}
{% endfor %}
{% endif %}
ExecStartPre={{ sshd_binary }} -t
ExecStart={{ sshd_binary }} -D -f {{ sshd_config_file }}
ExecStart={{ sshd_binary }} -D {{ __sshd_environment_variable }} -f {% if sshd_main_config_file is not none %}
{{- sshd_main_config_file }}
{% else %}
{{- sshd_config_file }}
{% endif %}
ExecReload={{ sshd_binary }} -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
{% if __sshd_service_restart_timeout is not none %}
RestartSec={{ __sshd_service_restart_timeout }}
{% endif %}
RestartPreventExitStatus=255
{% if __sshd_runtime_directory is not none %}
RuntimeDirectory={{ __sshd_runtime_directory }}
@ -18,3 +40,6 @@ RuntimeDirectoryMode={{ __sshd_runtime_directory_mode }}
[Install]
WantedBy=multi-user.target
{% if __sshd_service_alias is not none %}
Alias={{ __sshd_service_alias }}.service
{% endif %}

View file

@ -1,12 +1,19 @@
[Unit]
Description=OpenBSD Secure Shell server socket
Documentation=man:sshd(8) man:sshd_config(5)
Before={{ sshd_service }}.service
{% if __sshd_socket_accept %}
Conflicts={{ sshd_service }}.service
{% else %}
Before=sockets.target
{% endif %}
[Socket]
ListenStream=22
{% if __sshd_socket_accept %}
Accept=yes
{% else %}
Accept=no
{% endif %}
[Install]
WantedBy=sockets.target

View file

@ -1,12 +1,33 @@
[Unit]
Description=OpenBSD Secure Shell server per-connection daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=auditd.service
{% if __sshd_service_after is not none %}
After={{ __sshd_service_after }}
{% endif %}
{% if __sshd_service_wants is string %}
Wants={{ __sshd_service_wants }}
{% elif __sshd_service_wants is iterable %}
{% for file in __sshd_service_wants %}
Wants={{ file }}
{% endfor %}
{% endif %}
[Service]
ExecStart=-{{ sshd_binary }} -i -f {{ sshd_config_file }}
{% if __sshd_environment_file is string %}
EnvironmentFile=-{{ __sshd_environment_file }}
{% elif __sshd_environment_file is iterable %}
{% for file in __sshd_environment_file %}
EnvironmentFile=-{{ file }}
{% endfor %}
{% endif %}
ExecStart=-{{ sshd_binary }} -i {{ __sshd_environment_variable }} -f {% if sshd_main_config_file is not none %}
{{- sshd_main_config_file }}
{% else %}
{{- sshd_config_file }}
{% endif %}
StandardInput=socket
{% if __sshd_runtime_directory is not none %}
RuntimeDirectory={{ __sshd_runtime_directory }}
RuntimeDirectoryPreserve=yes
RuntimeDirectoryMode={{ __sshd_runtime_directory_mode }}
{% endif %}

View file

@ -58,4 +58,4 @@
main_sshd_config_path: /etc/ssh/sshd_config.d/
when:
- (ansible_facts['os_family'] == 'RedHat' and ansible_facts['distribution_major_version'] | int > 8) or
(ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_major_version']|int >= 22)
(ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_major_version'] | int >= 20)

View file

@ -90,10 +90,10 @@
ansible.builtin.shell: >-
set -o pipefail && man sshd_config \
| sed 's/\x08.//g' \
| grep -o '^ [A-Z][A-Za-z0-9]*\(.\| \)' \
| grep -o '^\( \| \)[A-Z][A-Za-z0-9]*\(.\| \)' \
| grep -v "[A-Za-z0-9] $" | grep -v "[^A-Za-z0-9 ]$" \
| awk '{ print $1 }' \
| grep -v '^$' | grep -v "^Match$"
| grep -v '^$' | grep -v "^\(Match\|OpenSSH\|The\|Arguments\|Theo\)$"
args:
executable: /bin/bash
register: sshd_options
@ -102,7 +102,7 @@
- name: Print all the possible options
ansible.builtin.debug:
var: ssh_options.stdout_lines
var: sshd_options.stdout_lines
- name: Construct the configuration list
ansible.builtin.set_fact:

View file

@ -135,12 +135,19 @@
- ansible_facts['os_family'] == 'Debian'
- ansible_facts['distribution_major_version'] | int < 22
- name: Check Ubuntu 22 defaults are present in the first configuration file
- name: Check Ubuntu 20+ defaults are present in the first configuration file
ansible.builtin.assert:
that:
- "'Include /etc/ssh/sshd_config.d/*.conf' in config3.content | b64decode"
- "'KbdInteractiveAuthentication no' in config.content | b64decode"
- "'UsePAM yes' in config.content | b64decode"
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution_major_version'] | int >= 20
- name: Check Ubuntu 22+ defaults are present in the first configuration file
ansible.builtin.assert:
that:
- "'KbdInteractiveAuthentication no' in config.content | b64decode"
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution_major_version'] | int >= 22

View file

@ -153,16 +153,22 @@
- ansible_facts['os_family'] == 'Debian'
- ansible_facts['distribution_major_version'] | int < 22
- name: Check Ubuntu 22 defaults are present in the first configuration file
- name: Check Ubuntu 20+ defaults are present in the first configuration file
ansible.builtin.assert:
that:
- "'Include /etc/ssh/sshd_config.d/*.conf' in config3.content | b64decode"
- "'KbdInteractiveAuthentication no' in config.content | b64decode"
- "'UsePAM yes' in config.content | b64decode"
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution_major_version']|int >= 22
- ansible_facts['distribution_major_version'] | int >= 20
- name: Check Ubuntu 22+ defaults are present in the first configuration file
ansible.builtin.assert:
that:
- "'KbdInteractiveAuthentication no' in config.content | b64decode"
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution_major_version'] | int >= 22
- name: Check content of second configuration file
ansible.builtin.assert:

View file

@ -17,7 +17,7 @@
state: absent
when:
- (ansible_facts['os_family'] == 'RedHat' and ansible_facts['distribution_major_version'] | int > 8) or
(ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_major_version'] | int >= 22)
(ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_major_version'] | int >= 20)
- name: Create a new configuration in drop-in directory
ansible.builtin.include_role:
@ -29,12 +29,12 @@
Ciphers: aes192-ctr
when:
- (ansible_facts['os_family'] == 'RedHat' and ansible_facts['distribution_major_version'] | int > 8) or
(ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_major_version'] | int >= 22)
(ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_major_version'] | int >= 20)
- name: Verify the options are correctly set
when:
- (ansible_facts['os_family'] == 'RedHat' and ansible_facts['distribution_major_version'] | int > 8) or
(ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_major_version'] | int >= 22)
(ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_major_version'] | int >= 20)
tags: tests::verify
block:
- name: Flush handlers

View file

@ -45,6 +45,8 @@
- name: Read the instantiated service file
ansible.builtin.slurp:
src: /etc/systemd/system/ssh@.service
when:
- ansible_facts['distribution_major_version'] | int < 12
register: service_inst
- name: Read the main socket file
@ -57,6 +59,12 @@
that:
- '"RuntimeDirectory=sshd" in service.content | b64decode'
- '"RuntimeDirectoryMode=0755" in service.content | b64decode'
- name: Check the runtime directory is in instantiated service files
when:
- ansible_facts['distribution_major_version'] | int < 12
ansible.builtin.assert:
that:
- '"RuntimeDirectory=sshd" in service_inst.content | b64decode'
- '"RuntimeDirectoryMode=0755" in service_inst.content | b64decode'

View file

@ -0,0 +1,159 @@
---
- name: Test systemd services and sockets files can be installed
hosts: all
vars:
__sshd_test_backup_files:
- /etc/ssh/sshd_config
- /etc/ssh/sshd_config.d/00-ansible_system_role.conf
- /etc/systemd/system/sshd.service
- /etc/systemd/system/sshd@.service
- /etc/systemd/system/sshd.socket
- /etc/systemd/system/ssh.service
- /etc/systemd/system/ssh@.service
- /etc/systemd/system/ssh.socket
__sshd_test_service_name: sshd
__sshd_service_list: []
__sshd_service_inst_list: []
__sshd_socket_list: []
tasks:
- name: Fix the service name on Debian
ansible.builtin.set_fact:
__sshd_test_service_name: ssh
when:
- ansible_facts['os_family'] == "Debian"
- name: Backup configuration files
ansible.builtin.include_tasks: tasks/backup.yml
- name: Configure sshd with default options and install service files
ansible.builtin.include_role:
name: ansible-sshd
vars:
sshd_install_service: true
- name: Read the service files and verify they are reasonable
tags: tests::verify
when:
- ansible_facts['service_mgr'] == 'systemd'
block:
- name: Read the distribution service file
ansible.builtin.slurp:
src: "/lib/systemd/system/{{ __sshd_test_service_name }}.service"
register: service_old
- name: Read the distribution socket file
ansible.builtin.slurp:
src: "/lib/systemd/system/{{ __sshd_test_service_name }}.socket"
register: socket_old
- name: Read the created service file
ansible.builtin.slurp:
src: "/etc/systemd/system/{{ __sshd_test_service_name }}.service"
register: service
- name: Read the created socket file
ansible.builtin.slurp:
src: "/etc/systemd/system/{{ __sshd_test_service_name }}.socket"
register: socket
- name: Decode service file
ansible.builtin.set_fact:
service_old: "{{ service_old.content | b64decode }}"
# quite basic, but it should do the job
# * I do not think the ConditionPathExists is much useful so skipping on Ubuntu
# * I do not think the Description needs to match verbatim either
- name: Construct the options list from old service file
ansible.builtin.set_fact:
__sshd_service_list: "{{ __sshd_service_list + [item] }}"
when:
- not item.startswith("#")
- not item.startswith("ConditionPathExists=")
- not item.startswith("Description=")
loop:
"{{ service_old.splitlines() }}"
- name: Test options in sshd.service are kept
ansible.builtin.assert:
that:
- "item in service.content | b64decode"
loop:
"{{ __sshd_service_list }}"
- name: Verify the ExecStart line contains the configuration file
ansible.builtin.assert:
that:
- "' -f /etc/ssh/' in service.content | b64decode"
- name: Decode socket file
ansible.builtin.set_fact:
socket_old: "{{ socket_old.content | b64decode }}"
# quite basic, but it should do the job
# * I do not think the ConditionPathExists is much useful so skipping on Ubuntu
# * Before= does not make any sense in combination with Conflicts=
# * I do not think the Description needs to match verbatim either
- name: Construct the options list from old socket file
ansible.builtin.set_fact:
__sshd_socket_list: "{{ __sshd_socket_list + [item] }}"
when:
- not item.startswith("#")
- not item.startswith("ConditionPathExists=")
- not item.startswith("Before=")
- not item.startswith("Description=")
loop:
"{{ socket_old.splitlines() }}"
- name: Test options in sshd.socket are kept
ansible.builtin.assert:
that:
- "item in socket.content | b64decode"
loop:
"{{ __sshd_socket_list }}"
- name: Read the instantiated service file and verify they are reasonable
tags: tests::verify
when:
- ansible_facts['service_mgr'] == 'systemd'
- ansible_facts['distribution'] != "Debian" or ansible_facts['distribution_major_version'] | int < 12
block:
- name: Read the distribution instantiated service file
ansible.builtin.slurp:
src: "/lib/systemd/system/{{ __sshd_test_service_name }}@.service"
register: service_inst_old
- name: Read the created instantiated service file
ansible.builtin.slurp:
src: "/etc/systemd/system/{{ __sshd_test_service_name }}@.service"
register: service_inst
- name: Decode instantiated service file
ansible.builtin.set_fact:
service_inst_old: "{{ service_inst_old.content | b64decode }}"
# quite basic, but it should do the job
# * I do not think the Description needs to match verbatim either
- name: Construct the options list from old instantiated service file
ansible.builtin.set_fact:
__sshd_service_inst_list: "{{ __sshd_service_inst_list + [item] }}"
when:
- not item.startswith("#")
- not item.startswith("Description=")
loop:
"{{ service_inst_old.splitlines() }}"
- name: Test options in sshd@.service are kept
ansible.builtin.assert:
that:
- "item in service_inst.content | b64decode"
loop:
"{{ __sshd_service_inst_list }}"
- name: Verify the ExecStart line contains the configuration file
ansible.builtin.assert:
that:
- "' -f /etc/ssh/' in service_inst.content | b64decode"
- name: "Restore configuration files"
ansible.builtin.include_tasks: tasks/restore.yml

View file

@ -13,3 +13,8 @@ __sshd_defaults:
Subsystem: "sftp {{ __sshd_sftp_server }}"
__sshd_os_supported: true
__sshd_runtime_directory: sshd
__sshd_environment_file: /etc/default/ssh
__sshd_environment_variable: $SSHD_OPTS
__sshd_service_after: auditd.service
__sshd_service_alias: sshd

View file

@ -14,3 +14,8 @@ __sshd_defaults:
Subsystem: "sftp {{ __sshd_sftp_server }}"
__sshd_os_supported: true
__sshd_runtime_directory: sshd
__sshd_environment_file: /etc/default/ssh
__sshd_environment_variable: $SSHD_OPTS
__sshd_service_after: auditd.service
__sshd_service_alias: sshd

View file

@ -14,3 +14,9 @@ __sshd_defaults:
Subsystem: "sftp {{ __sshd_sftp_server }}"
__sshd_os_supported: true
__sshd_runtime_directory: sshd
__sshd_environment_file: /etc/default/ssh
__sshd_environment_variable: $SSHD_OPTS
__sshd_service_after: auditd.service
__sshd_service_alias: sshd
__sshd_socket_accept: false

View file

@ -23,3 +23,11 @@ __sshd_hostkeys_nofips:
__sshd_drop_in_dir_mode: '0700'
__sshd_main_config_file: /etc/ssh/sshd_config
__sshd_environment_file: /etc/sysconfig/sshd
__sshd_environment_variable: $OPTIONS
__sshd_service_after: sshd-keygen.target
__sshd_service_wants:
- sshd-keygen.target
- ssh-host-keys-migration.service
__sshd_service_restart_timeout: 42s

View file

@ -20,8 +20,15 @@ __sshd_verify_hostkeys_default:
- /etc/ssh/ssh_host_ed25519_key
__sshd_hostkeys_nofips:
- /etc/ssh/ssh_host_ed25519_key
__sshd_hostkey_group: ssh_keys
__sshd_hostkey_mode: "0640"
__sshd_drop_in_dir_mode: '0700'
__sshd_main_config_file: /etc/ssh/sshd_config
__sshd_environment_file: /etc/sysconfig/sshd
__sshd_environment_variable: $OPTIONS
__sshd_service_after: sshd-keygen.target
__sshd_service_wants: sshd-keygen.target
__sshd_service_restart_timeout: 42s

View file

@ -31,3 +31,9 @@ __sshd_hostkey_group: ssh_keys
__sshd_hostkey_mode: "0640"
__sshd_hostkeys_nofips:
- /etc/ssh/ssh_host_ed25519_key
__sshd_environment_file: /etc/sysconfig/sshd
__sshd_environment_variable: $OPTIONS
__sshd_service_after: sshd-keygen.service
__sshd_service_wants: sshd-keygen.service
__sshd_service_restart_timeout: 42s

View file

@ -33,3 +33,11 @@ __sshd_hostkey_group: ssh_keys
__sshd_hostkey_mode: "0640"
__sshd_hostkeys_nofips:
- /etc/ssh/ssh_host_ed25519_key
__sshd_environment_file:
- /etc/crypto-policies/back-ends/opensshserver.config
- /etc/sysconfig/sshd
__sshd_environment_variable: $OPTIONS $CRYPTO_POLICY
__sshd_service_after: sshd-keygen.target
__sshd_service_wants: sshd-keygen.target ssh-host-keys-migration.service
__sshd_service_restart_timeout: 42s

View file

@ -25,3 +25,9 @@ __sshd_hostkey_mode: "0640"
__sshd_drop_in_dir_mode: '0700'
__sshd_main_config_file: /etc/ssh/sshd_config
__sshd_environment_file: /etc/sysconfig/sshd
__sshd_environment_variable: $OPTIONS
__sshd_service_after: sshd-keygen.target
__sshd_service_wants: sshd-keygen.target
__sshd_service_restart_timeout: 42s

View file

@ -1,15 +1,31 @@
---
__sshd_os_supported: true
__sshd_service: ssh
__sshd_packages:
- openssh-server
- openssh-sftp-server
# Ubuntu 20.04 backported support for drop-in directory so we touch
# just the included file with highest priority by default
__sshd_config_file: /etc/ssh/sshd_config.d/00-ansible_system_role.conf
__sshd_config_mode: "0644"
# the defaults here represent the defaults shipped in the main sshd_config
__sshd_defaults:
Include: /etc/ssh/sshd_config.d/*.conf
ChallengeResponseAuthentication: false
UsePAM: true
X11Forwarding: true
PrintMotd: false
AcceptEnv: LANG LC_*
Subsystem: "sftp /usr/lib/openssh/sftp-server"
__sshd_os_supported: true
__sshd_runtime_directory: sshd
__sshd_drop_in_dir_mode: '0755'
__sshd_main_config_file: /etc/ssh/sshd_config
__sshd_environment_file: /etc/default/ssh
__sshd_environment_variable: $SSHD_OPTS
# the defaults here represent the defaults shipped in the main sshd_config
__sshd_service_after: auditd.service
__sshd_service_alias: sshd

View file

@ -21,3 +21,8 @@ __sshd_runtime_directory: sshd
__sshd_drop_in_dir_mode: '0755'
__sshd_main_config_file: /etc/ssh/sshd_config
__sshd_environment_file: /etc/default/ssh
__sshd_environment_variable: $SSHD_OPTS
__sshd_service_after: auditd.service
__sshd_service_alias: sshd

View file

@ -64,3 +64,25 @@ __sshd_verify_hostkeys_default: []
# This switch can control if the validate step is supported by the target OS.
# This is useful for very old OpenSSH or for tests that generate invalid configurations
__sshd_supports_validate: true
# The path to an environment file for the SSHD service
__sshd_environment_file: ~
# The variable name we are passing from the environment file as an argument to the sshd
__sshd_environment_variable: $OPTIONS
# The systemd targets that need to be up before starting the service.
# The `network.target` is included by default in the main sshd.service (not the instantiated one)
__sshd_service_after: ~
# The systemd service name alias
__sshd_service_alias: ~
# The systemd service wants directive
__sshd_service_wants: ~
# The systemd service RestartSec directive
__sshd_service_restart_timeout: ~
# The systemd socket file does not accept the connection
__sshd_socket_accept: true