You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
communication-keys/bin/update_sops.sh

132 lines
4.5 KiB
Bash

#/usr/bin/env bash
# Purpose: manage .sops.yaml based on gpg keys in the same dir _and_ verify correct configuration
set -euo pipefail
# OPTIONS AND ARGPARSING
# assume location of script as running directly from repo with keys (instead of as a standalone packaged tool)
keyfiles_dir="$(realpath $(dirname "${BASH_SOURCE[0]}")/..)"
# assume location of secrets config file in pwd
sops_config_dir="${PWD}"
# optional: secrets files to be updated
secrets_file_list=()
while (( $# >= 1 ));do
cur="${1}";
case $cur in
-k|--key|--keyfiles) keyfiles_dir="${2}"; shift ;;
-c|--config_dir) sops_config_dir="${2}"; shift ;;
-s|--secrets_file|-f|--file) secrets_file_list+=( "${2}" ); shift ;;
*) secrets_file_list+=( "${cur}" )
esac
shift;
done
keyfiles_dir="$(realpath "${keyfiles_dir}")"
test -d "${keyfiles_dir}" || (echo "E: specify dir containing keyfiles; invalid dir: '${keyfiles_dir}'" && exit 1)
sops_config_dir="$(realpath "${sops_config_dir}")"
test -d "${sops_config_dir}" || (echo "E: specify dir containing .sops.yaml, invalid dir: '${sops_config_dir}'" && exit 1)
sops_config="${sops_config_dir}/.sops.yaml"
test -e "${sops_config}" || (echo "E: could not locate .sops.yaml, tried ${sops_config}" && exit 1)
if [[ "${#secrets_file_list[@]}" != "0" ]]; then
for secrets_file in "${secrets_file_list[@]}"; do
test -e "${secrets_file}" || (echo "E: could not locate file with secrets, tried: ${secrets_file}" && exit 1)
done
fi
# /OPTIONS AND ARGPARSING
function fn_extract_fpr(){
gpgkeyfile=$1;shift;
# fingerprint
# caveat: restrict to netgo.de email, use-case:
# uid ... <...@mehrwerk.net>
# uid ... <...@netgo.de>
# fancy gpg src: https://unix.stackexchange.com/a/731872
fpr="$(gpg --show-keys --list-options show-only-fpr-mbox "${gpgkeyfile}" | grep '@netgo.de' | awk "{print \$1}")"
echo "${fpr}"
}
function fn_extract_uid(){
gpgkeyfile=$1;shift;
# user id
# caveat: restrict to netgo.de email, use-case:
# uid ... <...@mehrwerk.net>
# uid ... <...@netgo.de>
# fancy gpg src: https://unix.stackexchange.com/a/731872
uid="$(gpg --show-keys --with-colons "${gpgkeyfile}" | awk -F':' '$1=="uid" {print $10}' | grep '@netgo.de')"
echo "${uid}"
}
function fn_update_sops_config(){
# sops.yaml doc: https://github.com/getsops/sops?tab=readme-ov-file#using-sops-yaml-conf-to-select-kms-pgp-and-age-for-new-files
# CAVEAT: dirty hacks, as DRY as feasible within bash
# hack: 2D list workaround, i.e. difficult to have list-of-lists
fpr_list=()
uid_list=()
type_list=()
for gpgkeyfile in *automation*gpg.pub; do
type_list+=( "autom" )
fpr_list+=( "$(fn_extract_fpr "${gpgkeyfile}")" )
uid_list+=( "$(fn_extract_uid "${gpgkeyfile}")" )
done
for gpgkeyfile in $(ls *gpg.pub | grep -v automation); do
type_list+=( "human" )
fpr_list+=( "$(fn_extract_fpr "${gpgkeyfile}")" )
uid_list+=( "$(fn_extract_uid "${gpgkeyfile}")" )
done
# header
echo "# Fingerprint | User Type | User ID"
# entries/rows
for ind in "${!fpr_list[@]}"; do
printf "# %s | %s | %s\n" \
"${fpr_list[${ind}]}" \
"${type_list[${ind}]}" \
"${uid_list[${ind}]}"
done
echo "# keys in https://git.dev-at.de/smardigo-hetzner/communication-keys"
cat <<EOM
creation_rules:
# list of keys for encryption in stage
- pgp: >-
EOM
# all but last line get comma
ind_2nd_last=$((${#fpr_list[@]} - 1))
for fpr in ${fpr_list[@]:0:${ind_2nd_last}}; do
echo " ${fpr},"
done
# last line no comma
# echo " ${fpr_list[-1]}," # requires bash v4.1
echo " ${fpr_list[${ind_2nd_last}]}"
}
# UPDATE SOPS CONFIG
# TODO: remove the 'pushd;popd' workaround and make the functions aware of the dir being read
pushd "${keyfiles_dir}" > /dev/null 2>&1
(fn_update_sops_config) > "${sops_config}"
popd > /dev/null 2>&1
# VERIFY
fn_sops_updatekeys_and_verify(){
sops_enc_file="${1}";shift;
# update keys in secrets file
test -e "${sops_enc_file}" || exit 1
# "update the keys of SOPS files using the config file"
sops updatekeys "${sops_enc_file}"
# verify: dump secrets, GPG_TTY src: https://www.varokas.com/secrets-in-code-with-mozilla-sops/
GPG_TTY=$(tty) sops -d "${sops_enc_file}"
}
if [[ "${#secrets_file_list[@]}" != "0" ]]; then
for secrets_file in "${secrets_file_list[@]}"; do
fn_sops_updatekeys_and_verify "${secrets_file}"
done
echo "# SUCESS: all users with keys in this dir should have functional keys"
else
echo "# WARN: no secrets file passed in, make sure to call 'sops updatekeys' on secrets files"
fi