Add support for managing selinux and firewall on RHEL

This commit is contained in:
Jakub Jelen 2022-12-13 17:55:13 +01:00 committed by Jakub Jelen
parent 788a3d8668
commit 04f056867c
9 changed files with 258 additions and 0 deletions

View file

@ -45,6 +45,21 @@ Tested on:
It will likely work on other flavours and more direct support via suitable It will likely work on other flavours and more direct support via suitable
[vars/](vars/) files is welcome. [vars/](vars/) files is welcome.
### Optional requirements
If you want to use advanced functionality of this role that can configure
firewall and selinux for you, which is mostly useful when custom port is used,
the role requires additional collections which are specified in
`meta/collection-requirements.yml`. These are not automatically installed.
You must install them like this:
```
ansible-galaxy install -vv -r meta/collection-requirements.yml
```
For more information, see `sshd_manage_firewall` and `sshd_manage_selinux`
options below. These roles are supported only on Red Hat based Linux.
Role variables Role variables
--------------- ---------------
@ -93,6 +108,25 @@ Using these variables, you can use your own custom templates. With the above
default templates, the name of the installed ssh service will be provided by default templates, the name of the installed ssh service will be provided by
the `sshd_service` variable. the `sshd_service` variable.
* `sshd_manage_firewall`
If set to *true*, the the SSH port(s) will be opened in firewall. Note, this
works only on Red Hat based OS. The default is *false*.
NOTE: `sshd_manage_firewall` is limited to *adding* ports. It cannot be used
for *removing* ports. If you want to remove ports, you will need to use the
firewall system role directly.
* `sshd_manage_selinux`
If set to *true*, the the selinux will be configured to allow sshd listening
on the given SSH port(s). Note, this works only on Red Hat based OS.
The default is *false*.
NOTE: `sshd_manage_selinux` is limited to *adding* policy. It cannot be used
for *removing* policy. If you want to remove ports, you will need to use the
selinux system role directly.
* `sshd` * `sshd`
A dict containing configuration. e.g. A dict containing configuration. e.g.

View file

@ -73,3 +73,11 @@ sshd_hostkey_mode: "{{ __sshd_hostkey_mode }}"
# instead of replacing the whole configuration file, just add a specified # instead of replacing the whole configuration file, just add a specified
# snippet # snippet
sshd_config_namespace: null sshd_config_namespace: null
# If this option is enabled, the role will configure firewall to open the ports
# defined in the configuration. This works only on Red Hat based systems.
sshd_manage_firewall: false
# If this option is enabled, the role will configure selinux to allow sshd to
# bind the ports defined in the configuration. This works only on Red Hat based systems.
sshd_manage_selinux: false

View file

@ -0,0 +1,3 @@
---
collections:
- name: fedora.linux_system_roles

26
tasks/find_ports.yml Normal file
View file

@ -0,0 +1,26 @@
---
- name: Find the port the ssh service is going to use
vars:
# This mimics the macro body_option() in sshd_config.j2
# The explicit to_json filter is needed for Python 2 compatibility
__sshd_ports_from_config_tmp: >-
{% if sshd_Port is defined %}
{{ sshd_Port | to_json }}
{% elif sshd['Port'] is defined %}
{{ sshd['Port'] | to_json }}
{% elif __sshd_defaults['Port'] is defined and not sshd_skip_defaults %}
{{ __sshd_defaults['Port'] | to_json }}
{% else %}
{{ [22] | to_json }}
{% endif %}
ansible.builtin.set_fact:
__sshd_ports_from_config: >-
{% if __sshd_ports_from_config_tmp | from_json is string or __sshd_ports_from_config_tmp | from_json is number %}
{{ [__sshd_ports_from_config_tmp | from_json] | to_json }}
{% else %}
{{ __sshd_ports_from_config_tmp }}
{% endif %}
when:
- sshd_manage_firewall | bool or sshd_manage_selinux | bool
- ansible_facts['os_family'] == 'RedHat'
- ansible_virtualization_type|default(None) not in __sshd_skip_virt_env

25
tasks/firewall.yml Normal file
View file

@ -0,0 +1,25 @@
---
- name: Ensure the ssh service or custom ports are opened in firewall
block:
- name: Enable the ssh service on default port
ansible.builtin.include_role:
name: fedora.linux_system_roles.firewall
vars:
firewall:
- service: ssh
state: enabled
when:
- __sshd_ports_from_config | from_json == [22]
- name: Enable the non-default port(s)
ansible.builtin.include_role:
name: fedora.linux_system_roles.firewall
vars:
firewall:
- port: "{{ sshd_item }}/tcp"
state: enabled
loop: "{{ __sshd_ports_from_config | from_json | d([]) }}"
loop_control:
loop_var: sshd_item # avoid conflicts with the firewall loops
when:
- __sshd_ports_from_config | from_json != [22]

View file

@ -121,6 +121,24 @@
when: when:
- __sshd_runtime_directory is not none - __sshd_runtime_directory is not none
- name: Find SSHD ports
ansible.builtin.include_tasks: find_ports.yml
- name: Configure firewall
ansible.builtin.include_tasks: firewall.yml
when:
- sshd_manage_firewall | bool
- ansible_facts['os_family'] == 'RedHat'
- ansible_facts['distribution_version'] is version('7', '>=')
- ansible_virtualization_type|default(None) not in __sshd_skip_virt_env
- name: Configure selinux
ansible.builtin.include_tasks: selinux.yml
when:
- sshd_manage_selinux | bool
- ansible_facts['os_family'] == 'RedHat'
- ansible_virtualization_type|default(None) not in __sshd_skip_virt_env
- name: Create the complete configuration file - name: Create the complete configuration file
ansible.builtin.include_tasks: install_config.yml ansible.builtin.include_tasks: install_config.yml
when: sshd_config_namespace is none when: sshd_config_namespace is none

16
tasks/selinux.yml Normal file
View file

@ -0,0 +1,16 @@
---
- name: Ensure the custom ports are configured in selinux
ansible.builtin.include_role:
name: fedora.linux_system_roles.selinux
vars:
selinux_ports:
- ports: "{{ sshd_item }}"
proto: tcp
setype: ssh_port_t
state: present
local: true
loop: "{{ __sshd_ports_from_config | from_json | d([]) }}"
loop_control:
loop_var: sshd_item # avoid conflicts with the selinux loops
when:
- __sshd_ports_from_config | from_json != [22]

View file

@ -1,3 +1,4 @@
--- ---
collections: collections:
- name: ansible.posix - name: ansible.posix
- name: fedora.linux_system_roles

View file

@ -0,0 +1,127 @@
---
- hosts: all
vars:
__sshd_test_backup_files:
- /etc/ssh/sshd_config
- /etc/ssh/sshd_config.d/00-ansible_system_role.conf
tasks:
- name: "Backup configuration files"
ansible.builtin.include_tasks: tasks/backup.yml
##########
# First test: default port
##########
- name: Configure the role on default port and let it handle firewall settings
ansible.builtin.include_role:
name: ansible-sshd
vars:
sshd_manage_selinux: true
sshd_manage_firewall: true
sshd:
Port: 22
- name: Verify the options are correctly set
block:
- name: Flush handlers
ansible.builtin.meta: flush_handlers
- name: Print current configuration file
ansible.builtin.slurp:
src: "{{ main_sshd_config }}"
register: config
- name: Check the options are in configuration file
ansible.builtin.assert:
that:
- "'Port 22' in config.content | b64decode"
tags: tests::verify
##########
# Second test: non-default port
##########
# is this going to break some tests running ansible through ssh?
- name: Configure the role on another port and let it handle firewall settings
ansible.builtin.include_role:
name: ansible-sshd
vars:
sshd_manage_firewall: true
sshd_manage_selinux: true
sshd:
Port: 222
- name: Verify the options are correctly set
block:
- name: Flush handlers
ansible.builtin.meta: flush_handlers
- name: Print current configuration file
ansible.builtin.slurp:
src: "{{ main_sshd_config }}"
register: config
- name: Check the options are in configuration file
ansible.builtin.assert:
that:
- "'Port 222' in config.content | b64decode"
tags: tests::verify
##########
# Third test: multiple ports
##########
- name: Configure the role on several ports and let it handle firewall settings
ansible.builtin.include_role:
name: ansible-sshd
vars:
sshd_manage_firewall: true
sshd_manage_selinux: true
sshd:
Port:
- 22
- 222
- name: Verify the options are correctly set
block:
- name: Flush handlers
ansible.builtin.meta: flush_handlers
- name: Print current configuration file
ansible.builtin.slurp:
src: "{{ main_sshd_config }}"
register: config
- name: Check the options are in configuration file
ansible.builtin.assert:
that:
- "'Port 222' in config.content | b64decode"
tags: tests::verify
##########
# Cleanup
##########
- name: "Restore configuration files"
ansible.builtin.include_tasks: tasks/restore.yml
- name: Remove the modification to the firewall rules
ansible.builtin.include_role:
name: fedora.linux_system_roles.firewall
vars:
firewall:
- port: "222/tcp"
state: disabled
when:
- ansible_facts['os_family'] == 'RedHat'
- ansible_virtualization_type|default(None) not in __sshd_skip_virt_env
- name: Remove the modification to the selinux policy
ansible.builtin.include_role:
name: fedora.linux_system_roles.firewall
vars:
selinux:
port: 222
proto: tcp
setype: ssh_port_t
state: absent
local: true
when:
- ansible_facts['os_family'] == 'RedHat'
- ansible_virtualization_type|default(None) not in __sshd_skip_virt_env