Managing dotfiles and secret with chezmoi
Every once in a while, you may need to bootstrap a new machine, and along with it to reconfigure home directory’s dot files. Various approaches already exist, using simple archive, git the home directory, gnu stow (symlinks), etc.
These approaches work more or less depending on your expectation, I wanted something
that chezmoi
touts:
the integration with password managers.
There are other features on the shelf too, like templating, and system bootstrap
facility.
Here I will only focus on the management of the dot files and of the secrets.
After you’ve got chezmoi
installed, you
need to initialize it
❯ chezmoi init
This will create a directory there ~/.local/share/chezmoi
and managed dotfiles will
land here, this folder is actually a git repository. This folder and the files in here
are referred to as the source files while the files in the home directory adn are
referred to as the target.
The basics
Then start adding the files you care about, e.g.
❯ chezmoi add .zshrc
❯ chezmoi add .config/alacritty/alacritty.yml
❯ chezmoi add .SpaceVim.d/init.toml
Then when ready commit the files, for that you need to go to the source
directory of chezmoi
and perform the git dance.
❯ chezmoi cd
❯ git st
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
dot_SpaceVim.d/autoload/myspacevim.vim
dot_SpaceVim.d/init.toml
dot_config/alacritty/alacritty.yml
dot_config/iterm/com.googlecode.iterm2.plist
dot_config/starship.toml
dot_config/topgrade.toml
dot_gitconfig
dot_gitignore-global
dot_gradle/init.d/checknetwork.gradle
dot_gradle/init.d/tasktree.gradle
dot_mrconfig
dot_p10k.zsh
dot_zshrc
❯ git add *
❯ git commit --message="Init config"
This repository can then be synchronized with a remote git repository.
Managing changes
Suppose the file $HOME/.zshrc
evolves a bit, like declaring another
oh-my-zsh plugin, in order to see
the difference between actual files, and the files that are backed up by
chezmoi
use the chezmoi diff
command
❯ chezmoi diff
install -m 644 /dev/null /Users/bric3/.zshrc
--- a/Users/bric3/.zshrc
+++ b/Users/bric3/.zshrc
@@ -85,7 +85,7 @@
gitfast
git-extras
- man osx
+ man
gradle mvn
kubectl helm docker
- The lines starting with a minus
-
comes from the target files, i.e. the files in the$HOME
directory. - The lines starting with a plus
+
comes from the source files, i.e. the files in thechezmoi
internal directory.
Knowing the above, this output can be understood as, the source file only have the man
plugin entry on that line, while the local file have man osx
plugin entries on that
same line.
Now let’s look at the output of two other commands:
-
chezmoi apply
will apply the source file to their local target, e.g. the.zshrc
file will converge to what’s in the source file, oncechezmoi apply
has been executed, this file will only declare theman
plugin.❯ chezmoi apply --verbose --dry-run ~/.zshrc install -m 644 /dev/null /Users/bric3/.zshrc --- a/Users/bric3/.zshrc +++ b/Users/bric3/.zshrc @@ -85,7 +85,7 @@ gitfast git-extras - man osx + man gradle mvn kubectl helm docker
-
chezmoi add
will do the opposite, it will change the source file.zshrc
from the local target file, i.e. after executingchezmoi add
, the source file will now have theman osx
entries.❯ chezmoi add --verbose --dry-run ~/.zshrc rm -rf /Users/bric3/.local/share/chezmoi/dot_zshrc install -m 644 /dev/null /Users/bric3/.local/share/chezmoi/dot_zshrc --- a/Users/bric3/.local/share/chezmoi/dot_zshrc +++ b/Users/bric3/.local/share/chezmoi/dot_zshrc @@ -85,7 +85,7 @@ gitfast git-extras - man + man osx gradle mvn kubectl helm docker
Note hoe the minus
-
and plus+
appears in the opposite change.
Handling secret with 1Password
So now that we have a basic setup and understanding of the tool, let’s manage files with secrets. Currently, I have some files that I’d rather keep protected, especially if I use a private repository on my git host be it GitHub, Gitlab or else.
For that we’ll need the chezmoi
templating feature, and the cli tool from
the password manager, in this blog post I’m using on 1Password, but check
the how-to documentation
for other password manager support like Bitwarden or Keypassx.
So I especially need to store securely files in my .ssh
folder and
my .gnupg
folders.
❯ chezmoi add .ssh/id_rsa_home.pub
Regarding SSH, id_rsa_home.pub
is a public key, so I’m just adding it raw, however
things get interesting for id_rsa
which is my private key.
❯ chezmoi add --template .ssh/id_rsa_home
❯ chezmoi add --template .ssh/config
❯ chezmoi add --template .gnupg/trustdb.gpg
❯ chezmoi add --template .gnupg/pubring.kbx
Here I’m telling chezmoi
to store id_rsa_home
as a template. While the stored
file template still contains the original:
Regarding GPG files,
chezmoi
supports GPG, but it’s used as way to encrypt secrets not to backup the GPG secrets. There’s indeed a way to extract these secret keys as non-binary files using a fewgpg
commands, via run scripts, however this break the declarative approach ofchezmoi
. So I chose to save the binary files, here onlytrustdb.gpg
andpubring.kbx
.
❯ chezmoi edit .ssh/id_rsa_home
# template is the same as the actual $HOME/.ssh/id_rsa_home
In order to tell make chezmoi
aware of 1Password for this template, I first need
to store the documents on 1Password. For that it is necessary to have a 1Password
subscription that lets you have an online vault.
First sign-in
❯ eval $(op signin my)
Then store documents using the 1Password cli tool op
❯ op create document .ssh/id_rsa --tags chezmoi --title .ssh/id_rsa
{"uuid":"ti2adie9Aixaidae4dahpoh5io","createdAt":"2020-04-01T17:53:49.596484+02:00","updatedAt":"2020-04-01T17:53:49.596484+02:00","vaultUuid":"eith2iequievuthae9Eedaiboh"}
❯ op create document .ssh/config --tags chezmoi --title .ssh/config
{"uuid":"pairahnietaluv5Moonahm2ea5","createdAt":"2020-04-01T17:54:36.402265+02:00","updatedAt":"2020-04-01T17:54:36.402265+02:00","vaultUuid":"eith2iequievuthae9Eedaiboh"}
❯ op create document .gnupg/trustdb.gpg --tags chezmoi --title .gnupg/trustdb.gpg
{"uuid":"zi8ieleiphieTithiep2xieg3u","createdAt":"2020-04-01T17:57:13.338949+02:00","updatedAt":"2020-04-01T17:57:13.338949+02:00","vaultUuid":"eith2iequievuthae9Eedaiboh"}
❯ op create document .gnupg/trustdb.gpg --tags chezmoi --title .gnupg/pubring.kbx
{"uuid":"losachuYeeho5Eiph2uzoquohl","createdAt":"2020-04-01T17:58:12.818754+02:00","updatedAt":"2020-04-01T17:58:12.818755+02:00","vaultUuid":"eith2iequievuthae9Eedaiboh"}
All UUIDs have been edited of course.
To complete this operation, we need to modify the templates files
where to get the file, since there’s a few binary files vim
is not suited
for that, so I’m using another mean to replace the content by the template.
❯ chezmoi cd
❯ echo -n '{{- onepasswordDocument "ti2adie9Aixaidae4dahpoh5io" -}}' > private_dot_gnupg/private_id_rsa.tmpl
❯ echo -n '{{- onepasswordDocument "pairahnietaluv5Moonahm2ea5" -}}' > private_dot_gnupg/config.tmpl
❯ echo -n '{{- onepasswordDocument "zi8ieleiphieTithiep2xieg3u" -}}' > private_dot_gnupg/private_trustdb.gpg.tmpl
❯ echo -n '{{- onepasswordDocument "losachuYeeho5Eiph2uzoquohl" -}}' > private_dot_gnupg/private_pubring.kbx.tmpl
❯ exit
Note that I’m replacing the binary file content of .gnupg/trustdb.gpg
and .gnupg/pubring.kbx
with the template character string {{- onepasswordDocument "uuid" -}}
.
Also, one downside with 1Password at this time, is that documents cannot be updated,
you need to remove the old document using the UUID and use the create
sub-command
on the new version of the file, you’ll get a new uuid and as such you’ll have to update
these template
Checking the templates
Eventually it’s possible to check the templating works by apply the source files to another target directory.
❯ chezmoi apply --verbose --destination /Users/bric3/tmphome --dry-run
One thing you may notice is that now, this is global command, and all global command will require the 1Password
op
session to be active, and you may have to run againeval $(op signin my)
as a pre-requisite.
If ready remove the --dry-run
, and see the tmphome
folder populated (after the
command has run because chezmoi
applies changes atomically).
❯ l /Users/bric3/tmphome
Permissions Size User Date Modified Name
drwxr-xr-x - bric3 2 Apr 2:54 .config
.rw-r--r-- 3.1k bric3 2 Apr 2:54 .gitconfig
.rw-r--r-- 2.4k bric3 2 Apr 2:54 .gitignore-global
drwx------ - bric3 2 Apr 2:55 .gnupg
drwxr-xr-x - bric3 2 Apr 2:55 .gradle
drwxr-xr-x - bric3 2 Apr 2:55 .kube
.rw-r--r-- 7.5k bric3 2 Apr 2:55 .mrconfig
.rw-r--r-- 51k bric3 2 Apr 2:55 .p10k.zsh
drwxr-xr-x - bric3 2 Apr 2:54 .SpaceVim.d
drwx------ - bric3 2 Apr 2:56 .ssh
.rw-r--r-- 7.4k bric3 2 Apr 2:56 .zshrc
Note, it’s possible to apply only a portion of the dot files in that temporary folder
just by adding the target absolute path /Users/bric3/tmphome/.gnupg/
❯ chezmoi apply --verbose --destination /Users/bric3/tmphome /Users/bric3/tmphome/.gnupg/
And what we wanted, make sure the templates are exactly the same as the original files
❯ b3sum /Users/bric3/tmphome/.gnupg/pubring.kbx /Users/bric3/.gnupg/pubring.kbx
1b51813215edef2e97846bfee51cd02dd8d6c2cb6a119b3681ac087597fb0197 /Users/bric3/tmphome/.gnupg/pubring.kbx
1b51813215edef2e97846bfee51cd02dd8d6c2cb6a119b3681ac087597fb0197 /Users/bric3/.gnupg/pubring.kbx
❯ b3sum /Users/bric3/tmphome/.gnupg/trustdb.gpg /Users/bric3/.gnupg/trustdb.gpg
d2c67bb808b223cc6f1b7c95b627b4b5551daa1312e12dd0ad3c5bfa1ac35dc9 /Users/bric3/tmphome/.gnupg/trustdb.gpg
d2c67bb808b223cc6f1b7c95b627b4b5551daa1312e12dd0ad3c5bfa1ac35dc9 /Users/bric3/.gnupg/trustdb.gpg
Looks good !
It’s not over
chezmoi
comes with some features that are a bit more involved, especially
the templating and the system bootstrap/configuration. As I’m not using them
for now I’ll leave it aside.
Now the only thing I’ll need to get my dotfiles is
❯ eval $(op signin my)
❯ chezmoi init --apply --verbose https://githost.tld/path/to/dotfiles.git
Additionally, in order to synchronize the dotfiles from upstream repository :
❯ eval $(op signin my)
❯ chezmoi source pull -- --rebase && chezmoi diff