#!/bin/bash set -euo pipefail if [[ "$#" -gt 0 && ("$1" == "-h" || "$1" == "--help") ]]; then cat <&2 echo "ERROR: Unknown argument: $1" exit 1 fi ensure_age ensure_private_key old_private_key="${private_key}" old_public_key="${key_id}" shopt -s nullglob store_files=("${store}"/*.age) shopt -u nullglob secret_count="${#store_files[@]}" if [[ "${assume_yes}" != "true" ]]; then if [[ ! -t 1 ]]; then >&2 echo "ERROR: rotate requires interactive confirmation (use --yes to skip)" exit 1 fi >&2 echo "This will generate a new age key and re-encrypt ${secret_count} secret(s)." >&2 echo "Current public key: ${old_public_key}" >&2 printf "Continue? [y/N] " local_confirm="" read -r local_confirm &2 echo "Aborted." exit 1 fi fi staging="$(mktemp -d "${TMPDIR:-/tmp}/szecret-rotate.XXXXXX")" temp_private_key="$(mktemp -u "${TMPDIR:-/tmp}/szecret-rotate-key.XXXXXX")" temp_public_key="$(mktemp "${TMPDIR:-/tmp}/szecret-rotate-pub.XXXXXX")" cleanup() { rm -rf "${staging}" rm -f "${temp_private_key}" "${temp_public_key}" } trap cleanup EXIT age-keygen -o "${temp_private_key}" 2>/dev/null new_public_key="$(age-keygen -y "${temp_private_key}")" echo "${new_public_key}" > "${temp_public_key}" for secret_file in "${store_files[@]}"; do secret_name="$(basename "${secret_file}" .age)" age_decrypt "${old_private_key}" "${secret_file}" \ | age_encrypt "${temp_public_key}" "${staging}/${secret_name}.age" done for secret_file in "${store_files[@]}"; do secret_name="$(basename "${secret_file}" .age)" mv "${staging}/${secret_name}.age" "${secret_file}" done echo "${new_public_key}" > "${public_key}" installed_private_key="${private_key_dir}/${new_public_key}" mv "${temp_private_key}" "${installed_private_key}" temp_private_key="" chmod 600 "${installed_private_key}" 2>/dev/null || true cat <