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
op
configuration 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 ifop
stored the account secret key encrypted using the user's password.)op signin
uses 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
op
session; if not, prompt for a passphrase to start a sessionset up a wrapper command for
op
that 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
...