Epistemic Status: evergreen tree Evergreen


I’ve been a user of 1Password, one of the more popular password managers, for years after moving away from LastPass, long before their acquisition and later spin off and recent disasters… really, you should stop using LastPass, like right now. Ever since, I haven’t looked back. I have a family plan so my partner and I can share passwords (e.g. things like Netflix subscription credentials) and other sensitive information securely.

Recently, I’ve discovered some features that have turned out to be incredibly useful for software engineering problems.

1Password CLI

It was originally announced as beta back in 2017, but until earlier this year, I didn’t take a look.

The 1Password CLI allows you to access secret vaults and read out secrets for use in shell scripts and other command line type situations. There are probably lots of ways to use it that I haven’t yet discovered, but it pairs with the desktop application very well, allowing it to handle authentication of access. This means I don’t have to store credentials in any configuration files or environment variables long term that could be accidentally read.

This works really well for command line tools, but you could probably find ways to make it work for GUI tools as well (depending on the tool). Some examples will help demonstrate what I mean.

An even better way

Shortly after writing this, I discovered the CLI has the op run and op inject commands that can handle a lot of this automatically and provide extra features. I’ll update my examples once I have a chance to try them.

There’s nothing wrong with what I’ve shown here, and might give you a better idea of how it works, just know there are probably better ways.

Accessing a database using psql

To access a Postgres database, you need to pass in arguments about what host and what database you’re trying to access, and then you need to provide credentials.

For databases I access frequently, I’ll often make a script to wrap this up to use the op command. For this example, I store my work related keys in a separate vault, which makes them easier to remove when I need to.

#!/usr/bin/env bash
 
# A helper function to call the 1Password CLI and fetch
# named fields from the record holding the credentials
function op-cmd () {
  $(op read 'op://Work Vault/Prod Read Replica/$1')
}
 
PGPASSWORD=$(op-cmd "password") \
    psql \
    -h "$(op-cmd "hostname")" \
    -U "$(op-cmd "username")" \
    "$(op-cmd "database")"

Then, when I execute this command, the op command will talk to my desktop 1Password client to try to access the vault item. When it does that, 1Password will pop up an authentication window (even if I’m logged into my vaults already). This is how it appears on MacOS, which is what my work machine is running.

I don’t know why this message isn’t more specific about what is being accessed. As you’ll see, they do this correctly elsewhere.

Then, I can just use Touch ID to give it access, or cancel if I don’t want it to have access (kitty is the terminal emulator I’m using). Nothing is stored on the file system and only the program that needs access to these credentials should have access (it does say that “kitty” is asking, so maybe the terminal emulator has access?).

And most importantly, 1Password asks me for authentication every time (similar to using sudo, it will remember the response for a short time so rerunning the command doesn’t require a new authentication).

AWS CLI Usage

The AWS CLI is great, but it often has you store secrets in configuration files or environment variables in order to use it. We can use op and a wrapper script to handle this in a more sane manner.

#!/usr/bin/env bash
 
# A helper function to call the 1Password CLI and fetch
# named fields from the record holding the credentials
# TODO - this could be made generic by having the vault string
#   be an argument passed in?
function op-cmd () {
  $(op read 'op://Work Vault/AWS CLI Creds/$1')
}
 
AWS_ACCESS_KEY_ID=$(op "Key ID") \
AWS_SECRET_ACCESS_KEY=$(op "Key Secret") \
AWS_DEFAULT_REGION=us-east-1 \
aws "$@"

I hard-coded region here, but that could also be included in the vault item and pulled from there as well to make it more general purpose.

SSH Keys

1Password recently added support for it to act as an SSH Agent.

Honestly, this is a killer feature for me, and their implementation of it is superb. This works both for SSH into remote machines as well as things like Git authentication via SSH keys.

For example, whenever I run a git pull on a work repository, I see this window pop up:

I’m not sure why you would want to use “Approve for all applications”… ever. But there must be uses for this.

From this I know that something is trying to access my SSH keys, and I know which key they want. Again, since 1Password is tied into Touch ID, I can just tap my finger to give it access.

Because it asks me before just handing out keys, it’s safe to use things like SSH Agent Forwarding to remote machines. Regular ssh-agent just hands keys out to whoever asks, and so using agent forwarding to a remote machine is a risky proposition with regular ssh-agent.

Combining tools for extra synergy

I use Nix (+ direnv) for creating stable consistent development environments for all the projects I work on. This lets me do things like have scripts that are scoped to just those projects. Combining this with 1Password’s CLI and SSH agent features makes it easy to do without having to hard-code credentials or place them in my environment where any program could read them.

That’s a story for another time winking face.