debops.sshd default variables
Sections
OpenSSH packages
- sshd__base_packages
List of base packages that should be installed for OpenSSH support.
sshd__base_packages: [ 'openssh-server', 'openssh-client' ]
- sshd__recommended_packages
List of recommended packages that should be installed with OpenSSH.
sshd__recommended_packages: '{{ ["openssh-blacklist", "openssh-blacklist-extra"]
if (ansible_distribution_release in
["trusty", "xenial"]) else [] }}'
- sshd__optional_packages
List of optional packages that should be installed with OpenSSH.
sshd__optional_packages: [ 'molly-guard' ]
- sshd__ldap_packages
List of packages related to LDAP support required by OpenSSH.
sshd__ldap_packages: '{{ ["ldap-utils"]
if (sshd__authorized_keys_lookup | bool and
("ldap" in sshd__authorized_keys_lookup_type))
else [] }}'
- sshd__packages
List of additional packages to install.
sshd__packages: []
- sshd__version
The version of the currently installed sshd daemon, exposed using Ansible local facts.
sshd__version: '{{ ansible_local.sshd.version | d("0.0") }}'
Host whitelists and allow lists
- sshd__whitelist
List of IP addresses or CIDR subnets which should be allowed to connect to SSH without any restrictions. This list does not disallow connections from other hosts. This is a global list.
sshd__whitelist: []
- sshd__group_whitelist
List of IP addresses or CIDR subnets which should be allowed to connect to SSH without any restrictions. This list does not disallow connections from other hosts. This is a group-based list.
sshd__group_whitelist: []
- sshd__host_whitelist
List of IP addresses or CIDR subnets which should be allowed to connect to SSH without any restrictions. This list does not disallow connections from other hosts. This is a host-based list.
sshd__host_whitelist: []
- sshd__allow
List of IP addresses or CIDR subnets that should be allowed to access SSH service. If it's set, access from hosts and networks not specified here is denied in TCP Wrappers and limited in iptables. This is a global list.
sshd__allow: []
- sshd__group_allow
List of IP addresses or CIDR subnets that should be allowed to access SSH service. If it's set, access from hosts and networks not specified here is denied in TCP Wrappers and limited in iptables. This is a group list.
sshd__group_allow: []
- sshd__host_allow
List of IP addresses or CIDR subnets that should be allowed to access SSH service. If it's set, access from hosts and networks not specified here is denied in TCP Wrappers and limited in iptables. This is a host list.
sshd__host_allow: []
TCP Wrappers configuration
- sshd__tcpwrappers_default
If list of allowed hosts is not specified, this value will be set in TCP
Wrappers for sshd
service. By default any host is allowed to connect.
sshd__tcpwrappers_default: 'ALL'
Firewall (ferm) configuration
- sshd__ferm_weight
Specify the "weight" of the sshd
firewall rules. The more weight they
have, the later in the firewall they will be defined. If you change the
default weight, you will need to remove the old rules manually from the remote
host.
sshd__ferm_weight: '30'
- sshd__ferm_limit
Enable or disable limited SSH access from all hosts in ip(6)tables. Recent new connections are filtered and when too many new connections are created in specified time window, the host is added to the recent blocklist.
sshd__ferm_limit: True
- sshd__ferm_limit_seconds
Length of the time window used by firewall to catch new offenders, by default 5 minutes.
sshd__ferm_limit_seconds: '{{ (60 * 5) }}'
- sshd__ferm_limit_hits
How many new connections to allow in specified time window.
sshd__ferm_limit_hits: '8'
- sshd__ferm_limit_chain
Name of the iptables chain used for filtering SSH connections.
sshd__ferm_limit_chain: 'filter-ssh'
- sshd__ferm_limit_target
Specify what happens with packets filtered by the firewall that are above the
specified limit. Either REJECT
, DROP
or name of iptables chain
where packets will be sent through.
sshd__ferm_limit_target: 'REJECT'
- sshd__ferm_ports
List of TCP ports to open in the firewall for SSH connections. You can use
port numbers or service names from /etc/services
.
If you use socket activation, remember to define firewall ports separately.
sshd__ferm_ports: '{{ sshd__ports
if ((ansible_local.sshd.socket_activation | d("disabled")) == "disabled")
else ["22"] }}'
- sshd__ferm_interface
List of interfaces to open in the firewall for SSH connections.
Consider settings this to {{ [ ansible_default_ipv4.interface ] }}
for example if the host has multiple network interfaces like when it is
running a VPN.
sshd__ferm_interface: []
OpenSSH server configuration
- sshd__ports
List of ports which sshd
will listen on. Make sure that the 22
TCP
port is included if you want to change this list. When systemd
socket activation is enabled, you can define here listen streams using the
systemd.socket(5) configuration syntax.
With socket activation enabled, you might need to open access to the
additional TCP ports in the firewall separately, using the
sshd__ferm_ports
variable.
sshd__ports: [ '22' ]
- sshd__host_keys
List of SSH host keys that should be enabled, in order of preference.
sshd__host_keys: [ 'ed25519', 'rsa', 'ecdsa' ]
- sshd__trusted_user_ca_keys
List of CA public keys used to assemble a TrustedUserCAKeys file.
sshd__trusted_user_ca_keys: []
- sshd__trusted_user_ca_keys_file
File name which will contain CA trusted keys
sshd__trusted_user_ca_keys_file: '/etc/ssh/trusted-user-ca-keys.pem'
- sshd__scan_for_host_certs
Detect, and add, any host certificates found in /etc/ssh/ that match ssh_host_*_key-cert.pub, as long as the key type matches what's in sshd__host_keys
sshd__scan_for_host_certs: False
SSH daemon configuration file
These variables define the contents of the /etc/ssh/sshd_config
configuration file. See sshd__configuration for more details.
- sshd__original_configuration
List of SSH daemon configuration options defined by default by the Debian
package on installation. You can find the original configuration in the
/usr/share/openssh/sshd_config
file.
sshd__original_configuration:
- name: 'Include_sshd_config.d'
option: 'Include'
value: '/etc/ssh/sshd_config.d/*.conf'
state: '{{ "absent" if ansible_distribution_release in ["stretch", "buster"] else "present" }}'
- name: 'Port'
value: 22
state: 'init'
separator: True
- name: 'AddressFamily'
value: 'any'
state: 'init'
- name: 'ListenAddress_ipv4'
option: 'ListenAddress'
value: '0.0.0.0'
state: 'init'
- name: 'ListenAddress_ipv6'
option: 'ListenAddress'
value: '::'
state: 'init'
# Define host keys in one entry, so that it can be easily regenerated later
- name: 'HostKey'
raw: |
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
state: 'init'
separator: True
- name: 'RekeyLimit'
comment: 'Ciphers and keyring'
value: 'default none'
state: 'init'
- name: 'SyslogFacility'
comment: 'Logging'
value: 'AUTH'
state: 'init'
- name: 'LogLevel'
value: 'INFO'
state: 'init'
- name: 'LoginGraceTime'
comment: 'Authentication'
value: '2m'
state: 'init'
- name: 'PermitRootLogin'
value: 'prohibit-password'
state: 'init'
- name: 'StrictModes'
value: True
state: 'init'
- name: 'MaxAuthTries'
value: 6
state: 'init'
- name: 'MaxSessions'
value: 10
state: 'init'
- name: 'PubkeyAuthentication'
value: True
state: 'init'
separator: True
- name: 'AuthorizedKeysFile'
comment: 'Expect .ssh/authorized_keys2 to be disregarded by default in future.'
value:
- '.ssh/authorized_keys'
- '.ssh/authorized_keys2'
state: 'init'
- name: 'AuthorizedPrincipalsFile'
value: 'none'
state: 'init'
separator: True
- name: 'AuthorizedKeysCommand'
value: 'none'
state: 'init'
separator: True
- name: 'AuthorizedKeysCommandUser'
value: 'nobody'
state: 'init'
- name: 'HostbasedAuthentication'
comment: 'For this to work you will also need host keys in /etc/ssh/ssh_known_hosts'
value: False
state: 'init'
- name: 'IgnoreUserKnownHosts'
comment: |
Change to yes if you don't trust ~/.ssh/known_hosts for
HostbasedAuthentication
value: False
state: 'init'
- name: 'IgnoreRhosts'
comment: "Don't read the user's ~/.rhosts and ~/.shosts files"
value: True
state: 'init'
- name: 'PasswordAuthentication'
comment: 'To disable tunneled clear text passwords, change to no here!'
value: True
state: 'init'
- name: 'ChallengeResponseAuthentication'
comment: |
Change to yes to enable challenge-response passwords (beware issues with
some PAM modules and threads). From openssh-server version 8.7, this option
is deprecated and is an alias of KbdInteractiveAuthentication.
value: False
state: 'init'
- name: 'KbdInteractiveAuthentication'
comment: |
Change to yes to enable challenge-response passwords (beware issues with
some PAM modules and threads)
value: False
state: 'init'
- name: 'PermitEmptyPasswords'
value: False
state: 'init'
- name: 'KerberosAuthentication'
comment: 'Kerberos options'
value: False
state: 'init'
- name: 'KerberosOrLocalPasswd'
value: True
state: 'init'
- name: 'KerberosTicketCleanup'
value: True
state: 'init'
- name: 'KerberosGetAFSToken'
value: False
state: 'init'
- name: 'GSSAPIAuthentication'
comment: 'GSSAPI options'
value: False
state: 'init'
- name: 'GSSAPICleanupCredentials'
value: True
state: 'init'
- name: 'GSSAPIStrictAcceptorCheck'
value: True
state: 'init'
- name: 'GSSAPIKeyExchange'
value: False
state: 'init'
- name: 'UsePAM'
comment: |
Set this to 'yes' to enable PAM authentication, account processing,
and session processing. If this is enabled, PAM authentication will
be allowed through the ChallengeResponseAuthentication and
PasswordAuthentication. Depending on your PAM configuration,
PAM authentication via ChallengeResponseAuthentication may bypass
the setting of "PermitRootLogin without-password".
If you just want the PAM account and session checks to run without
PAM authentication, then enable this but set PasswordAuthentication
and ChallengeResponseAuthentication to 'no'.
value: True
- name: 'AllowAgentForwarding'
value: True
state: 'init'
separator: True
- name: 'AllowTcpForwarding'
value: True
state: 'init'
- name: 'GatewayPorts'
value: False
state: 'init'
- name: 'X11Forwarding'
value: True
- name: 'X11DisplayOffset'
value: 10
state: 'init'
- name: 'X11UseLocalhost'
value: True
state: 'init'
- name: 'PermitTTY'
value: True
state: 'init'
- name: 'PrintMotd'
value: False
- name: 'PrintLastLog'
value: True
state: 'init'
- name: 'TCPKeepAlive'
value: True
state: 'init'
- name: 'PermitUserEnvironment'
value: False
state: 'init'
- name: 'Compression'
value: 'delayed'
state: 'init'
- name: 'ClientAliveInterval'
value: 0
state: 'init'
- name: 'ClientAliveCountMax'
value: 3
state: 'init'
- name: 'UseDNS'
value: False
state: 'init'
- name: 'PidFile'
value: '/var/run/sshd.pid'
state: 'init'
- name: 'MaxStartups'
value: '10:30:100'
state: 'init'
- name: 'PermitTunnel'
value: False
state: 'init'
- name: 'ChrootDirectory'
value: 'none'
state: 'init'
- name: 'VersionAddendum'
value: 'none'
state: 'init'
- name: 'Banner'
comment: 'no default banner path'
value: 'none'
state: 'init'
- name: 'AcceptEnv'
comment: 'Allow client to pass locale environment variables'
value:
- 'LANG'
- 'LC_*'
- name: 'Subsystem_sftp'
option: 'Subsystem'
comment: 'override default of no subsystems'
value: 'sftp /usr/lib/openssh/sftp-server'
- name: 'Match_user_anoncvs'
option: 'Match'
comment: 'Example of overriding settings on a per-user basis'
value:
- 'User anoncvs'
config: |
X11Forwarding no
AllowTcpForwarding no
PermitTTY no
ForceCommand cvs server
state: 'comment'
- sshd__default_configuration
List of SSH daemon configuration options defined by default by the debops.sshd Ansible role.
sshd__default_configuration:
# Generate a set of "Port" configuration options based on the default list of
# ports to listen on. We cannot use a list as the value, because each port
# needs to be specified in its own "Port" entry.
- name: 'Port'
raw: |
{% for port in sshd__ports %}
{{ 'Port {}'.format(port) }}
{% endfor %}
state: '{{ "ignore"
if (sshd__ports | length == 1 and
sshd__ports | first | string == "22")
else ("ignore"
if ((ansible_local.sshd.socket_activation | d("disabled")) == "enabled")
else "present") }}'
# Generate a set of "HostKey" configuration options based on the list of
# "allowed" host keys. We cannot use a list as the value, because each host
# key needs to be specified in its own "HostKey" entry.
- name: 'HostKey'
raw: |
{% for hostkey in sshd__host_keys %}
{% if ('ssh_host_' + hostkey + '_key') in sshd__register_host_keys.stdout_lines %}
{{ 'HostKey /etc/ssh/ssh_host_{}_key'.format(hostkey) }}
{% endif %}
{% endfor %}
state: 'present'
# Generate a list of host certificates detected on the host.
- name: 'HostCertificate'
raw: |
{% if (sshd__register_host_certs is defined and
"stdout_lines" in sshd__register_host_certs) %}
{% for cert in sshd__register_host_certs.stdout_lines %}
{% set sshd__key_matching_cert = cert | regex_replace('-cert', '') %}
{% set sshd__key_matching_host_keys = sshd__key_matching_cert | regex_replace('ssh_host_(\w+)_key', '\\1') %}
{% if sshd__key_matching_cert in sshd__register_host_keys.stdout_lines and sshd__key_matching_host_keys in sshd__host_keys %}
{{ 'HostCertificate /etc/ssh/{}.pub'.format(cert) }}
{% endif %}
{% endfor %}
{% endif %}
state: '{{ "present"
if (sshd__register_host_certs is defined and
"stdout_lines" in sshd__register_host_certs)
else "ignore" }}'
copy_id_from: 'HostKey'
# Add support for authorized keys managed by the
# "debops.authorized_keys" Ansible role.
- name: 'AuthorizedKeysFile'
value:
- name: '/etc/ssh/authorized_keys/%u'
weight: -100
state: 'present'
# If trusted CA keys are defined in the Ansible inventory, include them in
# the configuration file as well.
- name: 'TrustedUserCAKeys'
value: '{{ sshd__trusted_user_ca_keys_file }}'
state: '{{ "present"
if (sshd__trusted_user_ca_keys | d() | length > 0)
else "ignore" }}'
copy_id_from: 'AuthorizedKeysFile'
# Default "Match" conditional block which defines configuration for SFTPonly
# accounts. The 'stfponly' UNIX system group is created by the
# 'debops.system_groups' Ansible role.
- name: 'Match_group_sftponly'
option: 'Match'
comment: 'Support for strict SFTP UNIX accounts'
value: 'Group sftponly'
config: |
AuthorizedKeysFile /etc/ssh/authorized_keys/%u
ChrootDirectory %h
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PermitTunnel no
ForceCommand internal-sftp
state: 'present'
separator: True
# Specify if access to "root" UNIX account should be granted. By default, if
# the 'debops.root_account' role has been applied on the host, the "root"
# UNIX account can be accessed only using SSH public keys, since the role is
# expected to set them up. Otherwise, access via password is allowed.
# Alternatively, configured sysadmin accounts via the 'debops.system_users'
# role disable password-based access.
- name: 'PermitRootLogin'
value: '{{ "prohibit-password"
if (ansible_local.root_account.ssh_authorized_keys | d() | bool or
ansible_local.system_users.configured | d() | bool)
else True }}'
state: 'present'
# Enable or disable password authentication. This option will be enabled if
# the 'root' account does not have SSH authorized keys present, to allow the
# 'root' account to login to the host. Alternatively, sysadmin access
# configured by the 'debops.system_users' role disables password
# authentication.
- name: 'PasswordAuthentication'
value: '{{ False
if (ansible_local.root_account.ssh_authorized_keys | d() | bool or
ansible_local.system_users.configured | d() | bool)
else True }}'
state: 'present'
# Disable ChallengeResponseAuthentication by default. Otherwise, setting
# PasswordAuthentication to False is not enough to force authentication
# by public key. From openssh-server 8.7, ChallengeResponseAuthentication
# is deprecated and is an alias for KbdInteractiveAuthentication.
- name: 'ChallengeResponseAuthentication'
value: False
state: 'present'
# Disable KbdInteractiveAuthentication (new name of the deprecated
# ChallengeResponseAuthentication) by default. Otherwise, setting
# PasswordAuthentication to False is not enough to force authentication
# by public key.
- name: 'KbdInteractiveAuthentication'
value: False
state: 'present'
# Enable support for client hostname lookups by the SSH service. This is
# required if access control based on hostnames is used in the SSH server
# configuration, authorized keys or PAM access rules.
- name: 'UseDNS'
value: True
state: 'present'
- name: 'Ciphers'
comment: 'List of ciphers which are allowed for connections'
raw: |
{% set sshd__tpl_ciphers_max_version = sshd__ciphers_map.keys() | select('version_compare', sshd__version, '<=') | max %}
{% set sshd__tpl_ciphers = sshd__ciphers_map[sshd__tpl_ciphers_max_version] %}
{% set sshd__tpl_ciphers = (sshd__tpl_ciphers + sshd__ciphers_additional) | unique %}
{% if sshd__tpl_ciphers and sshd__paranoid | bool %}
{{ 'Ciphers {}'.format(([sshd__tpl_ciphers | first] + sshd__ciphers_additional) | unique | join(",")) }}
{% elif sshd__tpl_ciphers %}
{{ 'Ciphers {}'.format(sshd__tpl_ciphers | join(",")) }}
{% endif %}
state: 'present'
copy_id_from: 'RekeyLimit'
- name: 'KexAlgorithms'
comment: 'List of allowed key exchange algorithms'
raw: |
{% set sshd__tpl_kex_algorithms_max_version = sshd__kex_algorithms_map.keys() | select('version_compare', sshd__version, '<=') | max %}
{% set sshd__tpl_kex_algorithms = sshd__kex_algorithms_map[sshd__tpl_kex_algorithms_max_version] %}
{% set sshd__tpl_kex_algorithms = (sshd__tpl_kex_algorithms + sshd__kex_algorithms_additional) | unique %}
{% if sshd__tpl_kex_algorithms and sshd__paranoid | bool %}
{{ 'KexAlgorithms {}'.format(([sshd__tpl_kex_algorithms | first] + sshd__kex_algorithms_additional) | unique | join(",")) }}
{% elif sshd__tpl_kex_algorithms %}
{{ 'KexAlgorithms {}'.format(sshd__tpl_kex_algorithms | join(",")) }}
{% endif %}
state: 'present'
copy_id_from: 'RekeyLimit'
- name: 'MACs'
comment: 'List of allowed Message Authentication Code algorithms'
raw: |
{% set sshd__tpl_macs_max_version = sshd__macs_map.keys() | select('version_compare', sshd__version, '<=') | max %}
{% set sshd__tpl_macs = sshd__macs_map[sshd__tpl_macs_max_version] %}
{% set sshd__tpl_macs = (sshd__tpl_macs + sshd__macs_additional) | unique %}
{% if sshd__tpl_macs and sshd__paranoid | bool %}
{{ 'MACs {}'.format(([sshd__tpl_macs | first] + sshd__macs_additional) | unique | join(",")) }}
{% elif sshd__tpl_macs %}
{{ 'MACs {}'.format(sshd__tpl_macs | join(",")) }}
{% endif %}
state: 'present'
copy_id_from: 'RekeyLimit'
# Privilege Separation is default for sshd v7.5+
- name: 'UsePrivilegeSeparation'
comment: 'Privilege Separation is turned on for security'
value: 'sandbox'
state: '{{ "present" if (sshd__version is version("7.5", "<")) else "ignore" }}'
copy_id_from: 'RekeyLimit'
- name: 'KeyRegenerationInterval'
comment: 'Lifetime and size of ephemeral version 1 server key'
value: 3600
state: '{{ "present" if (sshd__version is version("7.4", "<")) else "ignore" }}'
copy_id_from: 'RekeyLimit'
- name: 'ServerKeyBits'
value: 1024
state: '{{ "present" if (sshd__version is version("7.4", "<")) else "ignore" }}'
copy_id_from: 'RekeyLimit'
- name: 'AuthorizedKeysCommand'
value: '/etc/ssh/authorized_keys_lookup'
state: '{{ "present"
if (sshd__authorized_keys_lookup | bool and
sshd__version is version("6.2", ">="))
else "ignore" }}'
- name: 'AuthorizedKeysCommandUser'
value: '{{ sshd__authorized_keys_lookup_user }}'
state: '{{ "present"
if (sshd__authorized_keys_lookup | bool and
sshd__version is version("6.2", ">="))
else "ignore" }}'
- sshd__configuration
List of SSH daemon configuration options which should be defined on all hosts in the Ansible inventory.
sshd__configuration: []
- sshd__group_configuration
List of SSH daemon configuration options which should be defined on hosts in a specific Ansible inventory group.
sshd__group_configuration: []
- sshd__host_configuration
List of SSH daemon configuration options which should be defined on specific hosts in the Ansible inventory.
sshd__host_configuration: []
- sshd__combined_configuration
Variable which combines all of the SSH daemon configuration lists and is used in role tasks and templates.
sshd__combined_configuration: '{{ sshd__original_configuration
+ sshd__default_configuration
+ sshd__configuration
+ sshd__group_configuration
+ sshd__host_configuration }}'
System-wide host fingerprints
- sshd__known_hosts
List of FQDN hostnames that should be scanned to add host fingerprints to the system-wide known hosts file (global).
sshd__known_hosts: []
- sshd__group_known_hosts
List of FQDN hostnames that should be scanned to add host fingerprints to the system-wide known hosts file (host group).
sshd__group_known_hosts: []
- sshd__host_known_hosts
List of FQDN hostnames that should be scanned to add host fingerprints to the system-wide known hosts file (host).
sshd__host_known_hosts: []
- sshd__known_hosts_file
System-wide file where host fingerprints are stored.
sshd__known_hosts_file: '/etc/ssh/ssh_known_hosts'
- sshd__known_hosts_command
Command used to scan host fingerprints into system-wide known hosts file.
sshd__known_hosts_command: 'ssh-keyscan -H -T 10'
Encryption parameters
- sshd__ciphers_map
Dict with list of ciphers which should be used by the sshd
server,
depending on available version, ordered from strongest to weakest. Newer version
supersedes older version.
sshd__ciphers_map:
# Source: https://wiki.mozilla.org/Security/Guidelines/OpenSSH
'6.5': [ 'chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com',
'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr',
'aes128-ctr' ]
# Source: https://xivilization.net/~marek/blog/2015/01/12/secure-secure-shell-on-debian-wheezy/
'6.0': [ 'aes256-ctr', 'aes192-ctr', 'aes128-ctr' ]
- sshd__ciphers_additional
List of additional key exchange algorithms which should be used by the
sshd
server, depending on available version, depending on available
version, ordered from stronger to weaker. Newer version supersedes older
version.
sshd__ciphers_additional: []
- sshd__kex_algorithms_map
Dict with list of key exchange algorithms which should be used by the
sshd
server, depending on available version, ordered from strongest to
oldest. Newer version supersedes older version.
sshd__kex_algorithms_map:
# Source: https://wiki.mozilla.org/Security/Guidelines/OpenSSH
'6.5': [ 'curve25519-sha256@libssh.org', 'ecdh-sha2-nistp521',
'ecdh-sha2-nistp384', 'ecdh-sha2-nistp256',
'diffie-hellman-group-exchange-sha256' ]
# Source: https://xivilization.net/~marek/blog/2015/01/12/secure-secure-shell-on-debian-wheezy/
'6.0': [ 'diffie-hellman-group-exchange-sha256' ]
- sshd__kex_algorithms_additional
List of additional key exchange algorithms which should be used by the
sshd
server, depending on available version, depending on available
version, ordered from stronger to weaker. Newer version supersedes older
version.
sshd__kex_algorithms_additional: []
- sshd__macs_map
Dict with list of message authentication code algorithms which should be used
by the sshd
server, depending on available version, ordered from stronger
to weaker. Newer version supersedes older version.
sshd__macs_map:
# Source: https://wiki.mozilla.org/Security/Guidelines/OpenSSH
'6.5': [ 'hmac-sha2-512-etm@openssh.com', 'hmac-sha2-256-etm@openssh.com',
'umac-128-etm@openssh.com', 'hmac-sha2-512', 'hmac-sha2-256',
'umac-128@openssh.com' ]
# Source: https://xivilization.net/~marek/blog/2015/01/12/secure-secure-shell-on-debian-wheezy/
'6.0': [ 'hmac-sha2-512', 'hmac-sha2-256', 'hmac-ripemd160' ]
- sshd__macs_additional
List of additional message authentication code algorithms to support
by the sshd
server, depending on available version, ordered from stronger
to weaker. Newer version supersedes older version.
sshd__macs_additional: []
- sshd__moduli_minimum
Specify minimum size of Diffie-Hellman parameters available to the SSH
server. Parameters smaller than the given amount will be removed from the
/etc/ssh/moduli
file.
sshd__moduli_minimum: '2048'
- sshd__paranoid
If set to True, only the first item (which is considered the strongest method
available) from the lists sshd__ciphers_map
,
sshd__kex_algorithms_map
and sshd__macs_map
will be configured for
sshd
. Use this with care as it will deny access to anyone not able to use
the first cryptographic method.
See https://github.com/debops/ansible-sshd/issues/20
sshd__paranoid: False
LDAP lookup configuration
- sshd__ldap_enabled
Enable or disable integration with the LDAP directory. The integration is enabled automatically when the debops.ldap environment is configured on the host.
sshd__ldap_enabled: '{{ ansible_local.ldap.enabled
if (ansible_local | d() and ansible_local.ldap | d() and
ansible_local.ldap.enabled is defined)
else False }}'
- sshd__ldap_base_dn
The base LDAP Distinguished Name used for SSH public key lookups.
sshd__ldap_base_dn: '{{ ansible_local.ldap.base_dn | d([]) }}'
- sshd__ldap_device_dn
The Distinguished Name of the device LDAP object that represents current
host. It will be used as a base DN for the sshd
LDAP account object.
sshd__ldap_device_dn: '{{ ansible_local.ldap.device_dn | d([]) }}'
- sshd__ldap_device_object_classes
List of additional LDAP Object Classes to add to the device LDAP object.
sshd__ldap_device_object_classes: [ 'ldapPublicKey' ]
- sshd__ldap_device_attributes
YAML dictionary with additional LDAP attributes which will be added to the device LDAP object.
sshd__ldap_device_attributes:
sshPublicKey: '{{ sshd__env_register_host_public_keys.stdout_lines }}'
- sshd__ldap_self_rdn
The Relative Distinguished Name of the sshd
LDAP account object.
sshd__ldap_self_rdn: 'uid={{ sshd__authorized_keys_lookup_user }}'
- sshd__ldap_self_object_classes
The LDAP Object Classes used to create the sshd
LDAP account object.
sshd__ldap_self_object_classes: [ 'account', 'simpleSecurityObject' ]
- sshd__ldap_self_attributes
YAML dictionary that contains the attributes of the sshd
LDAP account
object.
sshd__ldap_self_attributes:
uid: '{{ sshd__ldap_self_rdn.split("=")[1] }}'
userPassword: '{{ sshd__ldap_bindpw }}'
host: '{{ [ansible_fqdn, ansible_hostname] | unique }}'
description: 'Account used by the "sshd" service to access the LDAP directory'
- sshd__ldap_binddn
The Distinguished Name of the sshd
LDAP account object used to
authenticate to the LDAP directory, defined as a string.
sshd__ldap_binddn: '{{ ([sshd__ldap_self_rdn] + sshd__ldap_device_dn) | join(",") }}'
- sshd__ldap_bindpw
The password defined in the sshd
LDAP account object used to authenticate
to the LDAP directory.
sshd__ldap_bindpw: '{{ (lookup("password", secret + "/ldap/credentials/"
+ sshd__ldap_binddn | to_uuid + ".password length=32"))
if sshd__ldap_enabled | bool
else "" }}'
- sshd__ldap_filter
Active ldapsearch
filter used to select correct account while looking up
the SSH public key.
sshd__ldap_filter: '{{ sshd__ldap_filter_map["service+host"] }}'
- sshd__ldap_posix_urns
List of LDAP search filters which are derived from URN-like patterns defined for a given host in the debops.ldap role. See Host-based access control for more details.
sshd__ldap_posix_urns: '{{ ansible_local.ldap.urn_patterns | d([])
| map("regex_replace", "^(.*)$", "(host=posix:urn:\1)")
| list }}'
- sshd__ldap_filter_map
Dict with set of available LDAP filters that can be used to lookup the SSH public key.
sshd__ldap_filter_map:
# User account needs 'authorizedService' attribute
'service': '(&
(objectClass=posixAccount)
(uid=$username)
(|
(authorizedService=all)
(authorizedService=$service)
(authorizedService=shell)
)
)'
# User account needs 'host' attribute
'host': '(&
(objectClass=posixAccount)
(uid=$username)
(|
(host=posix:all)
(host=posix:$fqdn)
(host=posix:\2a.$domain)
{{ sshd__ldap_posix_urns | join(" ") }}
)
)'
# User account needs both 'authorizedService' and 'host' attributes.
'service+host': '(&
(objectClass=posixAccount)
(uid=$username)
(|
(authorizedService=all)
(authorizedService=$service)
(authorizedService=shell)
)
(|
(host=posix:all)
(host=posix:$fqdn)
(host=posix:\2a.$domain)
{{ sshd__ldap_posix_urns | join(" ") }}
)
)'
PAM configuration
- sshd__pam_deploy_state
Enable or disable support for custom PAM configuration for the sshd
service. Set to absent
to remove the customizations.
sshd__pam_deploy_state: 'present'
- sshd__pam_access_file
Specify the absolute path of the PAM access file to use for the sshd
service.
sshd__pam_access_file: '{{ "/etc/security/access-sshd.conf"
if ("sshd" in ansible_local.pam_access.rules | d([]))
else "/etc/security/access.conf" }}'
Configuration of other services
- sshd__ferm__dependent_rules:
Configuration for iptables firewall managed by ferm.
sshd__ferm__dependent_rules:
- type: 'accept'
dport: '{{ sshd__ferm_ports }}'
interface: '{{ sshd__ferm_interface }}'
weight: '0'
weight_class: 'sshd-chain'
name: 'sshd_jump-filter-ssh'
target: '{{ sshd__ferm_limit_chain }}'
rule_state: '{{ "present" if sshd__ferm_limit | bool else "absent" }}'
comment: 'Create a separate "iptables" chain for SSH rules'
- chain: '{{ sshd__ferm_limit_chain if (sshd__ferm_limit | bool) else "INPUT" }}'
type: 'accept'
dport: '{{ sshd__ferm_ports }}'
saddr: '{{ sshd__whitelist + sshd__group_whitelist + sshd__host_whitelist }}'
interface: '{{ [] if (sshd__ferm_limit | bool) else sshd__ferm_interface }}'
weight: '1'
weight_class: 'sshd-chain'
name: 'sshd_whitelist'
subchain: False
accept_any: False
comment: 'Accept any hosts in the whitelist unconditionally'
- chain: '{{ sshd__ferm_limit_chain if sshd__ferm_limit | bool else "INPUT" }}'
type: 'accept'
dport: '{{ sshd__ferm_ports }}'
saddr: '{{ sshd__allow + sshd__group_allow + sshd__host_allow }}'
interface: '{{ [] if (sshd__ferm_limit | bool) else sshd__ferm_interface }}'
weight: '2'
weight_class: 'sshd-chain'
name: 'sshd_allow'
subchain: False
accept_any: '{{ False if sshd__ferm_limit | bool else True }}'
comment: |
Accept any hosts in the allow list. If there are any hosts specified,
block connections from other hosts using TCP Wrappers.
- chain: '{{ sshd__ferm_limit_chain }}'
type: 'recent'
weight: '3'
weight_class: 'sshd-chain'
name: 'sshd_block-ssh'
dport: '{{ sshd__ferm_ports }}'
state: [ 'NEW' ]
subchain: False
recent_name: 'ssh-new'
recent_update: True
recent_seconds: '{{ sshd__ferm_limit_seconds }}'
recent_hitcount: '{{ sshd__ferm_limit_hits }}'
recent_target: 'REJECT'
rule_state: '{{ "present" if sshd__ferm_limit | bool else "absent" }}'
comment: |
Block new SSH connections that have been marked as recent if they make
too many new connection attempts.
- chain: '{{ sshd__ferm_limit_chain }}'
type: 'recent'
weight: '4'
weight_class: 'sshd-chain'
name: 'sshd_mark-ssh'
dport: '{{ sshd__ferm_ports }}'
state: [ 'NEW' ]
subchain: False
recent_set_name: 'ssh-new'
recent_log: False
rule_state: '{{ "present" if sshd__ferm_limit | bool else "absent" }}'
comment: 'Mark new connections to the SSH service for recent tracking'
- chain: '{{ sshd__ferm_limit_chain }}'
type: 'accept'
weight: '5'
weight_class: 'sshd-chain'
role: 'sshd'
role_weight: '60'
name: 'sshd_accept-ssh'
dport: '{{ sshd__ferm_ports }}'
rule_state: '{{ "present" if sshd__ferm_limit | bool else "absent" }}'
comment: 'Accept connections to the SSH service'
- sshd__tcpwrappers__dependent_allow
Configure TCP wrappers to allow access to the sshd
daemon.
sshd__tcpwrappers__dependent_allow:
- daemon: 'sshd'
client: '{{ sshd__whitelist + sshd__group_whitelist + sshd__host_whitelist }}'
accept_any: '{{ False if (sshd__allow + sshd__group_allow + sshd__host_allow) else True }}'
weight: '25'
filename: 'sshd_dependent_whitelist'
comment: 'Whitelist of hosts allowed to connect to ssh'
- daemon: 'sshd'
client: '{{ sshd__allow + sshd__group_allow + sshd__host_allow }}'
default: '{{ sshd__tcpwrappers_default }}'
accept_any: '{{ True if (sshd__whitelist + sshd__group_whitelist + sshd__host_whitelist) else False }}'
weight: '30'
filename: 'sshd_dependent_allow'
comment: 'List of hosts allowed to connect to ssh'
- sshd__ldap__dependent_tasks
Configuration for the debops.ldap Ansible role.
sshd__ldap__dependent_tasks:
- name: 'Add missing LDAP object classes to {{ sshd__ldap_device_dn | join(",") }}'
dn: '{{ sshd__ldap_device_dn }}'
attributes:
objectClass: '{{ sshd__ldap_device_object_classes }}'
state: '{{ "present"
if ((ansible_local.ldap.posix_enabled | d()) | bool and
sshd__ldap_device_dn | d())
else "ignore" }}'
- name: 'Update SSH host public keys in {{ sshd__ldap_device_dn | join(",") }}'
dn: '{{ sshd__ldap_device_dn }}'
attributes: '{{ sshd__ldap_device_attributes }}'
state: '{{ "exact"
if ((ansible_local.ldap.posix_enabled | d()) | bool and
sshd__ldap_device_dn | d())
else "ignore" }}'
- name: 'Create sshd account for {{ sshd__ldap_device_dn | join(",") }}'
dn: '{{ sshd__ldap_binddn }}'
objectClass: '{{ sshd__ldap_self_object_classes }}'
attributes: '{{ sshd__ldap_self_attributes }}'
no_log: '{{ debops__no_log | d(True) }}'
state: '{{ "present"
if (sshd__authorized_keys_lookup | bool and
("ldap" in sshd__authorized_keys_lookup_type))
else "ignore" }}'
- sshd__pam_access__dependent_rules
Configuration for the debops.pam_access Ansible role.
sshd__pam_access__dependent_rules:
- name: 'sshd'
options:
- name: 'allow-root-ansible-controllers'
comment: 'Grant access via SSH to root account from the Ansible Controller hosts'
permission: 'allow'
users: 'root'
origins: '{{ ansible_local.core.ansible_controllers | d([]) }}'
- name: 'allow-root'
comment: 'Grant access via SSH to root account on the same DNS domain'
permission: 'allow'
users: 'root'
origins: '.{{ ansible_domain }}'
- name: 'deny-root'
comment: 'Deny access to root account via SSH from anywhere else'
permission: 'deny'
users: 'root'
origins: 'ALL'
- name: 'allow-system-groups'
comment: |
Grant access via SSH to members of UNIX groups defined on this host
permission: 'allow'
groups: '{{ ansible_local.system_groups.access.sshd
| d(["admins", "sshusers", "sftponly"]) }}'
origins: 'ALL'
- name: 'allow-ldap-groups'
comment: |
Grant access via SSH to members of UNIX groups defined in LDAP
permission: 'allow'
groups: [ 'admins', 'sshusers', 'sftponly' ]
origins: 'ALL'
state: '{{ "present"
if (ansible_local.ldap.posix_enabled | d() | bool)
else "absent" }}'
- name: 'allow-domain'
comment: |
Grant access via SSH to users on the same DNS domain. The SSH server
needs to have UseDNS option enabled for this rule to work correctly.
permission: 'allow'
users: 'ALL'
origins: '.{{ ansible_domain }}'
- name: 'deny-all'
comment: 'Deny access via SSH by anyone from anywhere'
permission: 'deny'
users: 'ALL'
origins: 'ALL'
weight: 99999
- sshd__sudo__dependent_sudoers
Configuration for the debops.sudo Ansible role.
sshd__sudo__dependent_sudoers:
- name: 'sshd'
options:
- name: 'env_keep_ssh'
comment: |-
Allow molly-guard to detect that we connected via ssh even if
combined with sudo and tmux.
https://superuser.com/questions/666931/getting-molly-guard-to-work-with-sudo
It is not perfect, but works in most cases.
A case where it does not work is when `sudo tmux` is started via
local tty and then attached to it via ssh.
Or `sudo tmux` is executed via ssh and then attached locally.
But when a new shell is created in tmux, the environment variables in
that new shell are correct.
value: |
Defaults env_keep += SSH_CONNECTION