GnuPG, pass and environment variablesnixlinuxwsl
Vault is a popular secret store in the production thanks to its helm integration: the secrets are injected to the pods via helm side car. No secrets are exposed in the plaintext in the deployment pipeline. 🎉
I would like to adopt the similar approach to manage secrets in the local development such as the database credentials, API keys, etc. They are currently all over the place:
- Some credentials are exposed as the environment variables, open to access for any app. 😱
- Some app-specific credentials are set in the current shell before running the command. A malicious app may peak the command history to steal the credentials1.
- Some app-specific credentials are set in the
.envrc, and populated by
direnv. The secret management is more transparent to the user, but the secrets are still stored in the plaintext.
Pass and GnuPG
First, we need a secure secret store, such as pass. It uses the GnuPG to store
the secrets, we can retrieve the secrets as long as we can pass the
You may install and configure the GnuPG and pass with this PR if you use nix.
It configures the
pinentry to use curses interface and disable the SSH support
for the time being.
gpg --gen-key to generate the the private / public key pair. Then we
can initialize the pass store by following this guide:
pass init "<The name of GPG key> pass git init
As I am only use
pass for command line secret management, I use the
corporation/ENV_KEY naming convention, here is my pass store;
❯ pass Password Store └── epicgames └── CODEFRESH_API_KEY
Then I can replace the secret in plaintext with the following snippet
export CODEFRESH_API_KEY=$(pass epicgames/CODEFRESH_API_KEY)
The first time, you will be prompted with the pinentry for the pass phase of the private key, then it just works as long as the gpg session is not expired.
PGP, and its open source counterpart, GnuPG is complicated for this simple use case. Once I am in the gpg bandwagon, I may explore other usages:
- Replace the ssh-agent for SSH key management.
- Sign the github commit as a 10x engineer. 😎
Caveats under Windows
After I published this post, I suddenly realized the above solution does not solve my problem, at all — how could I populate the environment variables for IntelliJ in build and run time without exposing the secrets in plaintext?
There are several options, but none of them just works ™.
The JetBrains Gateway is still in beta, but it is probably the most
promising approach. It worked like VsCode remote development: the server runs
in the remote machine, WSL more concretely, then the UI client connects to the
server for rendering.
Unfortunately, the server is a puppet of the client. It does not support
direnv, and I have no idea how to inject the environment variables from the
I really have high hope on this project, as it seems to lead to the right direction to do remote development right. /sigh.
We can run
gradle in WSL to build, test the project in the shell
configured by the
direnv, and just use IntelliJ for editing and debugging.
This might be a reasonable compromise, and it should work. But I just miss the
one-click convenience to run gradle tasks.
We can try to replicate the above setup in Windows:
- There exists a abandoned Pass4Win project, which uses Gpg4Win for encryption.
- Use Git Bash for Windows as shell.
cdto the project, baptized with environment variables populated by direnv.
This might work, but I loathe to setup another shell environment with inconsistent behaviors while I already have one. I might just stick to cygwin if I could settle for a suboptimal experience, — personal opinion, no offense.
This is the exactly opposite of the above direction, we can run IntelliJ as a WSL GUI application, recommended in this guide.
I still consider the WSL is an augment for Windows developer to access Unix environment. If I decide to go this far, maybe I should just pull the trigger to ditch Windows and install NixOS on my workstation.
Every time I am frustrated with the tug of war of Windows application and shell, I just eyed my MacBook Pro: there is a reason developers pay premium for a coherent development environment.
- If you use zsh, you can set
HIST_IGNORE_SPACEoption to avoid persisting sensitive data in the history.↩