Ansible collection: foundata.sshd
Licensing
- Primary license: GPL-3.0-or-later
Miscellaneous
- Funding: Buy us a coffee
- Maintainer: foundata
- Status: Stable
The ansible collection foundata.sshd provides resources to manage and use the OpenSSH secure shell daemon (sshd), propably the world’s most used SSH (Secure SHell) server.
The following lists the README.md files of the most important included contents (e.g. roles). You might browse the source code repositories for a better overview.
Ansible role: foundata.sshd.run
The foundata.sshd.run Ansible role (part of the foundata.sshd Ansible collection). It provides automated configuration management of sshd, implementing security best practices by default across major platforms.
Features
Main features:
- Sane defaults:
- Modern cryptography.
- Extended Logging.
- Key-based authentication, no password based
rootlogin - Disabled Kerberos and GSSAPI (easy to re-enable but quite often not needed by default).
- See
__run_sshd_service_settings_defaultsin./vars/main.ymlfor a complete list.
- Default configuration result passes
ssh-auditwithout errors or warnings. - Access-preservation preflight (anti-lockout): the role aborts before restarting
sshdif the effective configuration would lock the Ansible connection account out (seerun_sshd_config_access_check). - Simple to use: extend or adapt / overwrite the role’s default configuration with a simple dictionary.
Example playbooks, using this role
Installation with automatic upgrade:
---
- name: "Initialize the foundata.sshd.run role"
hosts: localhost
gather_facts: false
tasks:
- name: "Trigger invocation of the foundata.sshd.run role"
ansible.builtin.include_role:
name: "foundata.sshd.run"
vars:
run_sshd_autoupgrade: true
Installation with custom configuration options (e.g., GatewayPorts: false) and an override of the role’s default setting for GSSAPIAuthentication and Port:
---
- name: "Initialize the foundata.sshd.run role"
hosts: localhost
gather_facts: false
tasks:
- name: "Trigger invocation of the foundata.sshd.run role"
ansible.builtin.include_role:
name: "foundata.sshd.run"
vars:
run_sshd_autoupgrade: true
run_sshd_service_settings:
Port: 2222
GatewayPorts: false
GSSAPIAuthentication: true
Uninstall (⚠️ Warning: This will remove SSH access from the target machines!)
---
- name: "Initialize the foundata.sshd.run role"
hosts: localhost
gather_facts: false
tasks:
- name: "Trigger invocation of the foundata.sshd.run role"
ansible.builtin.include_role:
name: "foundata.sshd.run"
vars:
run_sshd_state: "absent"
Supported tags
It might be useful and faster to only call parts of the role by using tags:
run_sshd_setup: Manage basic resources, such as packages or service users.run_sshd_config: Manage settings, such as adapting or creating configuration files.run_sshd_service: Manage services and daemons, such as running states and service boot configurations.
There are also tags that are generally not intended to be called directly but are included for completeness and to cover edge cases:
run_sshd_always,always: Tasks needed by the role itself for internal role setup and the Ansible environment.
Role variables
Main entry point for the foundata.sshd.run role
The following variables can be configured for this role:
| Variable | Type | Required | Default | Description (abstract) |
|---|---|---|---|---|
run_sshd_state | str | No | "present" | Determines whether the managed resources should be present or absent.present ensures that required components, such as software packages, are installed and configured.absent reverts changes as much as possible, such as … |
run_sshd_autoupgrade | bool | No | false | If set to true, all managed packages will be upgraded during each Ansible run (e.g., when the package provider detects a newer version than the currently installed one). |
run_sshd_service_state | str | No | "enabled" | Defines the status of the service(s).enabled: Service is running and will start automatically at boot.disabled: Service is stopped and will not start automatically at boot.running Service is running but will not start … |
run_sshd_service_settings | dict | No | {} | sshd service configuration values (additional ones or to overwrite defaults; see __run_sshd_service_settings_defaults in vars/main.yml for them).Note on the default KexAlgorithms: it lists both post-quantum hybrids … |
run_sshd_config_service_dropin_file_name | str | No | "00-managed.conf" | Filename of the drop-in configuration file to be placed in /etc/ssh/sshd_config.d. Defaults to 00-managed.conf. The 00- prefix ensures early loading and thus higher precedence over files with higher-numbered prefixes.If a non-default … |
run_sshd_config_access_check | bool | No | true | If set to true (the default), the role runs a best-effort access-preservation preflight (anti-lockout) before the SSH daemon gets restarted. It evaluates the effective configuration (via sshd -T, including resolved Match blocks) for the account … |
run_sshd_state
Determines whether the managed resources should be present or absent.
present ensures that required components, such as software packages, are
installed and configured.
absent reverts changes as much as possible, such as removing packages,
deleting created users, stopping services, restoring modified settings, …
- Type:
str - Required: No
- Default:
"present" - Choices:
present,absent
run_sshd_autoupgrade
If set to true, all managed packages will be upgraded during each Ansible
run (e.g., when the package provider detects a newer version than the
currently installed one).
- Type:
bool - Required: No
- Default:
false
run_sshd_service_state
Defines the status of the service(s).
enabled: Service is running and will start automatically at boot.
disabled: Service is stopped and will not start automatically at boot.
running Service is running but will not start automatically at boot.
This can be used to start a service on the first Ansible run without
enabling it for boot.
unmanaged: Ansible does not manage the service at all: both the
running state and the boot (enablement) state are left exactly as they
are. This is primarily useful when services are monitored and managed
by systems other than Ansible.
The singular form (service) is used for simplicity. However, the defined
status applies to all services if multiple are being managed by this role.
- Type:
str - Required: No
- Default:
"enabled" - Choices:
enabled,disabled,running,unmanaged
run_sshd_service_settings
sshd service configuration values (additional ones or to overwrite defaults;
see __run_sshd_service_settings_defaults in vars/main.yml for them).
Note on the default KexAlgorithms: it lists both post-quantum
hybrids (mlkem768x25519-sha256,sntrup761x25519-sha512@openssh.com).
Clients following the default crypto-policies of RHEL 10 and
Fedora 43/44 (as of 2026-08) offer only the MLKEM hybrid, so an
sntrup761-only server would lock them out. Platforms whose OpenSSH is older than 9.9 do not support the
MLKEM hybrid and get a default without it (see the OS-specific
files in vars/, e.g. vars/debian_12.yml). A KexAlgorithms
value you set yourself is used as-is on every platform.
Simply use standard SSH option names as keys with their corresponding values. Special cases:
For boolean values, use
true/false(these will be converted to “yes”/“no” strings by the role as needed).For options that can have multiple values, you can use a list:
HostKey: - "/etc/ssh/ssh_host_ed25519_key" - "/etc/ssh/ssh_host_ecdsa_key"This will generate multiple entries in the config file, one per list item.
Type:
dictRequired: No
Default:
{}
run_sshd_config_service_dropin_file_name
Filename of the drop-in configuration file to be placed in
/etc/ssh/sshd_config.d. Defaults to 00-managed.conf. The 00- prefix
ensures early loading and thus higher precedence over files with
higher-numbered prefixes.
If a non-default filename is used, any existing
/etc/ssh/sshd_config.d/00-managed.conf from previous Ansible runs will be
removed automatically to prevent conflicts.
Has to be a plain filename ending in .conf (the stock configuration only
includes sshd_config.d/*.conf), starting with an alphanumeric character
and containing only characters from A-Za-z0-9._- (no path separators);
the role fails during initialization otherwise.
- Type:
str - Required: No
- Default:
"00-managed.conf"
run_sshd_config_access_check
If set to true (the default), the role runs a best-effort
access-preservation preflight (anti-lockout) before the SSH daemon gets
restarted. It evaluates the effective configuration (via sshd -T,
including resolved Match blocks) for the account Ansible is connected
with and aborts the run if that account would provably be locked out,
for example by a restrictive AllowUsers, AllowGroups, DenyUsers,
DenyGroups, a PermitRootLogin value the connection cannot satisfy
(when connecting as root), an unsatisfiable AuthenticationMethods
combination, or by disabling every authentication method.
When Ansible is connected over SSH, the evaluation uses the real
connection context (client address, server address and port from the
session’s SSH_CONNECTION variable), so source-dependent Match
blocks and the host part of user@host access-list patterns are
checked against the actual client. On other connection types
localhost/127.0.0.1 is assumed. Only the common access-list glob
semantics (*, ?, !, user@host) are honored, and patterns whose
host part cannot be evaluated (unknown source, DNS name based
patterns) count as satisfied: the preflight only fails on lockouts it
can prove.
Contract and limitations of this emergency hatch (it guards against common footguns, it does not guarantee access):
- The check runs after the managed drop-in has been written; the written configuration is not rolled back on failure.
- Aborting prevents the queued restart handler under normal Ansible
failure semantics, so the running daemon keeps its previous
configuration; a later manual restart or reboot would still apply
the restrictive file.
force_handlers(or--force-handlers) executes queued handlers even after a failure and defeats this restart prevention. - Not detected: certificate/principal based restrictions,
authorized_keysoptions, PAM or account restrictions, DNS name based patterns and similar.
Set it to false to bypass the safeguard (only advisable if you are
certain you retain another way to access the host, e.g. a console).
- Type:
bool - Required: No
- Default:
true
Dependencies
See dependencies in meta/main.yml.
Compatibility
See min_ansible_version in meta/main.yml and __run_sshd_supported_platforms in vars/main.yml.
External requirements
- SELinux: This role does not handle SELinux configurations. Please add additional tasks before or after this role to accommodate these changes (e.g. if you’re changing SSH port on a system with SELinux enabled, allowing it via
ssh_port_tis needed). The following Ansible modules may help with SELinux configuration: - Firewall: This role does not manage firewall configurations. Please add additional tasks before or after this role to configure firewall rules as needed to access your SSH service.
Beside that, there are no special requirements not covered by the role or Ansible itself.