Using 1password for Laravel environment variables
Let's face it: secret management isn't the most thrilling part of development, but it's crucial. Even though it's very tempting to manage environment variables and secrets by ssh
ing into a server and typingvi .env
, better, and more secure alternatives are available. Let's look at some options, why we chose the 1Password CLI and how to set it up.
Why should I care?
Secret management goes beyond just keeping things secure - it's also about access control and avoiding accidents. Not everyone needs (all) the keys to the kingdom. You might want to give your junior dev access to the staging environment, but keep the production credentials under stricter control. In corporate speak this is called "implementing the principle of least privilege for effective risk mitigation in secret management", but we'll call it common sense.
The right tool for the right job
Before jumping right to the HashiCorp Vaults of this world, you might want to consider these more accessible options for your small to medium size Laravel application:
Product | Git Compatibility | Granular User Access | Encryption |
---|---|---|---|
artisan env:encrypt | ❌ | ❌ | ✔️ |
git-secret | ❌ | ✔️ | ✔️ |
1Password CLI | ✔️ | ✔️ | ✔️ |
As you can see, env:encrypt
and git-secret
are both effective tools for securely storing your environment variables. However, they save the encrypted secrets as binary files, which makes merging them with Git impossible. By using the 1Password CLI, we can manage .env
files for all our application's environments without any of the downsides.
1Password to rule them all
For as long as I can remember, we’ve relied on 1Password to securely share passwords, secrets, and API keys with the Spatie team. It feels like a logical progression to also use their developer tools for integrating those very secrets into our deployments. Did I mention there's excellent JetBrains and VS Code plugins as well, that make working with 1Password references in your config and .env files even easier?
Generating .env
files with 1Password
1Password's CLI, op
, has a couple of useful commands. We'll be using op inject
to inject some secrets from 1Password entries into a template file.
The .env.template
file:
APP_KEY=op://Flare/Environment/APP_KEY
# and other variables...
Executing this command will output a new .env
file with the 1Password references replaces with their actual values:
op inject -i .env.template -o .env
The outputted .env
file looks like this:
APP_KEY=123abc
# and other variables...
Simple, yet effective.
One template, many environments
Typically, you'll have different environment variables for your staging and production environment. Maybe you even want to share a set of local environment variables with your team to make local development easier.
For this, we'll create separate entries in our 1Password vault for each of these environments. Be sure to include the environment name in the entry title, as we'll use that to link it to the APP_ENV
.
[!TIP]
Keep the set of variables consistent across environments. Theop
CLI will complain if you use a 1Password reference that doesn't exist in your 1Password entry.
Next, you can use the $APP_ENV
variable in your 1Password references to access the correct entries (or vaults):
# The $APP_ENV will be replaced with "staging" or "production"
APP_KEY=op://Flare/$APP_ENV-env/APP_KEY
By setting the APP_ENV
before running the op inject
command, we can generate an .env
file using a specific 1Password entry, for a specific environment:
APP_ENV=staging op inject -i .env.template -o .env
[!WARNING]
Instead of creating separate entries in 1Password for each environment, you should create different Vaults. This approach allows you to manage access to each environment's secrets separately.
Integrating 1Password in your deploy process
Depending on your deployment process, there's two options to consider when integrating the 1Password CLI:
- generating the
.env
directly on your production server(s) - generating the
.env
file on your local machine (or CI) and copying it to the production server(s)
We prefer the first option, this way we never have to store any secrets on your local machine or CI.
To access the 1Password vault on your production or staging servers, you'll need to create a 1Password service account. The op
CLI and service account also need to be configured on your production or staging servers. It's best to only configure this service account during the deployment process. This way the op
CLI is useless for any bad actor that might gain direct access to the server.
In Laravel Envoy this looks like this:
@setup
// Get the service account token using the local 1Password CLI
$opServiceAccount = shell_exec('op read "op://Flare/service-account/credential"');
$environment ??= 'staging';
@endsetup
...
@task('updateEnvironment', ['on' => $environment])
# Configure the service account only for this shell session
export OP_SERVICE_ACCOUNT_TOKEN={{ $opServiceAccount }}
// Set the APP_ENV based on the $environment and generate the .env
APP_ENV={{ $environment }} op inject -i .env.1pass -o .env
@endtask
In the @setup
block, we're loading the service account credentials from our local op
CLI. Once we're ready to generate the .env
file on our remote server, the updateEnvironment
task will configure the service account token and run the op inject
command for the current environment.
If you're using GitHub actions to build or deploy your application, there's this excellent GitHub action to install, configure and use the op
CLI in your GH actions.
Wrapping up
And there you have it - using the 1Password CLI for your Laravel .env
files makes secret management less of a headache and more secure.