Synopsis #
Ansible can manage OpenBSD over SSH, but most modules require a packaged Python interpreter on each managed node. Privileged tasks also require an explicit doas(1) policy. This guide prepares one node, proves unprivileged operation, adds controlled privilege elevation, and runs an idempotent package example.
Ansible is not an OpenBSD base-system component. The controller, ansible-core, and collections have their own support cycles. Confirm their current OpenBSD notes before upgrading an automation environment.
Define the Trust Boundary #
An automation controller can distribute commands and files to every managed node. Protect its SSH keys, inventory, vault material, logs, and backups accordingly. Use a dedicated named account on managed nodes and do not permit direct root SSH login.
Before automation, establish:
- a working console or independent recovery path;
- a verified named administrator account;
- a host-key verification process;
- a backup of every file the first playbook will manage;
- a small initial host group rather than the entire fleet.
Create an Administrator Account and Configure Secure Remote Access provide the OpenBSD access baseline.
Install Python on the Managed Node #
Ansible’s raw module can operate without Python, but ordinary modules cannot. On the managed OpenBSD node, search the current package set and install the Python 3 meta-package selected for that release:
$ pkg_info -Q python
# Inspect available Python package stems
# pkg_add python%3
# Install the current Python 3 package selected by the package tools
$ /usr/local/bin/python3 --version
# Confirm the stable interpreter path used by inventory
If package selection is ambiguous, choose the maintained Python 3 package explicitly rather than hard-coding a minor version from an older guide. Keep Python updated with the rest of the installed packages.
Create the Inventory #
On the Ansible controller, create an inventory file such as inventory.ini:
[openbsd]
router1.example.net ansible_user=automation
[openbsd:vars]
ansible_python_interpreter=/usr/local/bin/python3
ansible_become=true
ansible_become_method=doas
ansible_become_user=root
The host name, account, and grouping are examples. Use verified DNS or addresses and retain SSH host-key checking. Do not use ansible_ssh_common_args to disable host-key verification.
Test SSH before invoking Ansible:
$ ssh automation@router1.example.net
Then test the Python module path without privilege elevation:
$ ansible openbsd -i inventory.ini -m ansible.builtin.ping -e ansible_become=false
Ansible’s ping module tests module execution; it does not send ICMP echo requests.
Configure doas Deliberately #
Ansible normally executes temporary module code as the target user. A command-specific doas.conf rule cannot authorize arbitrary privileged modules safely. Choose one of these models:
- Require the automation account’s password and run playbooks with
--ask-become-pass. - Grant passwordless elevation to a tightly protected automation account and treat its SSH key as a root credential.
- Avoid general elevation and use narrowly designed
rawtasks that match command-specific rules, accepting reduced module functionality.
For the interactive-password model, add this rule to /etc/doas.conf on the managed node:
permit automation as root
Parse the policy and test the exact identity with doas(1) :
# doas -C /etc/doas.conf
# Parse the complete policy without executing a command
$ doas -u root id
# Confirm authentication and target identity from the automation account
Run privileged playbooks with --ask-become-pass. A nopass rule should be adopted only after the root-equivalent trust decision is documented and the account is restricted to dedicated keys, controlled source addresses where practical, and monitored use.
Install the OpenBSD Collection Support #
The community.general.openbsd_pkg module is distributed in the community.general collection and is not part of ansible-core. Install and record the collection on the controller:
$ ansible-galaxy collection install community.general
$ ansible-galaxy collection list community.general
Pin collection versions in the automation project’s requirements file when repeatable controller builds are required.
Run an Idempotent Package Playbook #
Create openbsd-baseline.yml on the controller:
---
- name: Maintain a small OpenBSD package baseline
hosts: openbsd
gather_facts: true
become: true
tasks:
- name: Ensure rsync is installed
community.general.openbsd_pkg:
name: rsync
state: present
- name: Record the managed node release
ansible.builtin.command: uname -a
register: openbsd_uname
changed_when: false
- name: Display the managed node release
ansible.builtin.debug:
var: openbsd_uname.stdout
Run syntax and dry-run checks first:
$ ansible-playbook -i inventory.ini openbsd-baseline.yml --syntax-check
# Parse inventory and playbook structure
$ ansible-playbook -i inventory.ini openbsd-baseline.yml --check --diff --ask-become-pass --limit router1.example.net
# Preview the first managed node
$ ansible-playbook -i inventory.ini openbsd-baseline.yml --ask-become-pass --limit router1.example.net
# Apply only after the preview and recovery plan are acceptable
Check mode is a prediction and not every module can model all changes. Keep the first production run limited and inspect the resulting package and log state on the node.
Manage Configuration Safely #
For each managed file:
- render to a temporary path or use module validation where available;
- preserve owner, group, and mode explicitly;
- notify a handler only when content changes;
- validate daemon syntax before reload;
- keep the previous remote session and recovery route available;
- avoid overwriting package examples or local secrets without a documented merge policy.
Do not automate sysupgrade, PF replacement, disk operations, or SSH authentication changes as a first playbook. Establish idempotence, limits, serial rollout, and recovery on low-risk state before expanding authority.
Diagnose Failures #
Use -vvv only in protected logs because output can contain paths, variables, command lines, and remote data. Separate SSH, Python interpreter, become, module, and target-command failures. Confirm the same SSH account and doas action manually on the affected node before changing playbook logic.