mirror of
https://github.com/shokinn/.files.git
synced 2025-01-18 05:12:26 +00:00
add helper script for agenix
This commit is contained in:
parent
ff6a0ac663
commit
9095720835
|
@ -138,6 +138,9 @@ dotfiles:
|
||||||
f_secretfiles:
|
f_secretfiles:
|
||||||
dst: ~/.bin/secretfiles
|
dst: ~/.bin/secretfiles
|
||||||
src: bin/secretfiles
|
src: bin/secretfiles
|
||||||
|
f_agenix_helper:
|
||||||
|
dst: ~/.bin/agenix-helper
|
||||||
|
src: bin/agenix-helper
|
||||||
f_config:
|
f_config:
|
||||||
src: ssh/config
|
src: ssh/config
|
||||||
dst: ~/.ssh/config
|
dst: ~/.ssh/config
|
||||||
|
@ -225,6 +228,7 @@ profiles:
|
||||||
- f_rmquarantine
|
- f_rmquarantine
|
||||||
- f_commonfunc
|
- f_commonfunc
|
||||||
- f_secretfiles
|
- f_secretfiles
|
||||||
|
- f_agenix_helper
|
||||||
- f_config
|
- f_config
|
||||||
yoetunheimr:
|
yoetunheimr:
|
||||||
dotfiles:
|
dotfiles:
|
||||||
|
@ -304,6 +308,7 @@ profiles:
|
||||||
- f_commonfunc
|
- f_commonfunc
|
||||||
- f_gpgagent
|
- f_gpgagent
|
||||||
- f_secretfiles
|
- f_secretfiles
|
||||||
|
- f_agenix_helper
|
||||||
- f_config
|
- f_config
|
||||||
workspace:
|
workspace:
|
||||||
dotfiles:
|
dotfiles:
|
||||||
|
@ -337,4 +342,5 @@ profiles:
|
||||||
- f_rmquarantine
|
- f_rmquarantine
|
||||||
- f_commonfunc
|
- f_commonfunc
|
||||||
- f_secretfiles
|
- f_secretfiles
|
||||||
|
- f_agenix_helper
|
||||||
- f_config
|
- f_config
|
||||||
|
|
250
dotfiles/bin/agenix-helper
Executable file
250
dotfiles/bin/agenix-helper
Executable file
|
@ -0,0 +1,250 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# More safety, by turning some bugs into errors.
|
||||||
|
# Without `errexit` you don’t need ! and can replace
|
||||||
|
# ${PIPESTATUS[0]} with a simple $?, but I prefer safety.
|
||||||
|
set -euf -o pipefail
|
||||||
|
|
||||||
|
#---------------------------------------------------
|
||||||
|
#
|
||||||
|
# {{@@ header() @@}}
|
||||||
|
#
|
||||||
|
# age encryption / decryption helpers
|
||||||
|
# based on https://github.com/ryantm/agenix
|
||||||
|
#
|
||||||
|
# For macOS coreutils and gnu-getopt are required
|
||||||
|
# to run this script.
|
||||||
|
# brew install coreutils gnu-getopt
|
||||||
|
#
|
||||||
|
#---------------------------------------------------
|
||||||
|
|
||||||
|
#TMPPATH="/dev/shm"
|
||||||
|
TMPPATH="/tmp"
|
||||||
|
|
||||||
|
[[ -d "/opt/homebrew/opt/coreutils/libexec/gnubin" ]] && export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:${PATH}"
|
||||||
|
[[ -d "/opt/homebrew/opt/gnu-getopt/bin" ]] && export PATH="/opt/homebrew/opt/gnu-getopt/bin:${PATH}"
|
||||||
|
|
||||||
|
update_keys() {
|
||||||
|
local file="${1}"
|
||||||
|
local start_marker="${2}"
|
||||||
|
local end_marker="${3}"
|
||||||
|
local new_key="${4}"
|
||||||
|
local list_name="${5}"
|
||||||
|
local tmp_file=$(mktemp -p ${TMPPATH})
|
||||||
|
local content_file=$(mktemp -p ${TMPPATH})
|
||||||
|
|
||||||
|
local content_array=()
|
||||||
|
local content_array_unsorted=()
|
||||||
|
# Get current configured keys and save them to the array "content_array"
|
||||||
|
mapfile -t content_array_unsorted < <(awk "/${start_marker}/{f=1;next} /${end_marker}/{f=0} f" ${file})
|
||||||
|
# Add new key to the array "content_array"
|
||||||
|
content_array_unsorted+=("${new_key}")
|
||||||
|
# Sort content alphabetically
|
||||||
|
IFS=$'\n' content_array=($(sort <<<"${content_array_unsorted[*]}")); unset IFS
|
||||||
|
|
||||||
|
# Remove duplicates from the array
|
||||||
|
declare -A seen=()
|
||||||
|
unique_content_array=()
|
||||||
|
for item in "${content_array[@]}"; do
|
||||||
|
key="${item%%=*}" # Extract the key part
|
||||||
|
if [[ -z "${seen[$key]+unset}" ]]; then
|
||||||
|
unique_content_array+=("${item}")
|
||||||
|
seen[$key]=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Write the unique contents of the array to a temporary file
|
||||||
|
printf "%s\n" "${unique_content_array[@]}" > "${content_file}"
|
||||||
|
|
||||||
|
# Process the file to replace the keyword list and the block of text
|
||||||
|
awk -v start="${start_marker}" -v end="${end_marker}" -v content_file="${content_file}" -v keys="${!seen[*]}" -v list_name="${list_name}" '
|
||||||
|
BEGIN {
|
||||||
|
in_block = 0
|
||||||
|
split(keys, key_array, " ")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if ($0 ~ start) {
|
||||||
|
print
|
||||||
|
in_block = 1
|
||||||
|
while ((getline line < content_file) > 0) {
|
||||||
|
print line
|
||||||
|
}
|
||||||
|
close(content_file)
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if ($0 ~ end) {
|
||||||
|
in_block = 0
|
||||||
|
print
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if (!in_block) {
|
||||||
|
if ($0 ~ list_name " = \\[.*\\];") {
|
||||||
|
# Recreate the list_name list from the keys of unique_content_array
|
||||||
|
printf " %s = [ ", list_name
|
||||||
|
sep = ""
|
||||||
|
for (i in key_array) {
|
||||||
|
gsub(/^ +/, "", key_array[i]) # Remove leading spaces from keys
|
||||||
|
printf "%s%s", sep, key_array[i]
|
||||||
|
sep = " "
|
||||||
|
}
|
||||||
|
print " ];"
|
||||||
|
next
|
||||||
|
}
|
||||||
|
print
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "${file}" > "${tmp_file}"
|
||||||
|
|
||||||
|
# Move the temporary file to the original file
|
||||||
|
mv "${tmp_file}" "${file}"
|
||||||
|
rm "${content_file}"
|
||||||
|
}
|
||||||
|
|
||||||
|
gen-user-key() {
|
||||||
|
local keyname="${1}"
|
||||||
|
local public_key="${2}"
|
||||||
|
local working_directory="${3:-$(pwd)}"
|
||||||
|
local begin_marker='#-----BEGIN USER-SECRETS-----'
|
||||||
|
local end_marker='#------END USER-SECRETS------'
|
||||||
|
local input_file="${working_directory}/secrets.nix"
|
||||||
|
local userkey
|
||||||
|
|
||||||
|
if [[ ${public_key} == "EMPTY" ]]; then
|
||||||
|
echo "generating new keys for host ${keyname}";
|
||||||
|
ssh-keygen \
|
||||||
|
-t ed25519 \
|
||||||
|
-f ~/.ssh/${keyname} \
|
||||||
|
-C "agenix@${keyname}" \
|
||||||
|
-N ''
|
||||||
|
|
||||||
|
echo "getting user public key for user ${keyname}"
|
||||||
|
userkey=$(echo -n " ${keyname} = \"$(cat ~/.ssh/${keyname}.pub | awk -F' ' '{ print $1, $2 }')\";")
|
||||||
|
else
|
||||||
|
userkey=$(echo -n " ${keyname} = \"$(echo -n "${public_key}" | awk -F' ' '{ print $1, $2 }')\";")
|
||||||
|
fi
|
||||||
|
|
||||||
|
update_keys "${input_file}" "${begin_marker}" "${end_marker}" "${userkey}" "users"
|
||||||
|
}
|
||||||
|
|
||||||
|
get-host-key() {
|
||||||
|
local keyname="${1}"
|
||||||
|
local target="${2}"
|
||||||
|
local type="${3:-ssh-ed25519}"
|
||||||
|
local working_directory="${4:-$(pwd)}"
|
||||||
|
local begin_marker='#-----BEGIN SYSTEM-SECRETS-----'
|
||||||
|
local end_marker='#------END SYSTEM-SECRETS------'
|
||||||
|
local input_file="${working_directory}/secrets.nix"
|
||||||
|
local hostkey
|
||||||
|
|
||||||
|
echo "getting host public key for host ${keyname}"
|
||||||
|
hostkey=$(echo -n " ${keyname} = \"$(ssh-keyscan -t ${type} ${target} 2>/dev/null | awk -F' ' '{ print $2, $3 }')\";")
|
||||||
|
|
||||||
|
update_keys "${input_file}" "${begin_marker}" "${end_marker}" "${hostkey}" "systems"
|
||||||
|
}
|
||||||
|
|
||||||
|
help() {
|
||||||
|
echo "Usage: $(basename ${0}) < gen-user-key [argument ...] | get-host-key [argument ...] >"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " gen-user-key generates a new ssh-ed25519 keypair and adds the public key to secrets.nix"
|
||||||
|
echo ""
|
||||||
|
echo " -k, --public-key provide a public key, instead of generiting a new keypair (format: \"ssh-ed25519 AAAAC3N...\")"
|
||||||
|
echo " -n, --name keyname, usually the hostname (e.g. <hostname>)"
|
||||||
|
echo " -p, --path path to the root directory for the nixOS configuration files, defaults to \`pwd\`"
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
echo " get-host-key get a ssh host public key via ssh-keyscan and adds it to secrets.nix"
|
||||||
|
echo ""
|
||||||
|
echo " -t, --target hostname, fqdn or IP from whom the host key is requested"
|
||||||
|
echo " -n, --name keyname, usually the hostname (e.g. <hostname>)"
|
||||||
|
echo " -p, --path path to the root directory for the nixOS configuration files, defaults to \`pwd\`"
|
||||||
|
echo " --type type of the key which is requested via ssh-keyscan, defaults to \`ssh-ed25519\`"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# -allow a command to fail with !’s side effect on errexit
|
||||||
|
# -use return value from ${PIPESTATUS[0]}, because ! hosed $?
|
||||||
|
! getopt --test > /dev/null
|
||||||
|
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
|
||||||
|
echo 'I’m sorry, `getopt --test` failed in this environment.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# option --output/-o requires 1 argument
|
||||||
|
OPTIONS=hk:n:p:t:
|
||||||
|
LONGOPTS=help,name:,path:,public-key:,target:,type:
|
||||||
|
|
||||||
|
# -regarding ! and PIPESTATUS see above
|
||||||
|
# -temporarily store output to be able to check for errors
|
||||||
|
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
|
||||||
|
# -pass arguments only via -- "$@" to separate them correctly
|
||||||
|
! PARSED=$(getopt --options=${OPTIONS} --longoptions=${LONGOPTS} --name "$(basename ${0})" -- "${@:--h}")
|
||||||
|
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
|
||||||
|
# e.g. return value is 1
|
||||||
|
# then getopt has complained about wrong arguments to stdout
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
# read getopt’s output this way to handle the quoting right:
|
||||||
|
eval set -- "${PARSED}"
|
||||||
|
|
||||||
|
# now enjoy the options in order and nicely split until we see --
|
||||||
|
while true; do
|
||||||
|
case "${1}" in
|
||||||
|
-h|--help)
|
||||||
|
shift
|
||||||
|
help
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
-k|--public-key)
|
||||||
|
public_key="${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-n|--name)
|
||||||
|
name="${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-p|--path)
|
||||||
|
path="${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-t|--target)
|
||||||
|
target="${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--type)
|
||||||
|
type="${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "This option (${1}) does not exist. Exiting."
|
||||||
|
exit 3
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# handle non-option arguments
|
||||||
|
if [[ ${#} -eq 1 ]]; then
|
||||||
|
while true; do
|
||||||
|
case "${1}" in
|
||||||
|
gen-user-key)
|
||||||
|
gen-user-key "${name:?Error, missing option \"-n\"}" "${public_key:-"EMPTY"}" "${path:-}"
|
||||||
|
shift
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
get-host-key)
|
||||||
|
get-host-key "${name:?Error, missing option \"-n\"}" "${target:?Error, missing option \"-t\"}" "${type:-}" "${path:-}"
|
||||||
|
shift
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Wrong sub command, use -h to print the help."
|
||||||
|
exit 4
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "No sub command provided, use -h to print the help."
|
||||||
|
fi
|
Loading…
Reference in a new issue