Getting caching for templates using pass
#4903
Nicholas42
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
I am using the
passpassword manager withchezmoito add secrets to a few files that need them. There is a nice standard way for this with thepasstemplate function. However, thenpassgets called whenever I runchezmoi statusor something similar. Of course,chezmoineeds to know if the password changed, but withpass, you don't actually need to decrypt the password for this, since every password is a separate, encrypted file.So, what we want here is a file that gets cache-invalidated if the encrypted file changes, but when it gets recreated runs the decryption. There is hacky way to do this with
chezmoi:passcommand being run in the second stagerun_onchangeto be executed when the first stage template changesThe first stage template does not need to decrypt the password, since it just uses a file hash. The second stage template needs to decrypt the password, but is only run when the file hashes change.
It gets a bit tricky to get all of this right, such that it works well, but I think I have a pretty solid way, now. It is based on a few template helpers and a script
chezmoi-templatize-changedthat is inserted as a shebang by the first template. Those basically do what it says on the tin and I include them at the end.Now let's look at an example. I have a wireguard config with a private and pre-shared key in
passunderwireguard/vpn.keyandwireguard/vpn.pskrespectively. In chezmoi, we storedot_config/wireguard/run_onchange_vpn.conf.tmpl{{ includeTemplate "shebang-templatize-changed.tmpl" (merge . (dict "private" true)) -}} {{ includeTemplate "pass-hashes.tmpl" (merge . (dict "passwords" (dict "key" "wireguard/vpn.key" "psk" "wireguard/vpn.psk" ))) -}} [Interface] PrivateKey = [[ pass $passwords.key ]] Address = 10.0.0.2/24 [Peer] PublicKey = asdf PresharedKey = [[ pass $passwords.psk ]] Endpoint = endpoint.mc.endpoint.face AllowedIPs = 0.0.0.0/0, ::/0 # Route all traffic through vpnWhen this template is executed, it will result in the following output:
Note the
[[and]]here instead of{{and}}. This is set inchezmoi-templatize-changedsuch that we don't need to escape them everywhere. Be careful to only callpassin square brackets! This file is normally only created internally and you don't see it.As you can see, there are hashes for the password files (sorted by their filenames to make it deterministic) and a hash for the target file. Hence, the template changes if any of those files change and the script is run again. If those files do not change, the template is evaluated (which does not need to run
pass) but the script is not run.Adding the target file hash is necessary, otherwise
chezmoiwill not care if that file was deleted or changed. However,this will also have the effect when changing the password, the second run ofchezmoi applyalso executes the template, since the dependency on the output changed. For me, this is not that much of a problem, but it can be annoying.If you now execute the file above, you run it through
chezmoi-templatize-changedand create/home/me/.config/wireguard/vpn.confHere the necessary files:
~/.local/share/chezmoi/.chezmoitemplates/hash-file.tmpl{{- /* Hash file at given path, fail with reasonable error message if it doesn't exist */ -}} {{ if (stat .) }}{{ include . | sha256sum }}{{ else }}{{ fail (printf "Error trying to hash %s: file does not exist" .)}}{{ end -}}~/.local/share/chezmoi/.chezmoitemplates/pass-file-path.tmpl{{- /* return path in filesystem for a given pass path it is important that there are no extraneous whitespaces in this template */ -}} {{ coalesce (env "PASSWORD_STORE_DIR") (expandenv "$HOME/.password-store") }}/{{print . ".gpg" -}}~/.local/share/chezmoi/.chezmoitemplates/pass-hashes.tmpl{{- /* insert hashes of password files and pass them to second stage template. Expects a dictionary of passwords and */ -}} [[- /* this will ensure that nothing of the following is included in the final state {{ range (values .passwords | sortAlpha)}} pass hash: {{ includeTemplate "hash-file.tmpl" (includeTemplate "pass-file-path.tmpl" .) }} {{ end }} {{ includeTemplate "safe-hash-file.tmpl" .chezmoi.targetFile }} */ -]] {{- includeTemplate "transfer-variables.tmpl" (dict "passwords" .passwords) -}}~/.local/share/chezmoi/.chezmoitemplates/safe-hash-file.tmpl{{- /* hash file at given path, return random ash if it does not exist (non-existing file should always be a cache-miss) */ -}} {{ if (stat .) }}{{ include . | sha256sum }}{{ else }}{{ randAscii 24 | sha256sum }}{{ end -}}~/.local/share/chezmoi/.chezmoitemplates/shebang-templatize-changed.tmpl{{- /* Add a shebang that calls chezmoi-templatize-changed as expected */ -}} #!/bin/env -S chezmoi-templatize-changed {{ .chezmoi.targetFile | quote }} {{ if (get . "private") }}--private{{ end }} {{ if (get . "readonly") }}--readonly{{ end }}~/.local/share/chezmoi/.chezmoitemplates/transfer-value.tmpl{{- /* escape value in a form that can be reused in a template */ -}} ({{ . | mustToJson | quote }} | mustFromJson){{- /* remove following newline */ -}}~/.local/share/chezmoi/.chezmoitemplates/transfer-variables.tmpl{{- /* Output a dict of variables in a way that can then be used by chezmoi-templatize-changed */ -}} {{ range $name, $value := . -}} [[ ${{ $name }} := {{ includeTemplate "transfer-value.tmpl" $value }} ]] {{- end -}}~/.bin/chezmoi-templatize-changedNote:
~/.bin/is in my$PATHBeta Was this translation helpful? Give feedback.
All reactions