DEBOPS.SSHD(5)

NAME
debops.sshd - Manage OpenSSH server using Ansible

SYNOPSIS

debops run service/sshd [--limit group,host,...] [--diff] [--check] [--tags tag1,tag2,...] [--skip-tags tag1,tag2,...] [<ansible-playbook options>] ...

DESCRIPTION

OpenSSH <https://www.openssh.com/> is a secure replacement for telnet and other remote control programs. It allows you to connect to remote hosts over a encrypted communication channel and to perform a variety of tasks. It's also the primary communication channel used by Ansible.

GETTING STARTED

  Changes from Debian defaults

The debops.sshd role will configure the OpenSSH server to lookup the client hostnames in DNS by setting the UseDNS option to yes (the Debian and upstream default is no). This allows use of the DNS hostnames and domains in the authorized keys files and PAM access control rules.

The role will divert the original /etc/pam.d/sshd configuration file and generate a new one, with PAM access control enabled and using the separate /etc/security/access-sshd.conf configuration file. The ACL rules are defined in the sshd__pam_access__dependent_rules variable and are managed by the debops.pam_access Ansible role.

To disable the custom access control configuration, set the sshd__pam_deploy_state variable to absent. The PAM access control file will still be generated, but it will not be used by the sshd service.

  Global root access

By default the debops.pam_access configuration restricts access to the root account to hosts on the same DNS domain, for security. This might cause unintended lockouts if your Ansible Controller host is on a completely different domain than the remote host.

To disable the restricted access and allow connections to the root account from anywhere on the network, you can set in your Ansible inventory, for example in ansible/inventory/group_vars/debops_all_hosts/pam_access.yml file:

pam_access__rules:
  - name: 'sshd'
    state: 'append'
    options:
      - name: 'allow-root'
        origins: 'ALL'

Then, you need to apply the changes to the configuration using the "context" of the debops.sshd role, for example by executing the command:

$ debops run service/sshd -l <host> --tags role::pam_access --diff

This command will apply the PAM access configuration defined by the debops.sshd role with modifications from the inventory; they won't be applied in other contexts of the debops.pam_access role is used in and shouldn't affect other access lists.

You could also add subnets, domains or other origins instead of allowing access from any host; refer to the pam_access__rules for more details.

  Access to the root account via password

The debops.sshd role checks if the /root/.ssh/authorized_keys file is present on the host, using Ansible local facts defined by the debops.root_account role. If the file is present, we assume that the sysadmin SSH keys are on the host, and password-based access to the root account is disabled by setting the PermitRootLogin option to prohibit-password and the PasswordAuthentication option to no.

If the SSH authorized keys file is not present, the host is assumed to not be fully provisioned yet. The PermitRootLogin option as well as the PasswordAuthentication option will be set to yes to permit access to the root account via SSH. Note that the default PAM access policy set in the sshd__pam_access__dependent_rules variable still applies and access to the root account will be limited to hosts on the same DNS domain.

Alternatively, if the sysadmin accounts are configured using the debops.system_users Ansible role, access to the root account via password and password authentication will also be disabled.

  Useful variables

This is a list of role variables which you most likely want to define in Ansible inventory to customize OpenSSH server:

sshd__whitelist / sshd__group_whitelist / sshd__host_whitelist
    Lists which contain IP addresses or CIDR subnets that are permitted to connect to OpenSSH without restrictions or firewall limits. Adding entries here will not impose additional restrictions, unlike using sshd__*_allow lists.

sshd__known_hosts / sshd__group_known_hosts / sshd__host_known_hosts
    Here you can add lists of FQDN hostnames which should be added to systemwide /etc/ssh/ssh_known_hosts file. For example, setting:

    sshd__known_hosts: [ 'github.com' ]

    will add GitHub SSH fingerprint and allow you to clone git repositories over SSH with proper host authentication, without the need to ignore host fingerprints.

sshd__authorized_keys_lookup
    Boolean. If True, role will enable lookup of SSH public keys in external authentication databases, like LDAP. This might require additional configuration using sshd__ldap_* variables.

    LDAP key lookup depends on system-wide LDAP configuration in /etc/ldap/ldap.conf, which can be performed (at the moment) using debops.ldap role. It will be configured when LDAP-POSIX integration is enabled on a given host.

  Example inventory

debops.sshd role is part of the common.yml DebOps playbook. It will be executed automatically on every host managed by DebOps.

  Example playbook

If you are using this role without DebOps, here's an example Ansible playbook that uses the debops.sshd role:

---
- name: Manage OpenSSH Server
  collections: [ 'debops.debops', 'debops.roles01',
                 'debops.roles02', 'debops.roles03' ]
  hosts: [ 'debops_all_hosts', 'debops_service_sshd' ]
  become: True
  environment: '{{ inventory__environment | d({})
                   | combine(inventory__group_environment | d({}))
                   | combine(inventory__host_environment  | d({})) }}'
  pre_tasks:
    - name: Prepare sshd environment
      ansible.builtin.import_role:
        name: 'sshd'
        tasks_from: 'main_env'
      tags: [ 'role::sshd', 'role::ldap' ]
  roles:
    - role: ferm
      tags: [ 'role::ferm', 'skip::ferm' ]
      ferm__dependent_rules:
        - '{{ sshd__ferm__dependent_rules }}'
    - role: tcpwrappers
      tags: [ 'role::tcpwrappers', 'skip::tcpwrappers' ]
      tcpwrappers_dependent_allow:
        - '{{ sshd__tcpwrappers__dependent_allow }}'
    - role: python
      tags: [ 'role::python', 'skip::python', 'role::ldap' ]
      python__dependent_packages3:
        - '{{ ldap__python__dependent_packages3 }}'
      python__dependent_packages2:
        - '{{ ldap__python__dependent_packages2 }}'
    - role: ldap
      tags: [ 'role::ldap', 'skip::ldap' ]
      ldap__dependent_tasks:
        - '{{ sshd__ldap__dependent_tasks }}'
    - role: pam_access
      tags: [ 'role::pam_access', 'skip::pam_access' ]
      pam_access__dependent_rules:
        - '{{ sshd__pam_access__dependent_rules }}'
    - role: sudo
      tags: [ 'role::sudo', 'skip::sudo' ]
      sudo__dependent_sudoers:
        - '{{ sshd__sudo__dependent_sudoers }}'
    - role: sshd
      tags: [ 'role::sshd', 'skip::sshd' ]

  Ansible tags

You can use Ansible --tags or --skip-tags parameters to limit what tasks are performed during Ansible run. This can be used after a host was first configured to speed up playbook execution, when you are sure that most of the configuration is already in the desired state.

Available role tags:

role::sshd
    Main role tag, should be used in the playbook to execute all of the role tasks as well as role dependencies.

role::sshd:config
    Execute tasks related to sshd configuration file.

role::sshd:known_hosts
    Scan specified host fingerprints and add them to system-wide known_hosts.

DEFAULT VARIABLE DETAILS

Some of the debops.sshd default variables have more extensive configuration than simple strings or lists, here you can find documentation and examples for them.

  sshd__configuration

The sshd__*_configuration default variables define the contents of the /etc/ssh/sshd_config configuration file. The role uses Universal Configuration to generate this configuration file. You can read the sshd_config(5) <https://manpages.debian.org/sshd_config(5)> manual page for details about SSH daemon configuration options.

  Examples

Enable debug logs in the SSH service to diagnose connection issues:

sshd__configuration:
  - LogLevel: 'DEBUG'

Define a list of UNIX groups allowed to access the SSH service:

sshd__configuration:
  - AllowGroups: [ 'admins', 'sshusers', 'sftponly' ]

This is an example of the "simple syntax" definition. Note that the PAM configuration used in the role is much more flexible than the AllowGroups option.

Configure SSH service to accept plaintext password authentication on the root UNIX account (you shouldn't do this in production environments!):

sshd__configuration:
  - name: 'PermitRootLogin'
    value: True

Configure SSH daemon to listen on additional TCP ports for network connections:

sshd__configuration:
  - name: 'Port'  # default
    state: 'present'
  - name: 'Port_2222'
    option: 'Port'
    value: 2222
    copy_id_from: 'Port'
  - name: 'Port_3322'
    option: 'Port'
    value: 3322
    copy_id_from: 'Port'

Alternatively, you can specify a list of TCP ports using the sshd__ports variable which will generate the corresponding configuration options.

You can check the sshd__original_configuration variable to see an example configuration that comes with the openssh-server Debian package. The changes added by DebOps are set in the sshd__default_configuration variable which is also a good source of examples.

  Syntax

Each configuration entry in the list is a YAML dictionary. The simple form of the configuration uses the dictionary keys as the parameter names, and dictionary values as the parameter values. Remember that the parameter names need to be specified in the exact case they are used in the documentation (e.g. PermitRootLogin, ClientAliveCountMax), otherwise they will be duplicated in the generated configuration file. It's best to use a single YAML dictionary per configuration option.

If the YAML dictionary contains the name key, the configuration switches to the complex definition mode, with configuration options defined by specific parameters:

name
    Required. Specify the name of the SSH daemon configuration parameter. The case is important and should be the same as specified in the configuration file or the sshd_config(5) <https://manpages.debian.org/sshd_config(5)> manual page, otherwise the configuration entries will be duplicated.

    Multiple configuration entries with the same name parameter are merged together in order of appearance. This can be used to modify parameters conditionally.

option
    Optional. If a given sshd_config(5) <https://manpages.debian.org/sshd_config(5)> configuration option needs to be specified more than once (for example Port, ListenAddress or Match), you need to use unique name parameters in each case. The option parameter can be used to specify the actual option name in such case.

raw Optional. String or YAML text block with sshd_config(5) <https://manpages.debian.org/sshd_config(5)> configuration options which will be included as-is in the generated configuration file. When the raw parameter is specified, name and value options are not included so they need to be present explicitly. Jinja statements can be used to further augment the generated output.

value
    Required. The value of a given configuration option. It can be a string, number, True/False boolean or a YAML list. List entries will be joined with the space character

    Lists can use simple strings and numbers, or can be defined using YAML dictionary with specific parameters:

name
    The value of a given list element (string, number).

weight
    Positive or negative number, by default 0. Weight can be used to affect the order of list elements, with negative weight resulting in a given element being moved "up" towards the start of the list, and positive number resulting in an element being moved "down" towards the end of the list.

state
    Optional. If not specified or present, a given configuration parameter will be present in the generated configuration file. If absent, a given parameter will be removed from the configuration file. If comment, the parameter will be present but commented out.

    If the state is init, the parameter will be "primed" in the configuration pipeline, but it will be commented out in the generated configuration file. Any subsequent configuration entry with the same name will switch the state to present - this is used to define the default parameters in the role which can be changed via the Ansible inventory.

    If the state is ignore, a given configuration entry will not be evaluated during role execution. This can be used to activate configuration entries conditionally.

config
    Optional. String or YAML text block with sshd_config(5) <https://manpages.debian.org/sshd_config(5)> configuration options specified as-is (boolean variables should be specified as yes or no, not the Jinja/YAML equivalents). The contents will be included indented after a given configuration option. This parameter is meant to be used with the Match configuration option to specify Match options used by the SSH service.

comment
    Optional. String or YAML text block with additional comments for a specific configuration option.

separator
    Optional, boolean. If specified and True, a given configuration option will be separated by an empty line from previous options. Used for cosmetic purposes to better match the original /etc/ssh/sshd_config configuration file.

weight
    Optional. Positive or negative number, by default 0. Weight can be used to affect the order of configuration options (important, first option sets the configuration in the SSH daemon), with negative weight resulting in a given configuration being moved "up" in the generated config file, and positive number resulting in an option being moved "down" in the generated config file.

copy_id_from
    Optional. Name of a configuration entry (name parameter), which should be used as an "anchor" for a given entry. This parameter can be used to group related configuration options together - for example multiple Port options (see the examples section above). The weight parameter can be used to fine-tune the order of options in the generated configuration file.

AUTHOR
Maciej Delmanowski, Robin Schneider, David Härdeman

COPYRIGHT
2014-2024, Maciej Delmanowski, Nick Janetakis, Robin Schneider and others