Making Ansible, Doas and OpenBSD play nicely
A little while ago, OpenBSD replaced sudo in the base system with doas. This transition follows a typical path in the OpenBSD community where a large, difficult to audit daemon or key application is replaced by an auditable and securable alternate implementation. At the same time, features are pruned if they add significant complexity. I don’t need all the features of sudo and I like the simplicity and security of doas so I wanted to switch. I use Ansible to provision and manage my small collection of servers and when version 2.0 added support for privilege escalation via doas (via the become keyword) I started to make my switch. The main use-case in my Ansible playbooks is where I run as root and become
my non-admin user so that permissions are set correctly by default, and in this case doas behaves differently…
# /usr/bin/env | grep -E '^(HOME|USER)=' HOME=/root USER=root # sudo -u esteele /usr/bin/env | grep -E '^(HOME|USER)=' USER=esteele HOME=/home/esteele # doas -u esteele /usr/bin/env | grep -E '^(HOME|USER)=' HOME=/root USER=root
The key difference is which environment variables are propagated from the original session. I find doas to be a little unintuitive in this respect, particularly the inability in the new session to write to the directory specified by $HOME
and this behaviour causes problems with the Ansible git and the pip tasks. The git task expects $USER
to correspond to the effective user id and the pip task expects $HOME
to be the home directory of the effective user id. The unexpected doas environment meant the tasks were trying to write files, as my non-admin user, under /root which was unsurprisingly unsuccessful. Fortunately Ansible has a way to specify environment variables for a task and even though it’d be preferable for the become
mechanism to take care of this, it’s an effective workaround (albeit a slightly dirty one).
The example below, from my web host playbook, ties the environment specification and the doas become in a block
# doas preserves USER and HOME, which is different to sudo, however by # specifying them as environment variables we can proceed. # USER is required for git, and HOME is required for pip - block: - name: Checkout wordspeak repo git: repo=ssh://git@github.com/edwinsteele/wordspeak.org.git dest=/home/esteele/Code/wordspeak.org - name: Setup nikola virtualenv pip: requirements=/home/esteele/Code/wordspeak.org/requirements.txt virtualenv_command=/usr/local/bin/virtualenv virtualenv=/home/esteele/.virtualenvs/wordspeak_n7 become: True become_method: doas become_user: esteele environment: USER: esteele HOME: /home/esteele
And having tied this together, I can remove the sudo package!
- name: Remove sudo openbsd_pkg: name=sudo-- state=absent