For a long time I’ve used pass for password management.
I still believe it’s a good tool however my workflow changed and I needed suited for it.
pass
helped me keep unique passwords for a lot of services and tools.
I loved the clipboard integration and it’s use from the command line (I use the terminal).
However I did find it difficult to use in the browser and on other systems - like my mobile phone.
Another thing that bothered me was lack of One Time Password and TOTP integration.
That is why I searched for a replacement tool. And that is why now I’m migrating away from pass.
I’m using KeePassXC for some time now. While it does not fit perfectly into my workflow it does look more promising:
It has OTP/TOTP support
A single file database that you can copy around (that might be a good or a bad thing)
A way to share passwords with multiple devices including the browser and the phone
Ability to use hardware tokens to unlock the database
What I do miss right now is:
An easier way to use passwords in CLI apps. I like the way pass works: pass path/to/pw-entry
, it would be nice to have this flow in keepassxc-cli
.
An easy way to store files - to be able to share a secret database inside a team via git projects.
To make the migration I needed a way to export password data from pass and import it into KeePassXC. KeePassXC supports Importing CSV files so this was the way to go. All I needed was a way to convert the gpg encrypted files into a CSV and import that.
The migration process looks like this:
Export passwords from pass into a CSV file
Import them into KeePassXC
To export the passwords I chose to write a script in babashka.
For those of you who don’t know, babashka
is a tool to write using scripts in Clojure.
It also has a very cool logo .
For me it is a way to avoid writing bash scripts, because I find bash to be less fun to write and also because I wanted to test out babashka
.
The full source code is bellow and you can copy and paste it locally to use it.
#!/usr/bin/env bb
(ns script
(:require [clojure.java.io :as io]
[clojure.string :as str]
[clojure.pprint :as pp]
[clojure.edn :as edn]
[cheshire.core :as json]
[clojure.data.csv :as csv]
[clojure.java.shell :refer [sh]]))
;;
;; Iterez peste directoare / fișiere
;; pass pe fiecare
;; Creez un csv pentru KeePass
;;
;; https://github.com/borkdude/babashka
;; https://book.babashka.org/#style
;; https://book.babashka.org/#child_processes
;; http://clojure-doc.org/articles/cookbooks/files_and_directories.html
;; https://clojuredocs.org/clojure.core/file-seq
;; https://github.com/borkdude/babashka/blob/master/doc/projects.md
;;
;; https://keepassxc.org/docs/KeePassXC_UserGuide.html#_importing_csv_file
;;
(def home (System/getProperty "user.home"))
(def pstore-dir (str home "/.password-store/"))
(defn process-password-store [pstore-dir]
(let [files (file-seq (io/file pstore-dir))
gpg-filter (fn gpg-filter [f] (.contains (.toString f) ".gpg"))
gpg-files (filter gpg-filter files)]
;; process all files - read the contents and produce a list of entries
(for [f gpg-files
:let [f-str (.toString f)
pass-arg (-> f-str
(.substring (.length pstore-dir))
(.replace ".gpg" ""))
data (sh "/usr/bin/pass" pass-arg)
lines (str/split-lines (:out data))
pw (first lines)
other (rest lines)]]
{:file f-str
:data data
:out (:out data)
:pass-arg pass-arg
:password pw
:other other})))
(defn pwstore-edn->csv-table [elements]
(let [convert (fn convert [e] (let [{:keys [password pass-arg other]} e
notes (str/join "||" other)]
["pass" password pass-arg notes]))]
(map convert elements)))
(println "Processing files in " pstore-dir)
(def processed (process-password-store pstore-dir))
(spit "password-store-export.edn" (with-out-str (pp/pprint processed)))
(spit "password-store-export.json" (with-out-str (json/generate-string processed)))
(println "Done extracting files. Convert edn file to csv.")
(def csv-data (pwstore-edn->csv-table (edn/read-string (slurp "password-store-export.edn"))))
(with-open [writer (io/writer "password-store-export.csv")]
(csv/write-csv writer csv-data :quote? true))
Save it into a file passwordstore-export.sh
, make it executable and run it.
chmod +x passwordstore-export.sh
./passwordstore-export.sh
The file contains two functions and produces 3 files: an edn, a csv and a json file with mostly the same data.
The function process-password-store
iterates over all the files in the user’s .password-store/
directory calling pass
for each file.
This function produces a list of maps, one for each file with all the data.
The function pwstore-edn→csv-table
takes the data produced by the first function and produces a list of lists, suitable for CSV export.
The data is written in each file format at the end.
Importing the data in KeePassXC was pretty trivial - I just followed the online guide.
Developing the script was pleasant and I will use babashka
for more scripts.
I recommend you give it a try yourself.