diff --git a/edit-secrets b/edit-secrets index 149715e..b4d816c 100755 --- a/edit-secrets +++ b/edit-secrets @@ -18,91 +18,10 @@ umask 077 LAST_OUTPUT_FILENAME="/root/.last_edit-secrets_output" -if [[ "x${EDITOR}" != "x" ]]; then - declare -r REMOTE_EDITOR="${EDITOR}" -else - declare -r REMOTE_EDITOR='/usr/bin/vim.tiny' -fi +test -d /dev/shm && export TMPDIR='/dev/shm' -if [ "x$1" = "x" ]; then - echo "Syntax: $0 -l OR fqdn" - exit 1 -fi - -if [ "x$1" != "x-l" ]; then - host=$(echo $1 | sed -e 's!/*$!!') # remove trailing slashes - - if [ ! -d $host ]; then - echo "$0: No host-directory for '$host' found - execute in top-level cosmos dir" - exit 1 - fi - - # Execute this very script, on a remote host - TMPFILE=$(mktemp edit-secrets.$$.XXXXXXX) - if [ ! -f $TMPFILE ]; then - echo "$0: Failed creating temporary file" - exit 1 - fi - TMPFILE2=$(mktemp edit-secrets.$$.XXXXXXX) - if [ ! -f $TMPFILE2 ]; then - echo "$0: Failed creating temporary file" - exit 1 - fi - - trap "rm -f $TMPFILE $TMPFILE2" EXIT - - ssh -t root@$host EDITOR="${REMOTE_EDITOR}" /var/cache/cosmos/repo/edit-secrets -l - scp -q root@$host:$LAST_OUTPUT_FILENAME $TMPFILE - - if grep ^"STATUS=UPDATED" $TMPFILE > /dev/null; then - # extract the path of the file that should be updated in the Cosmos repo - save_to="${host}/overlay/etc/hiera/data/secrets.yaml.asc" - mkdir -p "`dirname $save_to`" - # extract the GPG output - perl -e '$a = 0; while (<>) { $a = 1 if ($_ =~ /-+BEGIN PGP MESSAGE-+/); - print $_ if $a; $a = 0 if ($_ =~ /-+END PGP MESSAGE-+/); }' < $TMPFILE > $TMPFILE2 - - if ! grep "END PGP MESSAGE" $TMPFILE2 > /dev/null; then - echo "$0: Failed extracting PGP output from file $TMPFILE into $TMPFILE2" - exit 1 - fi - # use cat to preserve permissions etc. - cat $TMPFILE > $save_to - git add $save_to - - echo "" - echo "$save_to updated" - echo "" - else - echo "" - echo "Not updated" - echo "" - fi - - rm $TMPFILE $TMPFILE2 - - exit 0 -fi - -# -# Local execution on a host -# - -SECRETFILE=/etc/hiera/data/secrets.yaml.asc -GNUPGHOME=/etc/hiera/gpg/ -export GNUPGHOME - -GPG=`which gpg2 || true` -if [ ! -x "$GPG" ]; then - GPG=`which gpg || true` - if [ ! -x "$GPG" ]; then - echo "$0: gpg2 or gpg not found" - exit 1 - fi -fi - -TMPFILE=$(mktemp --tmpdir=/dev/shm) -TMPFILE2=$(mktemp --tmpdir=/dev/shm) +TMPFILE=$(mktemp edit-secrets.XXXXXXXXXX) +TMPFILE2=$(mktemp edit-secrets.XXXXXXXXXX) if [ ! -f $TMPFILE ]; then echo "$TMPFILE" @@ -117,47 +36,239 @@ fi trap "rm -f $TMPFILE $TMPFILE2" EXIT -if ! $GPG --list-secret-keys | grep -q ^"sec\s"; then - echo "$0: Secret key does not exist (in $GNUPGHOME)." - echo "" - echo "Generate it with /var/cache/cosmos/model/pre-tasks.d/040hiera-gpg" - echo "" + +if [[ ! $1 ]]; then + # deliberately don't mention the --on-host argument + echo "Syntax: $0 fqdn" exit 1 fi -if [ -s $SECRETFILE ]; then - $GPG -d $SECRETFILE > $TMPFILE -fi +function patch_broken_eyaml { + # + # Ubuntu 22.04 (jammy) has a broken hiera-eyaml package, a bug report + # exists here: https://bugs.launchpad.net/ubuntu/+source/hiera-eyaml/+bug/1974059 + # -cp $TMPFILE $TMPFILE2 -sensible-editor $TMPFILE -rm -f ${TMPFILE}~ ${TMPFILE2}~ + if [ "$(lsb_release -cs)" == "jammy" ]; then + plugins_file="/usr/share/rubygems-integration/all/gems/hiera-eyaml-3.2.2/lib/hiera/backend/eyaml/plugins.rb" + if [ -f $plugins_file ]; then + # We only want to try patching the file if it is the known broken version + bad_sum="1d0f14765ebcfcdae300d8ac5d715845ef9b283345d19114a23d96161556618f" + sum=$(sha256sum $plugins_file | awk '{print $1}') + if [ "$sum" == "$bad_sum" ]; then + patch --fuzz=0 --directory=/ --strip=0 <<'EOF' +--- /usr/share/rubygems-integration/all/gems/hiera-eyaml-3.2.2/lib/hiera/backend/eyaml/plugins.rb.orig 2023-01-18 08:20:22.140338419 +0000 ++++ /usr/share/rubygems-integration/all/gems/hiera-eyaml-3.2.2/lib/hiera/backend/eyaml/plugins.rb 2023-01-18 08:21:05.654053501 +0000 +@@ -32,6 +32,7 @@ + specs = Gem::VERSION >= "1.6.0" ? source.latest_specs(true) : source.latest_specs -echo "" -echo "" + specs.each do |spec| ++ spec = spec.to_spec if spec.respond_to?(:to_spec) + next if @@plugins.include? spec -status=0 -cmp -s $TMPFILE $TMPFILE2 || status=1 -if [ $status -eq 0 ]; then - ( - echo "STATUS=NOT_CHANGED" - ) > $LAST_OUTPUT_FILENAME - echo "" - echo "$0: No changes detected" -else - # figure out this hosts gpg key id - recipient=$($GPG --list-secret-key | grep ^sec | head -1 | awk '{print $2}' | cut -d / -f 2) + dependency = spec.dependencies.find { |d| d.name == "hiera-eyaml" } +EOF + fi + fi + fi +} - save_to="`hostname --fqdn`/overlay${SECRETFILE}" - echo "" - ( - echo "STATUS=UPDATED" +function edit_copy_and_commit() +{ + # + # This code runs on the administrators local machine + # + local host=$1 + + if [[ ${EDITOR} ]]; then + declare -r REMOTE_EDITOR="${EDITOR}" + else + declare -r REMOTE_EDITOR='/usr/bin/vim.tiny' + fi + + # Execute this script, on a remote host + ssh -t root@"${host}" EDITOR="${REMOTE_EDITOR}" /var/cache/cosmos/repo/edit-secrets --on-host + scp -q root@"${host}:${LAST_OUTPUT_FILENAME}" ${TMPFILE} + + local save_to + if grep ^"STATUS=UPDATED" $TMPFILE > /dev/null; then + save_to="${host}/overlay/etc/hiera/data/secrets.yaml.asc" + + # extract the GPG output + perl -e '$a = 0; while (<>) { $a = 1 if ($_ =~ /-+BEGIN PGP MESSAGE-+/); + print $_ if $a; $a = 0 if ($_ =~ /-+END PGP MESSAGE-+/); }' < $TMPFILE > $TMPFILE2 + + if ! grep "END PGP MESSAGE" $TMPFILE2 > /dev/null; then + echo "$0: Failed extracting PGP output from file $TMPFILE into $TMPFILE2" + exit 1 + fi + elif grep ^"STATUS=EYAML_UPDATED" $TMPFILE > /dev/null; then + save_to="${host}/overlay/etc/hiera/data/local.eyaml" + + # extract the eyaml output + perl -e '$a = 0; while (<>) { $a = 1 if ($_ =~ /^---$/); + print $_ if $a }' < $TMPFILE > $TMPFILE2 + + if ! grep "^---$" $TMPFILE2 > /dev/null; then + echo "$0: Failed extracting yaml output from file $TMPFILE into $TMPFILE2" + exit 1 + fi + else echo "" - ) > $LAST_OUTPUT_FILENAME - $GPG --output - --armor --recipient $recipient --sign --encrypt $TMPFILE >> $LAST_OUTPUT_FILENAME + echo "Not updated" + echo "" + + exit 0 + fi + + # use cat to preserve permissions etc. + mkdir -p "`dirname ${save_to}`" + cat $TMPFILE2 > "${save_to}" + git add "${save_to}" + + if grep ^"STATUS=EYAML_UPDATED" $TMPFILE > /dev/null; then + git diff --cached "${save_to}" + fi + echo "" - echo "GPG output saved in $LAST_OUTPUT_FILENAME - save it in Cosmos as" + echo "$save_to updated" echo "" - echo " $save_to" + + exit 0 +} + +function edit_file_on_host() { + # + # Local execution on a host + # + + local SECRETFILE=/etc/hiera/data/secrets.yaml.asc + local EYAMLFILE=/etc/hiera/data/local.eyaml + + if [ -f "${EYAMLFILE}" ]; then + edit_eyaml_file ${EYAMLFILE} + elif [ -f "${SECRETFILE}" ]; then + edit_gpg_file ${SECRETFILE} + elif [ -f /etc/hiera/eyaml/public_certkey.pkcs7.pem ]; then + # default to eyaml if the key exists and none of the secrets-file above exist + touch ${EYAMLFILE} + edit_eyaml_file ${EYAMLFILE} + fi +} + +function edit_gpg_file() +{ + local SECRETFILE=$1 + + GNUPGHOME=/etc/hiera/gpg/ + export GNUPGHOME + + local GPG=`which gpg2 || true` + if [ ! -x "$GPG" ]; then + GPG=`which gpg || true` + if [ ! -x "$GPG" ]; then + echo "$0: gpg2 or gpg not found" + exit 1 + fi + fi + + if ! $GPG --list-secret-keys | grep -q ^"sec\s"; then + echo "$0: Secret key does not exist (in $GNUPGHOME)." + echo "" + echo "Generate it with /var/cache/cosmos/model/pre-tasks.d/040hiera-gpg" + echo "" + exit 1 + fi + + if [ -s $SECRETFILE ]; then + $GPG -d $SECRETFILE > $TMPFILE + fi + + cp $TMPFILE $TMPFILE2 + sensible-editor $TMPFILE + rm -f ${TMPFILE}~ ${TMPFILE2}~ + echo "" + echo "" + + local status=0 + cmp -s $TMPFILE $TMPFILE2 || status=1 + if [ $status -eq 0 ]; then + ( + echo "STATUS=NOT_CHANGED" + ) > $LAST_OUTPUT_FILENAME + echo "" + echo "$0: No changes detected" + else + # figure out this hosts gpg key id + if lsb_release -r | grep -qE '(18|20).04'; then + recipient=$($GPG --list-secret-keys | grep -A1 '^sec' | tail -1 | awk '{print $1}') + else + recipient=$($GPG --list-secret-key | grep ^sec | head -1 | awk '{print $2}' | cut -d / -f 2) + fi + + save_to="`hostname --fqdn`/overlay${SECRETFILE}" + echo "" + ( + echo "STATUS=UPDATED" + echo "" + ) > $LAST_OUTPUT_FILENAME + $GPG --output - --armor --recipient $recipient --sign --encrypt $TMPFILE >> $LAST_OUTPUT_FILENAME + echo "" + echo "GPG output saved in $LAST_OUTPUT_FILENAME - save it in Cosmos as" + echo "" + echo " $save_to" + echo "" + fi +} + +function edit_eyaml_file() +{ + local EYAMLFILE=$1 + + local FQDN=$(hostname --fqdn) + local privkey='/etc/hiera/eyaml/private_key.pkcs7.pem' + local pubkey='/etc/hiera/eyaml/public_certkey.pkcs7.pem' + for f in $privkey $pubkey; do + test -f "${f}" || { echo "$0: eyaml key file ${f} not found"; exit 1; } + done + + patch_broken_eyaml + + # save source file for comparision afterwards + cp "${EYAMLFILE}" "${TMPFILE}" + eyaml edit --pkcs7-private-key "${privkey}" --pkcs7-public-key "${pubkey}" "${EYAMLFILE}" + + local status=0 + cmp -s "${EYAMLFILE}" $TMPFILE || status=1 + if [ $status -eq 0 ]; then + ( + echo "STATUS=NOT_CHANGED" + ) > $LAST_OUTPUT_FILENAME + echo "" + echo "$0: No changes detected" + else + echo "" + ( + echo "STATUS=EYAML_UPDATED" + echo "" + ) > $LAST_OUTPUT_FILENAME + cat "${EYAMLFILE}" >> $LAST_OUTPUT_FILENAME + fi +} + + +if [[ $1 == '--on-host' ]]; then + edit_file_on_host +else + host=$(echo $1 | sed -e 's!/*$!!') # remove trailing slashes + + if [ ! -d $host ]; then + echo "$0: No host-directory for '$host' found - execute in top-level cosmos dir" + exit 1 + fi + + edit_copy_and_commit $host fi + +exit 0