#!/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 <"${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}" while read encrypted_file; do rsync -rp -R "${encrypted_file}" "${TMPDIR}/${BASENAME}" done < ${TMPDIR}/${BASENAME}/${TMPFILE} cd "${TMPDIR}/${BASENAME}" while read encrypted_file; do git add "${encrypted_file}" done < ${TMPDIR}/${BASENAME}/${TMPFILE} git commit -m "New encrypted files" || true popd git crypt lock git pull "${TMPDIR}/${BASENAME}" rm -rf -- "${TMPDIR}"