In the previous post, I covered how to get started with the sshd Red Hat Enterprise Linux (RHEL) System Role, which can help you automate and manage your SSH server configuration across your RHEL environment. This is important because having a properly configured and secured SSH server is a key component of securing a RHEL system.  

I intentionally made the first part of this series very simple by specifying the role variables directly in the playbook. This requires editing the playbook any time the variables need to be updated or changed. A better method would be to store the role variables in the inventory, which I will cover in this post.  

I also had the same SSH server configuration applied to all of my hosts in the previous post. However, in the real world, there are frequent exceptions to be made and servers with special requirements that need to deviate slightly from the usual configuration. 

Thus, in this post, I will also cover how to override settings for servers that need a slightly different configuration.  

Example environment overview

I’m using the same environment used in the first post, which includes an Red Hat Ansible control node, two RHEL 8 servers, and two RHEL 7 servers:  

Figure 1.

My desired SSH server configuration for these five servers is:

  • The /etc/ssh/sshd_config file should have the owner/group set to root/root, and the 0600 file permissions

  • The following options should be set in the sshd_config file:

    • X11Forwarding false

    • MaxAuthTries 4

    • ClientAliveInterval 300

    • LoginGraceTime 60

    • AllowTcpForwarding no

    • PermitRootLogin no

    • MaxStartups 10:30:60 

However, two of the systems have special requirements, and should deviate from that configuration with the following differences:

  • On rhel8-server1 the PermitRootLogin should be set to yes.

  • On rhel7-server2 the PermitRootLogin and X11Forwarding should both be set to yes.

Setting up the inventory with the role variables

In part 1, I created the inventory file at sshd_playbook/inventory/inventory.yml with the following contents:

all:
  hosts:
    rhel8-server1.example.com:
    rhel8-server2.example.com:
    rhel7-server1.example.com:
    rhel7-server2.example.com:
    controlnode.example.com:
      ansible_connection: local

No additional changes are needed to the inventory.yml file. However, to define the general configuration outside of the playbook and to implement the special requirements for the servers mentioned above, I will need to create two additional directories under the inventory directory: group_vars and host_vars.  

$ mkdir -p sshd_playbook/inventory/group_vars
$ mkdir -p sshd_playbook/inventory/host_vars

The group_vars directory can be used to set variables at the group level and, in this example, we’ll be setting variables for the all group. The host_vars directory can be used to set variables at the host level and we’ll use this functionality to override the settings rhel8-server1.example.com and rhel7-server2.example.com need to deviate on.  

I’ll create the sshd_playbook/inventory/group_vars/all.yml file to specify the role variables that should apply to all of the hosts with the following content:

sshd_config_owner: root
sshd_config_group: root
sshd_config_mode: 0600
sshd:
  X11Forwarding: false
  MaxAuthTries: 4
  ClientAliveInterval: 300
  LoginGraceTime: 60
  AllowTcpForwarding: no
  PermitRootLogin: no
  MaxStartups: 10:30:60    

Note that the sshd_config options we would like to be set are defined in the sshd dictionary variable. Simple variables named sshd_option (for example, sshd_PermitRootLogin) can also be used and these will override the configuration specified in the sshd dictionary variable.  

Due to this, we can easily specify the deviated configuration options the rhel8-server1.example.com and rhel7-server2.example.com should have by creating files for each server under the sshd_playbook/inventory/host_vars directory. In these files we will define the sshd_option variables each host should have to override the global configuration specified in the all.yml group_vars file.

There will be a file for rhel8-server1 named sshd_playbook/inventory/host_vars/rhel8-server1.example.com.yml which contains:

sshd_PermitRootLogin: yes

In addition, there will be a file for rhel7-server2 named sshd_playbook/inventory/host_vars/rhel7-server2.example.com.yml which contains:

sshd_X11Forwarding: yes
sshd_PermitRootLogin: yes

With this configuration, the rhel8-server1 and rhel7-server2 hosts will use the configuration specified in the all.yml file, with the exception of the options specified in each of their host_vars files, which will override just those settings for each host.  

Creating and running the playbook

In part 1, the playbook contained the role variables. Now that these have been moved to the inventory, the role variables can be removed from the playbook, making it very short and simple.  

The updated playbook at sshd_playbook/sshd.yml file will now only contain:

- hosts: all
  become: true
  roles:
    - role: redhat.rhel_system_roles.sshd

At this point I’m ready to run the playbook, which will apply our desired SSH server configuration on the five hosts.  

I’ll change directory into the sshd_playbook directory and use the ansible-playbook command to run the playbook, specifying the playbook name and inventory file that should be used:

$ cd sshd_playbook
$ ansible-playbook sshd.yml  -i inventory/inventory.yml

The playbook runs and at the end a summary is shown:

imageI verified by checking the contents of the sshd_config file on a couple of the hosts:

$ ssh rhel7-server1.example.com sudo cat /etc/ssh/sshd_config
# Ansible managed
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS
AllowTcpForwarding no
AuthorizedKeysFile .ssh/authorized_keys
ChallengeResponseAuthentication no
ClientAliveInterval 300
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
LoginGraceTime 60
MaxAuthTries 4
MaxStartups 10:30:60
PasswordAuthentication yes
PermitRootLogin no
Subsystem sftp /usr/libexec/openssh/sftp-server
SyslogFacility AUTHPRIV
UsePAM yes
UsePrivilegeSeparation sandbox
X11Forwarding no

$ ssh rhel7-server2.example.com sudo cat /etc/ssh/sshd_config
# Ansible managed
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS
AllowTcpForwarding no
AuthorizedKeysFile .ssh/authorized_keys
ChallengeResponseAuthentication no
ClientAliveInterval 300
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
LoginGraceTime 60
MaxAuthTries 4
MaxStartups 10:30:60
PasswordAuthentication yes
PermitRootLogin yes
Subsystem sftp /usr/libexec/openssh/sftp-server
SyslogFacility AUTHPRIV
UsePAM yes
UsePrivilegeSeparation sandbox
X11Forwarding yes

Note that there is a comment at the top of each of the files mentioning it is managed by Ansible. Also note that on rhel7-server2 the deviated configuration settings we specified for PermitRootLogin and X11Forwarding in the server’s host_vars file were properly set.  

Conclusion

While the first part detailed a simple example of using the sshd RHEL System Role, this post covered how to move the role variables out of the playbook and included a more real world scenario where some servers need a slightly different configuration applied. 

Review the list of available RHEL System Roles and start managing your RHEL servers in a more efficient, consistent and automated manner today. 

Take RHEL System Roles for a quick test drive in our hands-on interactive lab environment that walks you through a common RHEL System Roles use case. 


About the author

Brian Smith is a Product Manager at Red Hat focused on RHEL automation and management.  He has been at Red Hat since 2018, previously working with Public Sector customers as a Technical Account Manager (TAM).  

Read full bio