GnuPG, pass and environment variables
nix linux wslVault 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 bydirenv
. 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 gpg-agent
’s
challenge.
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.
Invoke 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
in the .envrc
:
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.
Future work
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 ™.
JetBrains Gateway
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
shell.
I really have high hope on this project, as it seems to lead to the right direction to do remote development right. /sigh.
Remote Debugging
We can run maven
or 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.
All-in Windows
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.
- Always
cd
to 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.
All-in Linux
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.
Closing Thoughts
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.
Footnotes
-
If you use zsh, you can set
HIST_IGNORE_SPACE
option to avoid persisting sensitive data in the history. ↩