This commit is contained in:
parent
b3a5af5182
commit
ab33b9a1c1
1 changed files with 416 additions and 0 deletions
|
@ -0,0 +1,416 @@
|
|||
+++
|
||||
title = "Consolidating Secrets in Pass"
|
||||
author = ["Peter Tillemans"]
|
||||
date = 2025-06-28T13:15:00+02:00
|
||||
tags = ["emacs", "linux", "security"]
|
||||
categories = ["shell", "apps"]
|
||||
draft = false
|
||||
[taxonomies]
|
||||
tags = ["emacs", "linux", "security"]
|
||||
categories = ["shell", "apps"]
|
||||
+++
|
||||
|
||||
## Background {#background}
|
||||
|
||||
Like a lot of people I've had a long history managing passwords and
|
||||
secrets over the years. From a little black book, over an Excel sheet,
|
||||
using a GPG encoded secrets file (works really well with Emacs gpg
|
||||
support), 1password (till they racked up their prices), lastpass (till
|
||||
they got bought by the Evil LogMeIn Corp), KeepassXC and lately [pass](https://passwordstore.org).
|
||||
|
||||
I was perfectly happy with KeepassXC for a very long time, except for
|
||||
the command line integration. So I kept ending up with passwords in
|
||||
**.envrc** files in folders and excluded in the global **.gitignore** to avoid
|
||||
too many red cheeks. While this does keep secrets out of harms way
|
||||
mostly, it kept nagging that I had them in plain text in those
|
||||
files. In theory there is **keepassxc-cli** to query the passwords from
|
||||
the command line, but let's say the experience does not spark joy. It
|
||||
has no easy way to cache the password between calls and it is
|
||||
optimized for interactive use. (AFAICT, just the giant size of the
|
||||
command to type gives me dread).
|
||||
|
||||
Some day I stumbled over **pass** and found that after setup I could just
|
||||
`pass snamellit/website` to get the password on stdout. I [wrote about
|
||||
the setup and emacs integration in a previous post](https://www.snamellit.com/blog/20240624t104859-secrets-management-using-unix-password-store-pass-linux-osx-sysadmin/). Since it
|
||||
leverage gpg, password caching is handled by the gpg-agent and my
|
||||
**.envrc** files quickly were purged of blasphemous secrets, replace by
|
||||
pure bliss:
|
||||
|
||||
```shell
|
||||
export MY_SECRET=$(pass my/secret)
|
||||
export OTHER_SECRET=$(pass other/secret)
|
||||
```
|
||||
|
||||
similarly in emacs I can consistently get my passwords and related
|
||||
info with:
|
||||
|
||||
```elisp
|
||||
(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"))
|
||||
```
|
||||
|
||||
When needed the **gpg-agent** will launch the appropriate pin-entry
|
||||
program whether in terminal or in the GUI and the caching will not
|
||||
force me to login several times when entering the folder.
|
||||
|
||||
So I ended up with my interactive use covered by **KeepassXC** and
|
||||
automated use by **pass**.
|
||||
|
||||
However, after some time I ended up with hundreds of secrets in
|
||||
**KeepassXC**, hundreds in **pass**, it is not always clear whether use is
|
||||
interactive or automated so confusion and duplication starts and
|
||||
things become harder to manage. In addition **KeepassXC** was using
|
||||
historically Dropbox to make it available on all my devices, recently
|
||||
migrated to Nextcloud, which has issues with dealing with conflicts
|
||||
which occasionally bite me in the behind. On the other hand **pass**
|
||||
secrets are stored encrypted in git where conflict punch you in the
|
||||
face. I prefer the latter. And started contemplating whether to move
|
||||
everything to pass.
|
||||
|
||||
Thanks to the encouragement of SummerEmacs, one of the more
|
||||
enthusiastic SystemCrafters, ensuring the great experience in browsers
|
||||
and iOS mobile devices I had no more excuses to keep postponing it.
|
||||
|
||||
|
||||
## Preparation {#preparation}
|
||||
|
||||
I started out with keeping my pass passwords as part of my
|
||||
dotfiles. This was convenient when they were few. However this is
|
||||
<span class="underline">weird</span> so this will attract <span class="underline">weirdness</span> when configuring all integrations
|
||||
I'll need.
|
||||
|
||||
Also **pass** supports a **git** command to manage the password-store with git
|
||||
which is not really useful when it is part of something else. So the
|
||||
first order of the day is to move all secrets to a separate repository
|
||||
and update the **dotfiles** to check for presence and clone the repo if
|
||||
missing (and do a gently pull when it is).
|
||||
A quick visit to each of the machines in my machine park to apply this
|
||||
change.
|
||||
Everything still seems to be working.
|
||||
|
||||
|
||||
## Migration of the KeepassXC data {#migration-of-the-keepassxc-data}
|
||||
|
||||
I used the [pass-import](https://github.com/roddhjav/pass-import) tool which adds in **import** command to pass which
|
||||
supports a crazy amount of password managers, including keepassxc. For
|
||||
keepassxc it need the **pykeepass**. If you're running on Arch, everything
|
||||
is a `yay -S` away. However on Ubuntu and its derivatives it is the
|
||||
usual slog we start to get accustomed to. It's all in the [pass-import
|
||||
README](https://github.com/roddhjav/pass-import) , note that on Ubunty the **pykeepass** library is available with
|
||||
`apt install python3-pykeepass`.
|
||||
|
||||
Once it is installed I tried a dry run (with the `-d` flag) to see if
|
||||
basic functionality is working
|
||||
|
||||
```shell
|
||||
pass import -a -d keepassxc ~/Nextcloud/Apps/Keepassxc/Passwords.kdbx
|
||||
Password for /home/pti/Nextcloud/Apps/Keepassxc/Passwords.kdbx:
|
||||
w Data would be imported from keepassxc to pass
|
||||
. Passwords imported from: /home/pti/Nextcloud/Apps/Keepassxc/Passwords.kdbx
|
||||
. Passwords exported to: /home/pti/.password-store
|
||||
. Number of password imported: 2035
|
||||
. All data imported
|
||||
w Weak password detected: eDGQqipE might be weak. Score 2 (100000001 guesses). This estimate is based on the sequence eDGQqipE(bruteforce)
|
||||
w Weak password detected: eDGQqipE might be weak. Score 2 (100000001 guesses). This estimate is based on the sequence eDGQqipE(bruteforce)
|
||||
w Weak password detected: eDGQqipE might be weak. Score 2 (100000001 guesses). This estimate is based on the sequence eDGQqipE(bruteforce)
|
||||
... large list of names of secrets
|
||||
|
||||
```
|
||||
|
||||
This asks for the password of the Keepass file and some remarks it
|
||||
has.
|
||||
|
||||
This all looks reasonable. So we can try the import. Since the
|
||||
password-store is a git repo no real damage can be done to it (as it
|
||||
is safely pushed somewhere else where the import tool cannot touch it)
|
||||
and any damage done can be reverted....
|
||||
|
||||
Now is a good time to check if the mooring lines of your laptop are
|
||||
properly secured as encrypting all the secrets will spin up the
|
||||
propellors if the number is large enough.
|
||||
|
||||
I run it again without the `-d` flag and after several minutes the
|
||||
noise dies down and I am left with a lot of additional folders in my
|
||||
`~/.password-store` which match the grouping in KeepassXC. The files
|
||||
contain the secrets and the expected metadata. This looks good so I
|
||||
add/commit the things to complete the level.
|
||||
|
||||
|
||||
## Integration with iOS for my iPhone {#integration-with-ios-for-my-iphone}
|
||||
|
||||
Let's start with the most scary one : the iPhone.
|
||||
|
||||
Upon recommendation I had installed **passforios** which needs to be
|
||||
configured.
|
||||
|
||||
Configuring the host, repo and username to use for the git repository
|
||||
is straightforward enough.
|
||||
|
||||
I always use ssh to access my repos so we need to add an ssh keypair
|
||||
for this purpose. There is no support to generate key-pairs in
|
||||
passforios for reasons, so I have to do it externally and upload the
|
||||
key. A quick `ssh-keygen` , uploading the public key to the forge,
|
||||
allowing access to the repo and if I can get the private key on my
|
||||
phone we can access the repo.
|
||||
|
||||
**passforios** has a nice feature to load ascii armored keys via a QR
|
||||
code. A bit digging surfaced the [asc-key-to-qr-code-gif tool](https://github.com/yishilin14/asc-key-to-qr-code-gif) which
|
||||
was made for this specific purpose. The ssh key is already in the
|
||||
appropriate format so this can be directly converted
|
||||
|
||||
```shell
|
||||
./asc-to-gif.sh ~/.ssh/id-passforios ssh-pub.gif
|
||||
display ssh-pub.gif
|
||||
```
|
||||
|
||||
Then go to the repository settings, press the circled i on the **SSH
|
||||
Key** button, select the ASCII-Armor Key and click to scan the QR
|
||||
code. Point the camera to the QR code on the screen and it should
|
||||
appear in the key field in the app.
|
||||
|
||||
We have to repeat this 2 more times to get the private and public key
|
||||
for the password-store into the app. First exporting the keys
|
||||
|
||||
```shell
|
||||
gpg --export -a 1234ABCD >gpg.pub
|
||||
gpg --export-secret-key -a 1234ABCD >gpg.key
|
||||
```
|
||||
|
||||
converting to a gif, displaying them and scanning them in \*Settings ->
|
||||
PGP Key -> ASCII-Armor Key in the respective fields.
|
||||
|
||||
If, after synching, you go now to the Passwords you should be greeted
|
||||
with a listing of all folders and keys and the secrets should be
|
||||
visible if made visible by tapping the eye icon.
|
||||
|
||||
I needed to enable **passforios** as a source for autofill : Settings ->
|
||||
Passwords -> Autofill Passwords and slide the toggle for **Pass**. I also
|
||||
disabled the toggle for **Strongbox** which I was using for integration
|
||||
with the Keepass database.
|
||||
|
||||
Now I see the option to select the secrets from the **passforios** app. It
|
||||
does not narrow down to the right key, but that is a problem for
|
||||
future me.
|
||||
|
||||
Ok, the hard part is done. Or at least the most risky part, ... in my
|
||||
eyes... whatever. Moving on...
|
||||
|
||||
|
||||
## Integration with FireFox {#integration-with-firefox}
|
||||
|
||||
Checking at the bottom of the [pass website](https://www.passwordstore.org/) we find that **passff** is the
|
||||
good stuff for integration with FireFox. From previous adventures with
|
||||
KeepassXC and NativeMessaging I assumed there had to be a host part to
|
||||
be installed too.
|
||||
|
||||
Indeed we are directed to the [passff-host github repo](https://codeberg.org/PassFF/passff-host) to get an
|
||||
install-script which generates the native messaging json for the
|
||||
different browsers and a small executable python script which contains
|
||||
remarkable clean and no-dependency code. Similarly the install script
|
||||
is straightforward. I do not understand why it support half a dozen
|
||||
browser, mostly chrome based as for the life of me I cannot find an
|
||||
extension which uses this host program. So either I need bigger
|
||||
glasses or there is some knowledge beyond my grasp.
|
||||
|
||||
Running the installer, installing the extension, restarting firefox
|
||||
for good luck and the extension appears and offers passwords on the
|
||||
sites I try.
|
||||
|
||||
Out of curiosity I check the configuration in
|
||||
~.mozilla/native-messaging :
|
||||
|
||||
```shell
|
||||
pti@tuxedo ~> ls .mozilla/native-messaging-hosts/
|
||||
org.keepassxc.keepassxc_browser.json passff.json passff.py*
|
||||
pti@tuxedo ~> cat .mozilla/native-messaging-hosts/passff.json
|
||||
{
|
||||
"name": "passff",
|
||||
"description": "Host for communicating with zx2c4 pass",
|
||||
"path": "/home/pti/.mozilla/native-messaging-hosts/passff.py",
|
||||
"type": "stdio",
|
||||
"allowed_extensions": [ "passff@invicem.pro" ]
|
||||
}
|
||||
pti@tuxedo ~> cat .mozilla/native-messaging-hosts/passff.py
|
||||
#!/usr/bin/python3
|
||||
"""
|
||||
Host application of the browser extension PassFF
|
||||
that wraps around the zx2c4 pass script.
|
||||
"""
|
||||
|
||||
import json
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Nothing out of the ordinary, the passff.py python is the same as in
|
||||
the repo. My old keepassxc extension support is still there.
|
||||
|
||||
Firefox is installed natively on this machine, not with a flatpak
|
||||
which I assume will come with its own challenges.
|
||||
|
||||
|
||||
## Chromium Support {#chromium-support}
|
||||
|
||||
Time to tackle the Chrome family. Chrome is required to put food on
|
||||
the table so we have to get that going eventually. But Chrome is
|
||||
distributed as a flatpak (or a snap but I am **NOT** going to deal with
|
||||
that), and I can install Chromium natively, and apparently native
|
||||
installs are **MUCH** better supported than the versions in wrappers so
|
||||
let's start with that one first.
|
||||
|
||||
From the [pass website](https://www.passwordstore.org/) we find that **[browserpass](https://github.com/browserpass/browserpass-extension)** is the way to go for
|
||||
the chrome family. The browser extension installs from the usual
|
||||
places without drama and starts promptly complaining it cannot find
|
||||
the native host to talk to.
|
||||
|
||||
The native host in question is from [the browsaerpass-native sister
|
||||
repo](https://github.com/browserpass/browserpass-native) . As usual for all distro's there are packages ready to install
|
||||
but because Ubuntu-derivative I can compile from source. Downloading
|
||||
the source for version 3.1.0 from the [releases page](https://github.com/browserpass/browserpass-native/releases). Again this repo
|
||||
refers to all browsers including firefox although I cannot for the
|
||||
life of me find a Firefox Extension supporting this host app.
|
||||
|
||||
Then building and installing timelapse :
|
||||
|
||||
```shell
|
||||
tar -xzvf ~/Downloads/browserpass-native-3.1.0.tar.gz
|
||||
cd browserpass-native-3.1.0
|
||||
ls
|
||||
less README.md
|
||||
PREFIX=/usr/local make configure
|
||||
sudo make PREFIX=/usr/local install
|
||||
which browserpass
|
||||
```
|
||||
|
||||
which shows the executable lives at `/usr/local/bin/browserpass` and
|
||||
this totally went fine the first time (NOT!!!!).
|
||||
|
||||
The `Makefile` has support to install the magic json to enable native
|
||||
messaging for the different browsers.
|
||||
|
||||
```shell
|
||||
PREFIX=/usr/local make hosts-chromium-user
|
||||
PREFIX=/usr/local make hosts-chrome-user
|
||||
```
|
||||
|
||||
The second invocation is a hail-mary because I already know the Chrome
|
||||
flatpak does not look in the same places and will require some
|
||||
additional finnagling
|
||||
|
||||
For now focus on Chromium and check if the configuration looks
|
||||
reasonable:
|
||||
|
||||
```shell
|
||||
pti@tuxedo ~> cd .config/chromium/NativeMessagingHosts/
|
||||
pti@tuxedo ~/.c/c/NativeMessagingHosts> ls
|
||||
com.github.browserpass.native.json@
|
||||
pti@tuxedo ~/.c/c/NativeMessagingHosts> cat com.github.browserpass.native.json
|
||||
{
|
||||
"name": "com.github.browserpass.native",
|
||||
"description": "Browserpass native component for the Chromium extension",
|
||||
"path": "/usr/local/bin/browserpass",
|
||||
"type": "stdio",
|
||||
"allowed_origins": [
|
||||
"chrome-extension://naepdomgkenhinolocfifgehidddafch/",
|
||||
"chrome-extension://pjmbgaakjkbhpopmakjoedenlfdmcdgm/",
|
||||
"chrome-extension://klfoddkbhleoaabpmiigbmpbjfljimgb/"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Cool, the executable is looked at where it is installed (this is not
|
||||
obvious, don't ask how I know). The rest looks also like how these
|
||||
things should look. Let's try...
|
||||
|
||||
The extension settings page is no longer complaining the native host
|
||||
is missing and there are password entries visible. Checking with some
|
||||
website shows the password is injected. yay!.
|
||||
|
||||
Level complete, ready for the final boss.
|
||||
|
||||
|
||||
## Enabling Chrome Support, now with more Flatpak! {#enabling-chrome-support-now-with-more-flatpak}
|
||||
|
||||
Ok, we have a working chromium support so repo access, host app,
|
||||
native host configuration et al are proven working. We can only focus
|
||||
on jumping over the Flatpak Firewall...
|
||||
|
||||
As a good cargo cultist I do a literature study and find that I should
|
||||
|
||||
- find the config location of the flatpak app
|
||||
|
||||
- use `flatpak-spawn` to spawn the native messaging host app
|
||||
|
||||
- enable D-Bus Session socket access for chrome
|
||||
|
||||
- Package up the calling of the host app in a single script to
|
||||
configure in the json.
|
||||
|
||||
Not necessarily in that order....
|
||||
|
||||
For the permission to access D-Bus Session start up **flatseal** from
|
||||
flathub, navigate to **com.google.Chrome** and enable the D-Bus Session
|
||||
socket. This should be possible with some additional cursing in the
|
||||
manifest file of Chrome. I cannot find decent reference documentation
|
||||
in a reasonable time, so **flatseal** it is.
|
||||
|
||||
The configuration of the flatpak app is easy too, painful experience
|
||||
seared in my brain that flatpaks look in **~/.var/app/** folder so for
|
||||
Chrome this will be **~/.var/app/com.google.Chrome** . From the hail-mary
|
||||
install for chrome done above I know that it just creates a symbolic
|
||||
link to
|
||||
**/usr/local/lib/browserpass/hosts/chromium/com.github.browserpass.native**
|
||||
so we can start from there. We will have to edit that so copy it. We
|
||||
also need a wrapper to call the native host app
|
||||
|
||||
```shell
|
||||
cd ~/.var/app/com.google.Chrome/config/google-chrome/NativeMessagingHosts
|
||||
cp /usr/local/lib/browserpass/hosts/chromium/com.github.browserpass.native
|
||||
ec browserpass.sh
|
||||
```
|
||||
|
||||
Add the content of the wrapper
|
||||
|
||||
```shell
|
||||
#!/bin/sh
|
||||
cd ~
|
||||
/usr/bin/flatpak-spawn --host /usr/local/bin/browserpass 2>/tmp/browserpass-error.log
|
||||
```
|
||||
|
||||
I added the optional redirect of **stderr** to an error logfile because
|
||||
from experience I know nothing ever goes wrong if you enable error
|
||||
reporting beforehand.
|
||||
|
||||
```shell
|
||||
chmod +x browserpass.sh
|
||||
pwd
|
||||
pwd | wl-copy
|
||||
ec com.github.browserpass.native.json
|
||||
```
|
||||
|
||||
Installing the browserpass extension in Chrome after restarting it (I
|
||||
am not superstitious, just careful) and I can bask in the glory of
|
||||
seeing proposals for passwords when trying to log in. Most of the
|
||||
proposals are pretty garbage, but that is a problem for future me.
|
||||
|
||||
|
||||
## Conclusion {#conclusion}
|
||||
|
||||
I have access to my password-store secrets on my phone, my browsers on laptop and
|
||||
desktop, and most importantly **Emacs**. Narrowing of the proposed secrets
|
||||
is, euhmmm, sub-optimal, but since it is sub-optimal in the same way
|
||||
on all platforms I assume that some TLC in the password-store and
|
||||
cleaning of the migrated secrets will fix that in time.
|
||||
|
||||
In the process I gained much more confidence in configuring flatpak
|
||||
apps. I can decommission the keepassxc system including dealing with
|
||||
the sync conflicts (which was admittedly super easy with the merge
|
||||
database feature in KeepassXC). I no longer have to deal with giving
|
||||
the KeepassXC window a place on the desktop and autostarting it.
|
||||
|
||||
I am a bit puzzled about the host-apps referring to supporting
|
||||
browsers for which no extensions are available. This probably might
|
||||
warrant some additional investigation.
|
||||
|
||||
Big step forward
|
Loading…
Add table
Reference in a new issue