diff --git a/README.md b/README.md index 61ae5fc..1e6bb43 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,38 @@ -# ssh-hardening +ssh-hardening +========= + +SSH hardening, based on https://www.sshaudit.com & more + +Requirements +------------ + +- tbd + +Role Variables +-------------- + +- tbd + +Dependencies +------------ + +- None + +Example Playbook +---------------- + + - hosts: servers + roles: + - { role: ssh-hardening } + +License +------- + +AGPL3.0-or-later + +Author Information +------------------ + +- Sven Velt +- https://git.velt.biz/ -SSH hardening, based on https://www.sshaudit.com & more \ No newline at end of file diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..de5102d --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,19 @@ +--- +ssh_hardening_hostkeys: + - rsa + - ed25519 + +ssh_hardening_hostkeys_all: + - dsa + - ecdsa + - rsa + - ed25519 + +ssh_hardening_moduli: /etc/ssh/moduli +ssh_hardening_moduli_backup: /etc/ssh/moduli.not-hardened + +ssh_hardening_sshd_config: /etc/ssh/sshd_config + +ssh_hardening_service_name: FIXME + + diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..c487fa2 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: Restart SSH + service: + name: "{{ ssh_hardening[ssh_hardening_distri]|default('sshd') }}" + state: restarted + + diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..b6bfa62 --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,39 @@ +galaxy_info: + author: Sven Velt + description: SSH hardening + company: velt.biz + issue_tracker_url: https://git.velt.biz/Ansible/ssh-hardening/issues + license: AGPL-3.0-or-later + min_ansible_version: 2.1 + platforms: + - name: Debian + versions: + - stretch + - buster + - bullseye + - name: Ubuntu + versions: + - trusty + - xenial + - bionic + - focal + - groovy + - hirsute + - impish + - name: Fedora + versions: + - 33 + - 34 + - 35 + - name: EL + versions: + - 6 + - 7 + - 8 + + galaxy_tags: + - ssh + - security + +dependencies: [] + diff --git a/ssh-hardening.yml b/ssh-hardening.yml new file mode 100644 index 0000000..1c128ca --- /dev/null +++ b/ssh-hardening.yml @@ -0,0 +1,6 @@ +--- +- hosts: all + + roles: + - ssh-hardening + diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..153ca4a --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,81 @@ +--- +- name: Set some variables + set_fact: + ssh_hardening_backup_suffix: "42.{{ ansible_date_time.date }}@{{ ansible_date_time.time }}~" + ssh_hardening_distri: "{{ (ansible_distribution|lower).split(' ')[0] }}" + + +- name: Get SSH version number + shell: 'ssh -V 2>&1 | grep -o "OpenSSH_[0-9]\+\.[0-9]" | grep -o "[0-9]\+\.[0-9]"' + changed_when: False + register: ssh_hardening_version + + +- name: Read SSH version config file + include_vars: "ssh_{{ ssh_hardening_version.stdout_lines.0 }}.yml" + + +- name: Read distribution specific variables + include_vars: "{{ item }}" + with_first_found: + - "os_{{ ssh_hardening_distri }}.yml" + - "os_{{ ansible_distribution_family }}.yml" + - "os_{{ ansible_os_family }}.yml" + - "os_default.yml" + + +- name: Backup sshd_config + copy: + src: "{{ ssh_hardening_sshd_config }}" + dest: "{{ ssh_hardening_sshd_config }}.{{ ssh_hardening_backup_suffix }}" + remote_src: yes + owner: root + group: root + mode: 0600 + +#################### + +- name: "Hostkeys: Disable (EC)DSA" + lineinfile: + dest: "{{ ssh_hardening_sshd_config }}" + regexp: '(?i)\s*#*\s*hostkey.*{{ item }}_key' + state: absent + loop: + - dsa + - ecdsa + notify: Restart SSH + + +- name: "Hostkeys: Enable (RSA &) ED25519" + lineinfile: + dest: "{{ ssh_hardening_sshd_config }}" + regexp: '(?i)\s*#*\s*hostkey.*{{ item }}_key' + line: 'HostKey /etc/ssh/ssh_host_{{ item }}_key' + loop: '{{ ssh_hardening_hostkeys }}' + notify: Restart SSH + +#################### + +- name: "INCLUDE: Remove small Diffie-Hellman moduli" + include_tasks: moduli.yml + +#################### + +- name: "Check for crypto-policies" + stat: + path: /etc/crypto-policies/back-ends/opensshserver.config + register: ssh_hardening_use_policies + + +- debug: var=ssh_hardening_use_policies.stat.exists + + +- name: "The (RedHat) crypto policy way..." + include_tasks: restrictions_crypto_policy.yml + when: ssh_hardening_use_policies.stat.exists + + +- name: "The standard config way..." + include_tasks: restrictions_configfile.yml + when: not ssh_hardening_use_policies.stat.exists + diff --git a/tasks/moduli.yml b/tasks/moduli.yml new file mode 100644 index 0000000..8ec9d36 --- /dev/null +++ b/tasks/moduli.yml @@ -0,0 +1,19 @@ +--- +- name: 'Moduli: Check if "moduli.not-hardened" already exists' + stat: + path: "{{ ssh_hardening_moduli_backup }}" + register: ssh_hardening_moduli_backup_file + +- name: 'Moduli: Backup "moduli.not-hardened"' + shell: 'cp -a {{ ssh_hardening_moduli }} {{ ssh_hardening_moduli_backup }}' + when: not ssh_hardening_moduli_backup_file.stat.exists + +- name: 'Moduli: Check for small Diffie-Hellman moduli' + shell: "grep -c ' 1535 \\| 2047 ' /etc/ssh/moduli || true" + changed_when: False + register: ssh_hardening_moduli_small + +- name: 'Moduli: Remove small Diffie-Hellman moduli' + shell: "TMPF=$(mktemp) && awk '$5 >= 3071' /etc/ssh/moduli >${TMPF} && mv ${TMPF} /etc/ssh/moduli" + when: ssh_hardening_moduli_small.stdout|int > 0 + diff --git a/tasks/restrictions_configfile.yml b/tasks/restrictions_configfile.yml new file mode 100644 index 0000000..5a0c26c --- /dev/null +++ b/tasks/restrictions_configfile.yml @@ -0,0 +1,30 @@ +--- +- name: Check for Include directory + shell: "awk '/Include/ { print $2; }' /etc/ssh/sshd_config" + changed_when: False + register: ssh_hardening_includedir + +- debug: var=ssh_hardening_includedir + +- name: Write restrictions to include file + template: + src: 'sshd_config_hardening.j2' + dest: '{{ ssh_hardening_includedir.stdout_lines.0|dirname + "/ssh-hardening.conf" }}' + owner: root + group: root + mode: 0600 + backup: yes + when: ssh_hardening_includedir.stdout_lines|length > 0 + notify: Restart SSH + + +- name: Write restrictons block to sshd_config + blockinfile: + path: '{{ ssh_hardening_sshd_config }}' + block: '{{ lookup("template", "sshd_config_hardening.j2") }}' + insertbefore: '^# Logging' + marker: '# {mark} ANSIBLE ROLE ssh-hardening' + when: ssh_hardening_includedir.stdout_lines|length == 0 + notify: Restart SSH + + diff --git a/tasks/restrictions_crypto_policy.yml b/tasks/restrictions_crypto_policy.yml new file mode 100644 index 0000000..dcf3415 --- /dev/null +++ b/tasks/restrictions_crypto_policy.yml @@ -0,0 +1,22 @@ +--- +- name: Move original link + command: mv /etc/crypto-policies/back-ends/opensshserver.config /etc/crypto-policies/back-ends/opensshserver.config.not-hardened + args: + creates: /etc/crypto-policies/back-ends/opensshserver.config.not-hardened + when: ssh_hardening_use_policies.stat.islnk + + +- name: Write new crypto policies + template: + src: '{{ item }}' + dest: /etc/crypto-policies/back-ends/opensshserver.config + owner: root + group: root + mode: 0644 + backup: yes + with_first_found: + - 'crypto-policies/opensshserver_{{ ansible_distribution|lower }}.config.j2' + - 'crypto-policies/opensshserver_{{ ansible_os_family|lower }}.config.j2' + notify: Restart SSH + + diff --git a/templates/crypto-policies/opensshserver_fedora.config.j2 b/templates/crypto-policies/opensshserver_fedora.config.j2 new file mode 120000 index 0000000..8963391 --- /dev/null +++ b/templates/crypto-policies/opensshserver_fedora.config.j2 @@ -0,0 +1 @@ +../sshd_config_hardening.j2 \ No newline at end of file diff --git a/templates/crypto-policies/opensshserver_redhat.config.j2 b/templates/crypto-policies/opensshserver_redhat.config.j2 new file mode 100644 index 0000000..a7dbcfd --- /dev/null +++ b/templates/crypto-policies/opensshserver_redhat.config.j2 @@ -0,0 +1,2 @@ +CRYPTO_POLICY='-o{% for key in ssh_hardening_opts %}{{ key }}={{ ssh_hardening_opts[key]|join(",") }} {% endfor %}' + diff --git a/templates/sshd_config_hardening.j2 b/templates/sshd_config_hardening.j2 new file mode 100644 index 0000000..6e8a9b5 --- /dev/null +++ b/templates/sshd_config_hardening.j2 @@ -0,0 +1,2 @@ +{% for key in ssh_hardening_opts %}{{ key }} {{ ssh_hardening_opts[key]|join(",") }} +{% endfor %} diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..35ddcbe --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for ssh-hardening diff --git a/vars/os_default.yml b/vars/os_default.yml new file mode 100644 index 0000000..cd21505 --- /dev/null +++ b/vars/os_default.yml @@ -0,0 +1,2 @@ +--- + diff --git a/vars/os_devuan.yml b/vars/os_devuan.yml new file mode 100644 index 0000000..df732ba --- /dev/null +++ b/vars/os_devuan.yml @@ -0,0 +1,3 @@ +--- +ssh_hardening_service_name: ssh + diff --git a/vars/os_ubuntu-14.yml b/vars/os_ubuntu-14.yml new file mode 100644 index 0000000..676f17b --- /dev/null +++ b/vars/os_ubuntu-14.yml @@ -0,0 +1,4 @@ +--- +ssh_hardening_hostkeys: + - ed25519 + diff --git a/vars/ssh_6.6.yml b/vars/ssh_6.6.yml new file mode 100644 index 0000000..df63763 --- /dev/null +++ b/vars/ssh_6.6.yml @@ -0,0 +1,19 @@ +# 6.6: Ubuntu 14 +# 7.2: Ubuntu 16 + +ssh_hardening_opts: + KexAlgorithms: + - curve25519-sha256@libssh.org + - diffie-hellman-group-exchange-sha256 + Ciphers: + - chacha20-poly1305@openssh.com + - aes256-gcm@openssh.com + - aes128-gcm@openssh.com + - aes256-ctr + - aes192-ctr + - aes128-ctr + MACs: + - hmac-sha2-256-etm@openssh.com + - hmac-sha2-512-etm@openssh.com + - umac-128-etm@openssh.com + diff --git a/vars/ssh_7.2.yml b/vars/ssh_7.2.yml new file mode 120000 index 0000000..131cf4c --- /dev/null +++ b/vars/ssh_7.2.yml @@ -0,0 +1 @@ +ssh_6.6.yml \ No newline at end of file diff --git a/vars/ssh_7.4.yml b/vars/ssh_7.4.yml new file mode 100644 index 0000000..9a70c80 --- /dev/null +++ b/vars/ssh_7.4.yml @@ -0,0 +1,22 @@ +# 7.4: Debian 9 +# 7.4: RedHat/CentOS 7 + +ssh_hardening_opts: + KexAlgorithms: + - curve25519-sha256 + - curve25519-sha256@libssh.org + - diffie-hellman-group18-sha512 + - diffie-hellman-group16-sha512 + - diffie-hellman-group-exchange-sha256 + Ciphers: + - chacha20-poly1305@openssh.com + - aes256-gcm@openssh.com + - aes128-gcm@openssh.com + - aes256-ctr + - aes192-ctr + - aes128-ctr + MACs: + - hmac-sha2-256-etm@openssh.com + - hmac-sha2-512-etm@openssh.com + - umac-128-etm@openssh.com + diff --git a/vars/ssh_7.6.yml b/vars/ssh_7.6.yml new file mode 100644 index 0000000..a65e96c --- /dev/null +++ b/vars/ssh_7.6.yml @@ -0,0 +1,24 @@ +# 7.6: Ubuntu 18 + +ssh_hardening_opts: + KexAlgorithms: + - curve25519-sha256 + - curve25519-sha256@libssh.org + - diffie-hellman-group16-sha512 + - diffie-hellman-group18-sha512 + - diffie-hellman-group-exchange-sha256 + Ciphers: + - chacha20-poly1305@openssh.com + - aes256-gcm@openssh.com + - aes128-gcm@openssh.com + - aes256-ctr + - aes192-ctr + - aes128-ctr + MACs: + - hmac-sha2-256-etm@openssh.com + - hmac-sha2-512-etm@openssh.com + - umac-128-etm@openssh.com + HostKeyAlgorithms: + - ssh-ed25519 + - ssh-ed25519-cert-v01@openssh.com + diff --git a/vars/ssh_7.9.yml b/vars/ssh_7.9.yml new file mode 100644 index 0000000..69eedcf --- /dev/null +++ b/vars/ssh_7.9.yml @@ -0,0 +1,29 @@ +# 7.9: Debian 10 +# 8.0: RedHat/CentOS 8 + +ssh_hardening_opts: + KexAlgorithms: + - curve25519-sha256 + - curve25519-sha256@libssh.org + - diffie-hellman-group16-sha512 + - diffie-hellman-group18-sha512 + - diffie-hellman-group-exchange-sha256 + Ciphers: + - chacha20-poly1305@openssh.com + - aes256-gcm@openssh.com + - aes128-gcm@openssh.com + - aes256-ctr + - aes192-ctr + - aes128-ctr + MACs: + - hmac-sha2-256-etm@openssh.com + - hmac-sha2-512-etm@openssh.com + - umac-128-etm@openssh.com + HostKeyAlgorithms: + - ssh-ed25519 + - ssh-ed25519-cert-v01@openssh.com + - rsa-sha2-256 + - rsa-sha2-512 + - rsa-sha2-256-cert-v01@openssh.com + - rsa-sha2-512-cert-v01@openssh.com + diff --git a/vars/ssh_8.0.yml b/vars/ssh_8.0.yml new file mode 120000 index 0000000..cb8ec93 --- /dev/null +++ b/vars/ssh_8.0.yml @@ -0,0 +1 @@ +ssh_7.9.yml \ No newline at end of file diff --git a/vars/ssh_8.2.yml b/vars/ssh_8.2.yml new file mode 100644 index 0000000..dc6199a --- /dev/null +++ b/vars/ssh_8.2.yml @@ -0,0 +1,33 @@ +# 8.2: Ubuntu 10 +# 8.4: Debian 11 +# 8.6: Fedora 34 (no diff in *hardened policy* to 8.4) +# 8.7: Fedora 35 (no diff in *hardened policy* to 8.4) + +ssh_hardening_opts: + KexAlgorithms: + - curve25519-sha256 + - curve25519-sha256@libssh.org + - diffie-hellman-group16-sha512 + - diffie-hellman-group18-sha512 + - diffie-hellman-group-exchange-sha256 + Ciphers: + - chacha20-poly1305@openssh.com + - aes256-gcm@openssh.com + - aes128-gcm@openssh.com + - aes256-ctr + - aes192-ctr + - aes128-ctr + MACs: + - hmac-sha2-256-etm@openssh.com + - hmac-sha2-512-etm@openssh.com + - umac-128-etm@openssh.com + HostKeyAlgorithms: + - ssh-ed25519 + - ssh-ed25519-cert-v01@openssh.com + - sk-ssh-ed25519@openssh.com + - sk-ssh-ed25519-cert-v01@openssh.com + - rsa-sha2-256 + - rsa-sha2-512 + - rsa-sha2-256-cert-v01@openssh.com + - rsa-sha2-512-cert-v01@openssh.com + diff --git a/vars/ssh_8.3.yml b/vars/ssh_8.3.yml new file mode 120000 index 0000000..685a8e8 --- /dev/null +++ b/vars/ssh_8.3.yml @@ -0,0 +1 @@ +ssh_8.4.yml \ No newline at end of file diff --git a/vars/ssh_8.4.yml b/vars/ssh_8.4.yml new file mode 120000 index 0000000..8a29ec4 --- /dev/null +++ b/vars/ssh_8.4.yml @@ -0,0 +1 @@ +ssh_8.2.yml \ No newline at end of file diff --git a/vars/ssh_8.6.yml b/vars/ssh_8.6.yml new file mode 120000 index 0000000..8a29ec4 --- /dev/null +++ b/vars/ssh_8.6.yml @@ -0,0 +1 @@ +ssh_8.2.yml \ No newline at end of file diff --git a/vars/ssh_8.7.yml b/vars/ssh_8.7.yml new file mode 120000 index 0000000..8a29ec4 --- /dev/null +++ b/vars/ssh_8.7.yml @@ -0,0 +1 @@ +ssh_8.2.yml \ No newline at end of file