From eb5c12da4b0545ea9b0f1a5f5dcfcb0c57461f1d Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Tue, 27 Feb 2024 20:08:26 +0300 Subject: [PATCH] packages: add git-crypt-rm-gpg-user Source: https://gist.github.com/Falkor/7b29f16f5f79404fe41476be0d992783 --- home/users/jan/default.nix | 4 + packages/git-crypt-rm-gpg-user/default.nix | 5 + .../git-crypt-rm-gpg-user.sh | 229 ++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 packages/git-crypt-rm-gpg-user/default.nix create mode 100644 packages/git-crypt-rm-gpg-user/git-crypt-rm-gpg-user.sh diff --git a/home/users/jan/default.nix b/home/users/jan/default.nix index 8e2cd23..d48018f 100644 --- a/home/users/jan/default.nix +++ b/home/users/jan/default.nix @@ -49,6 +49,10 @@ in ]; home.stateVersion = config.system.stateVersion; + + home.packages = [ + (pkgs.callPackage ../../../packages/git-crypt-rm-gpg-user { }) + ]; }; nix.settings.trusted-users = lib.mkAfter [ "jan" ]; diff --git a/packages/git-crypt-rm-gpg-user/default.nix b/packages/git-crypt-rm-gpg-user/default.nix new file mode 100644 index 0000000..ca8bd65 --- /dev/null +++ b/packages/git-crypt-rm-gpg-user/default.nix @@ -0,0 +1,5 @@ +{ writeScriptBin }: + +writeScriptBin + "git-crypt-rm-gpg-user" + (builtins.readFile ./git-crypt-rm-gpg-user.sh) diff --git a/packages/git-crypt-rm-gpg-user/git-crypt-rm-gpg-user.sh b/packages/git-crypt-rm-gpg-user/git-crypt-rm-gpg-user.sh new file mode 100644 index 0000000..70aa1cb --- /dev/null +++ b/packages/git-crypt-rm-gpg-user/git-crypt-rm-gpg-user.sh @@ -0,0 +1,229 @@ +#!/usr/bin/env bash +# +# Script to remove GPG user (recipient) with git-crypt +# +# It will re-initialize git-crypt for the repository and re-add all keys except +# the one requested for removal. +# +# Note: You still need to change all your secrets to fully protect yourself. +# Removing a user will prevent them from reading future changes but they will +# still have a copy of the data up to the point of their removal. +# +# Usage: see 'git-crypt-rm-gpg-user.sh -h' +# git-crypt-rm-gpg-user.sh -l : list GPG keys configured within git-crypt for the current directory +# git-crypt-rm-gpg-user.sh -r : remove specified key from git-crypt configuration +# Ex: remove-gpg-user.sh -r 3BC18383F838C0B815B961480F8CAF5467D +# +# Typical workflow (assuming the script is placed in ~/bin/) : +# +# cd /path/to/protected/repo +# git checkout -b git-crypt-remove +# git-crypt-rm-gpg-user.sh -l # List configured GPG keys +# git-crypt-rm-gpg-user.sh -r +# git push origin git-crypt-remove # publish the branch +# +# Merge (sync with your collaborators for the appropriate timing): +# git checkout master # or devel or whatever main branch +# git-crypt lock +# git merge git-crypt-remove +# # release the repository +# +# Pulling for your collaborators +# cd /path/to/protected/repo +# git-crypt lock +# git pull origin +# +# You can check the full, 64-bit (8-byte) key ID for a key within your keyring with +# gpg -k [email | pattern] +# gpg -k --with-colons [email | pattern | id] | awk -F: '/^pub:/ { print $5 }' +# See https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob_plain;f=doc/DETAILS +# +# The script will create multiple commits to your repo. Feel free to squash them +# all down to one. +# +# Based on https://github.com/AGWA/git-crypt/issues/47#issuecomment-212734882 +# +# +# GIST source https://gist.github.com/glogiotatidis/e0ab45ed5575a9d7973390dace0552b0 +# amended to work on Mac OS and with cosmetic changes for a more convenient interface in +# https://gist.github.com/Falkor/7b29f16f5f79404fe41476be0d992783 +# Script is now shellcheck compliant and safer: https://gist.github.com/thomsh/ed14fa82cf43a6b283c1eea9574fb76e +################# +set -eo pipefail + +KEY_TO_REMOVE= +KEY_FINGERPRINT= +# CMD_PREFIX= + +# HELPERS +print_error_and_exit () { + echo "*** ERROR *** $*" + echo "Usage: see '$(basename $0) -h'" + exit 1 +} + +really_continue() { + echo "/!\\ WARNING: $*" + echo "Are you sure you want to continue? [Y|n]" + read -r ans + case $ans in + n*|N*) exit 1;; + esac +} + +print_usage () { + cat < : remove key from git-crypt configuration + +Typical workflow: + + cd /path/to/protected/repo + git checkout -b git-crypt-remove + $(basename $0) -l # List configured GPG keys + $(basename $0) -r + +You can check the full, 64-bit (8-byte) key ID for a key within your keyring with + gpg -k [email | pattern] + gpg -k --with-colons [email | pattern | id] | awk -F: '/^pub:/ { print \$5 }' + +See https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob_plain;f=doc/DETAILS +EOF +} + +### +# Print info for a given key from your keyring +## +print_key_info() { + [ -z "${1:-}" ] && print_error_and_exit "${FUNCNAME[0]} missing text argument" + local key + local gpgid + local fpr + key="$1" + gpgid="$(gpg --list-keys --with-colons "${key}" | awk -F: '/^pub:/ { print $5 }')" + fpr="$(gpg -k --with-colons "${key}" | awk -F: '/^fpr:/ { print $10; exit; }')" # Only first fpr + echo "==== Key Fingerprint: ${fpr} ===" + echo "==== Long GPG ID: ${gpgid}" + gpg --list-key "$key" || print_error_and_exit "Couldn't find info about $key" + export KEY_FINGERPRINT="$fpr" +} + +### +# List GPG keys configured for protecting this repository +## +list_git_crypt_keys() { + for f in .git-crypt/keys/default/0/*.gpg; do + # key="$(basename "${f}" .gpg)" + print_key_info "$(basename "${f}" .gpg)" + # gpgid=$(gpg --list-keys --with-colons $key | awk -F: '/^pub:/ { print $5 }') + # echo "==== Key Fingerprint: ${key} ===" + # echo "==== Long GPG ID: ${gpgid}" + # gpg --list-key "$key" || print_error_and_exit "Couldn't find info about $key" + done +} + +##### Let's go ##### +# Check for options +while [ $# -ge 1 ]; do + case $1 in + -h | --help) print_usage; exit 0;; + -l | --list) list_git_crypt_keys; exit 0;; + -r | --remove) shift; KEY_TO_REMOVE=$1;; + # -n | --dry-run) CMD_PREFIX=echo ;; + esac + shift +done + +if [ -z "${KEY_TO_REMOVE:-}" ];then + print_error_and_exit "No key to remove has been indicated" +fi + +print_key_info "${KEY_TO_REMOVE}" + +if [ -z "${KEY_FINGERPRINT:-}" ]; then + print_error_and_exit "Unable to retrieve key fingerprint" +fi + +if [ -z "$(command -v rsync)" ]; then + print_error_and_exit "This script use rsync, sadly rsync is not found on your system" +fi + +really_continue "About to remove the GPG Key ID ${KEY_TO_REMOVE} from Git-crypt configuration" + +set -x # enable debug just in case we need to dig +set -euo pipefail # enforce mode with nounset (-u) + +# Below code adapted from @glogiotatidis - https://gist.github.com/glogiotatidis/e0ab45ed5575a9d7973390dace0552b0 +TMPDIR="$(mktemp -d)" +CURRENT_DIR="$(git rev-parse --show-toplevel)" +BASENAME="$(basename "$(pwd)")" +TMPFILE=list-encrypted-files.txt + +cat < "${TMPFILE}" +if [ -s "${TMPFILE}" ]; then + awk '{print $2}' ${TMPFILE} | xargs rm + #xargs -I {} -- rm -- {} < "${TMPFILE}" + git commit -a -m "Remove encrypted files" +fi +rm -rf -- .git-crypt +git commit -m "Remove git-crypt (in particular configuration related to key ${KEY_TO_REMOVE})" .git-crypt +rm -rf .git/git-crypt + +# Re-initialize git crypt +git crypt init + +# Add existing users, except the key to remove +find "${CURRENT_DIR}/.git-crypt/keys/default/0" -type f -iname '*gpg' -print|while read -r keyfilename +do + basename="$(basename "$keyfilename")" + key="${basename%.*}" + if [[ "$key" == "$KEY_FINGERPRINT" ]]; then + echo "ignoring key ${KEY_FINGERPRINT} to be removed" + continue + fi + # check if the key is expired - second field is 'e' + # See https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob_plain;f=doc/DETAILS + expired="$(gpg --with-colons --list-keys --with-fingerprint "$key" | awk -F: '/^pub:/ { print $2; }')" + if [[ "${expired}" == "e" ]]; then + echo "/!\\ WARNING: key $key expired thus not integrated in the git-crypt keyring" + echo "/!\\ WARNING: key details: " + print_key_info "$key" + continue + fi + git crypt add-gpg-user "$key" +done + +cd "${CURRENT_DIR}" +for encrypted_file in $(awk '{print $2}' ${TMPDIR}/${BASENAME}/${TMPFILE}); do + rsync -rp -R "${encrypted_file}" "${TMPDIR}/${BASENAME}" +done + +cd "${TMPDIR}/${BASENAME}" +for encrypted_file in $(awk '{print $2}' ${TMPFILE}); do + git add "${encrypted_file}" +done + +git commit -m "New encrypted files" || true +popd + +git crypt lock +git pull "${TMPDIR}/${BASENAME}" + +rm -rf -- "${TMPDIR}"