2021-11-23 - Progress - Tony Finch
i have been looking at how to use the 1Password op command-line tool
with Ansible. It works fairly nicely.
You need to install the 1Password command-line tool.
You need a recent enough Ansible with the community.general
collection installed, so that it includes the onepassword lookup
plugin
To try out an example, create an op.yml file containing:
---
- hosts: localhost
tasks:
- name: test 1password
debug:
msg: <{{ lookup("onepassword",
"Mythic Beasts",
field="username") }}>
You might need to choose an item other than Mythic Beasts if you don't have a login with them.
Initialize op and start a login session by typing:
eval $(op signin)
Then see if Ansible works:
ansible-playbook op.yml
Amongst the Ansible verbiage, I get the output:
ok: [localhost] => {
"msg": "<hostmaster@cam.ac.uk>"
}
Some more detailed notes follow...
aims
I want it to be easy to keep secrets encrypted when they are not in use. Things like ssh private keys, static API credentials, etc. "Not in use" means when not installed on the system that needs them.
In particular, secrets should normally be encrypted on any systems on which we run Ansible, and decrypted only when they need to be deployed.
And it should be easy enough that everyone on the team is able to use it.
what about regpg?
I wrote regpg to tackle this problem in a way I consider to be safe. It is modestly successful: people other than me use it, in more places than just Cambridge University Information Services.
But it was not the right tool for the Network Systems team in which I
work. It isn't possible for a simple wrapper like regpg to fix
gpg's usability issues: in particular, it's horrible if you don't
have a unix desktop, and it's horrible over ssh.
1password
Since I wrote regpg we have got 1Password set up for the team. I
have used 1Password for my personal webby login things for years, and
I'm happy to use it at work too.
There are a couple of ways to use 1Password for ops automation...
secrets automation and 1password connect
First I looked at the relatively new support for "secrets automation" with 1Password. It is based around a 1Password Connect server, which we would install on site. This can provide an application with short-term access to credentials on demand via a REST API. (Sounds similar to other cloudy credential servers such as Hashicorp Vault or AWS IAM.)
However, the 1Password Connect server needs credentials to get access to our vaults, and our applications that use 1Password Connect need API access tokens. And we need some way to deploy these secrets safely. So we're back to square 1.
1password command line tool
The op command has
basically the same functionality as 1Password's GUIs. It has a similar
login model, in that you type in your passphrase to unlock the vault,
and it automatically re-locks after an inactivity timeout. (This is
also similar to the way regpg relies on the gpg agent to cache
credentials so that an Ansible run can deploy lots of secrets with
only one password prompt.)
So op is clearly the way to go, though there are a few niggles:
The
opconfiguration file contains details of the vaults it has been told about, including your 1Password account secret key in cleartext. So the configuration file is sensitive and should be kept safe. (It would be better ifopstored the account secret key encrypted using the user's password.)op signinuses an environment variable to store the session key, which is not ideal because it is easy to accidentally leak the contents of environment variables. It isn't obvious that a collection of complicated Ansible playbooks can be trusted to handle environment variables carefully.It sometimes reauires passing secrets on the command line, which exposes them to all users on the system. For instance, the documented way to find out whether a session has timed out is with a command line like:
$ op signin --session $OP_SESSION_example example
I have reported these issues to the 1Password developers.
Ansible and op
Ansible's community.general collection includes
some handy wrappers around the op command, in particular the
onepassword lookup plugin. (I am not so keen on the others
because the documentation suggests to me that they do potentially
unsafe things with Ansible variables.)
One of the problems I had with regpg was bad behaviour that occurred
when an Ansible playbook was started when the gpg agent wasn't ready;
the fix was to add a task to the start of the Ansible playbook which
polls the gpg agent in a more controlled manner.
I think a similar preflight task might be helpful for op:
check if there is an existing
opsession; if not, prompt for a passphrase to start a sessionset up a wrapper command for
opthat gets the session key from a more sensible place than the environment
To refresh a session safely, and work around the safety issue with op
signin mentioned above, we can test the session using a benign
command such as op list vaults or op get account, and run op
signin if that fails.
The wrapper script can be as simple as:
#!/bin/sh OP_SESSION_example=SQUEAMISHOSSIFRAGE /usr/local/bin/op "$@"
Assuming there is somewhere sensible and writable on $PATH...