diff --git a/content/blog/20240624T104859--secrets-management-using-unix-password-store-pass__linux_osx_sysadmin.md b/content/blog/20240624T104859--secrets-management-using-unix-password-store-pass__linux_osx_sysadmin.md new file mode 100644 index 0000000..ed234ea --- /dev/null +++ b/content/blog/20240624T104859--secrets-management-using-unix-password-store-pass__linux_osx_sysadmin.md @@ -0,0 +1,303 @@ ++++ +title = "Workflows for Unix Password Store with Emacs and Shell" +date = 2024-04-13 +[taxonomies] +tags = ["programming"] +categories = ["lisp" "shell"] ++++ + +# Table of Contents + +1. [Managing Secrets using Pass](#orgeea54c9) +2. [Installation](#org44c3160) + 1. [GUIX](#org7920015) + 2. [Debian, Ubuntu et al](#orgbee77ce) +3. [Emacs Integration](#orgad61fe8) + 1. [Enable the Unix Password Store.](#orgf739a63) + 2. [Helper Function](#org1c798ff) + 3. [Using in Emacs Configuration](#org25108ee) +4. [Shell integration](#org849f72f) + 1. [DirEnv integration](#org299dd7c) +5. [Tips](#org158dbf2) + 1. [Entering passwords on the terminal](#org00fffd0) + 2. [Getting fields from multi field secrets](#org6ab1e11) + + + + + +# Managing Secrets using Pass + +On UNIX there is a well known tool to manage secrets called +**password-store** or **pass** for short. + +It is a very minimal tool, more to facilitate workflows that to do +real work, very much in the UNIX philosophy. It stores all secrets in +plain files in a folder structure. It does not care about what is in +the files and encrypts them using a GPG public key so only the owner +of the private key can decrypt them. It does offer special access to +the first line so a password can be quickly fetched and copied to the +clipboard or stdout or wherever some **pass** aware integration needs it. + +Certain folders can be configured to use a different public keys to +allow pragmatic secret delegation to different systems without +exposing all secrets. + +It leverages the **gpg** infrastructure for key management, distribution, +unlocking with **pinentry**, caching with **gpg-agent**. + + + + +# Installation + + + + +## GUIX + +Add **password-store**, **gpg** and **pinentry** to your package list. + +The **pinentry** program provides a client to securely unlock your keys in +a GUI and terminal environment. There are other options to make it +better fit your environment, but this is fine for me. + + + + +## Debian, Ubuntu et al + +Add **pass**, **gpg**, **gpg-agent** and **pinentry-gnome** of **pinentry-qt** using + + $ apt-get install pass gpg gpg-agent pinentry-qt + +Both the gnome and qt versions at least fall back gracefully if no +graphical environment are available. + +The current selected pinentry program is provided as +**/usr/bin/pinentry** and this link is managed by the usual alternatives +machinery in debian based distros. + + + + +# Emacs Integration + +Emacs **auth-source** infrastructure supports **pass** out of the box. + + + + +## Enable the Unix Password Store. + +We have to make sure the password-store is added to the +auth-sources. There is a handy function for that: + + ;; enable unix password-store + (auth-source-pass-enable) + +We add that somewhere in the init.el before any secrets are needed. + + + + +## Helper Function + +Auth-sources is a flexible system and works like a database, i.e. you +can query, browse through results and have multiple fields per secret. + +Example: + + (defun snam-password (host user) + "Get password from the unix password store. + + Searches the password file for a secret in the folder corresponding to + the HOST name given, which is the folder with the '/' replaced by a '.'. + The filename in the folder corresponds to the USER argument with a + '.pgp' extension." + (auth-info-password + (car (auth-source-search + :max 1 + :host host + :user user)))) + +\`auth-source-search\` returns a list of results, so we have to get the +first entry with \`car\`. The secret is lightly obfuscated, hence the +need to decode it with the \`auth-info-password\` function. + +Luckily there is a function [auth-source-pass-get](help:auth-source-pass-get) to get a password +from the password store which also follows the recommended conventions +for multiple fields in a pass file. + + (auth-source-pass-get 'secret "snamellit/znc") + +The pseudo key \`'secret\` returns the first line of the password store +entry which contains the password, per **pass** conventions. + + + + +## Using in Emacs Configuration + +For single secrets, like connecting to my **znc** IRC bouncer: + + (erc-tls :id 'znc :server "********" + :port "****" :user "xyz" :nick + "foobar" :password (auth-source-pass-get 'secret "foobar/znc"))) + +For multifield secrets, like for google authentication, we can +leverage the multiple fields in the password store. Here is my +**org-gcal** configuration: + + (setq org-gcal-client-id (auth-source-pass-get 'secret "snamellit/org-gcal-client") + org-gcal-client-secret (auth-source-pass-get "id" "snamellit/org-gcal-client") + org-gcal-fetch-file-alist '(("xyz@foobar.com" . "~/org/schedule.org"))) + + + + +# Shell integration + + + + +## DirEnv integration + +When developping 12-factor or similar inspired apps, the configuration +is passed using environment variables. Using **.envrc** files with +**direnv** integration in the shell is a very smooth way to work in +multiple projects. + +It is of course less than ideal to have the secrets exposed in the +**.envrc** files in your project tree even if it is in the **.gitignore** +file, although that is infinitely better than having secrets end up in +the git repository. + +Secrets in the **.envrc** files can be easily moved to the password store +by entering the folder. The following snippet prints the current value +to the screen and then inserts it in the password store, and verifies +it actually is entered correctly. + + $ echo $FOO_BAR_PASSWORD + $ echo $FOO_BAR_PASSWORD | pass add -e foo/bar + $ pass foo/bar + +and then editing the **.envrc** file from + + ... + FOO_BAR_PASSWORD= + ... + +to + + ... + FOO_BAR_PASSWORD=$(pass foo/bar) + ... + +After modification you'll be asked to allow to read the new **.envrc** +file content and you can check if it still works by comparing the +password with the one printed previously. + +Printing the passwords allows to fix any typos. If this are the only +copies you have of them you might rug-pull yourself. Ideally it should +be possible to quickly recreate secrets if you lose any, but reality +is often far from ideal. Echoing all these passwords is also not +ideal, but preferable over keeping a little black book of secrets. If +these passwords are coming from another password manager it is not +needed of course. + +Do not forget to clear your terminal scroll back history with + + $ clear + +To help migration of **.envrc** files I created a bash script I stored in +**~/.local/bin/envrc-to-pass**: + + #!/bin/bash + + if [ "$#" -ne 2 ]; then + echo "Usage: $0 " >&2 + exit 1 + fi + + VAR_NAME=$1 + SLUG=$(echo $VAR_NAME | sed 's/_/-/g' | tr '[:upper:]' '[:lower:]') + NAMESPACE=$2 + + + echo "Moving variable $VAR_NAME to $NAMESPACE/$SLUG in password store" + + SECRET=$(grep "$VAR_NAME=" .envrc | cut -d'=' -f2) + if (echo $SECRET | grep '^\$(pass.*)'); then + echo "secret already migrated" + else + echo $SECRET | pass insert -e $NAMESPACE/$SLUG + fi + + sed -i "s#$VAR_NAME=.*#$VAR_NAME=\\\$(pass $NAMESPACE\/$SLUG)#" .envrc + +This makes short work of migrating projects to use the password-store. + + + + +# Tips + + + + +## Entering passwords on the terminal + +Usually invoking \`pass add foobar/baz\` will ask to enter the password +and confirm it in the shell. + +However when piping a secret into the password-store +with + + echo FOO_BAR_PASSWORD | pass add foo/bar + +will silently fail although the man pages tell that \`pass add\` will +read from **stdin**. It is not clear IMO that you have to specify the **-e** +flag like : + + echo FOO_BAR_PASSWORD | pass -e add foo/bar + +to suppress the confirmation and make it work as expected. + + + + +## Getting fields from multi field secrets + +Often secrets come in multiple parts which are nice to be stored in a +single entry in order not to complicate the tree. The **pass** +documentation suggests: + + + field1: + field2: + +the main secret is just the first line, and can have the same +structure as the other lines, if that makes more sense. + +e.g. for a google integration: + + service-account: 1234567-abcdefghijklm@developer.gserviceadmin.com(some-project) + email: xyz@foobar.com + private-key: ... + +In this case it is useful to document which kind of secret it is as it +could also be an api-key, or a refresh-token, or whatever part of the +authentication menagerie that Google offers. + +To get these fields individually I use: + + GOOGLE_EMAIL=$(pass foo/bar | awk '/^email:/ {print $2}) + +in emacs this syntax is supported directly and the same info can be +fetched with + + (let + ((google-email (auth-source-pass-get "email" "foo/bar"))) + ... ) + diff --git a/themes/blow b/themes/blow index d987049..1a510f1 160000 --- a/themes/blow +++ b/themes/blow @@ -1 +1 @@ -Subproject commit d987049cd2660ab019ebaaf108aa69fff7fb847d +Subproject commit 1a510f1be436d04c36a3ff0596b3162673ff1298