Skip to content

Instantly share code, notes, and snippets.

@edpichler
Forked from Falkor/git-crypt-rm-gpg-user.sh
Created February 17, 2020 20:13
Show Gist options
  • Save edpichler/7660b257b39a679dc3d6f7d853cfa0f9 to your computer and use it in GitHub Desktop.
Save edpichler/7660b257b39a679dc3d6f7d853cfa0f9 to your computer and use it in GitHub Desktop.
Git-crypt remove user.
#!/bin/bash
#
# Script to remove GPG key from 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 'remove-gpg-user.sh -h'
# remove-gpg-user.sh -l : list GPG keys configured within git-crypt for the current directory
# remove-gpg-user.sh -r <FULL-GPG-FINGERPRINT> : 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
# ~/bin/$(basename $0) -l # List configured GPG keys
# ~/bin/$(basename $0) -r <KEYID>
#
# 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
#################
set -eo pipefail
KEY_TO_REMOVE=
KEY_FINGERPRINT=
CMD_PREFIX=
###############
print_error_and_exit() { echo -e "*** ERROR *** $*\n Usage: see '$(basename $0) -h'"; exit 1; }
really_continue() {
echo -n -e "/!\\ WARNING: $*\n Are you sure you want to continue? [Y|n]"
read ans
case $ans in
n*|N*) exit 1;;
esac
}
print_usage() {
cat <<EOF
Remove a given GPG key from a git-crypt enabled repository.
Usage:
$(basename $0) -l : list GPG keys configured within git-crypt
$(basename $0) [-n] -r <FULL-GPG-FINGERPRINT> : remove key from git-crypt configuration
Typical workflow:
cd /path/to/protected/repo
git checkout -b git-crypt-remove
~/bin/$(basename $0) -l # List configured GPG keys
~/bin/$(basename $0) -r <KEYID>
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] missing text argument"
local key=$1
local gpgid=$(gpg --list-keys --with-colons $key | awk -F: '/^pub:/ { print $5 }')
local 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
[ -z "${KEY_TO_REMOVE}" ] && print_error_and_exit "No key to remove has been indicated"
print_key_info "${KEY_TO_REMOVE}"
[ -z "${KEY_FINGERPRINT}" ] && print_error_and_exit "Unable to retrieve key fingerprint"
really_continue "About to remove the GPG Key ID ${KEY_TO_REMOVE} from Git-crypt configuration"
# 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 <<EOF
TMPDIR=${TMPDIR}
CURRENT_DIR=${CURRENT_DIR}
EOF
# Unlock the directory, we need to copy encrypted versions of the files
git crypt unlock
# Work on copy.
cp -rp $(pwd) $TMPDIR || true
pushd $TMPDIR/$BASENAME
git stash # stash potential typechange
# git status
# Remove encrypted files and git-crypt
git crypt status -e > ${TMPFILE}
if [ -s "${TMPFILE}" ]; then
awk '{print $2}' ${TMPFILE} | xargs rm
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
for keyfilename in `ls $CURRENT_DIR/.git-crypt/keys/default/0/*gpg`; 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 i in `awk '{print $2}' ${TMPDIR}/${BASENAME}/${TMPFILE}`; do
#cp -rp --parents $i $TMPDIR/$BASENAME;
#gcp -rp --parents $i $TMPDIR/$BASENAME;
rsync -rp -R $i $TMPDIR/$BASENAME;
done
cd $TMPDIR/$BASENAME
for i in `awk '{print $2}' ${TMPFILE}`; do
git add $i
done
git commit -m "New encrypted files" || true
popd
git crypt lock
git pull $TMPDIR/$BASENAME
rm -rf $TMPDIR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment