V8.00.000.2025.06.17
Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
This commit is contained in:
@@ -44,28 +44,36 @@ yaml_parser() {
|
||||
|
||||
### Generate Arrays for [Grub Parameter], [Locales], [NTPSec Server FQDN], [Software Packages].
|
||||
while IFS='=' read -r var_key var_value; do
|
||||
|
||||
var_value=${var_value#\'}
|
||||
var_value=${var_value%\'}
|
||||
|
||||
# shellcheck disable=SC2034,SC2249
|
||||
case "${var_key}" in
|
||||
|
||||
grub_parameter_[0-9]*) ARY_BOOTPARAM+=("${var_value}") ;;
|
||||
locale_locale_[0-9]*) ARY_LOCALE+=("${var_value}") ;;
|
||||
ntp_server_[0-9]*) ARY_NTPSRVR+=("${var_value}") ;;
|
||||
ssh_allow_ipv4_[0-9]*) ARY_ALLOW_IPV4+=("${var_value}") ;;
|
||||
ssh_allow_ipv6_[0-9]*) ARY_ALLOW_IPV6+=("${var_value}") ;;
|
||||
software_[0-9]*) ARY_PACKAGES+=("${var_value}") ;;
|
||||
|
||||
esac
|
||||
|
||||
done < "${VAR_PRESEED}"
|
||||
|
||||
var_key=""
|
||||
|
||||
### Search all set variables for user_userN_name patterns.
|
||||
# shellcheck disable=SC2312
|
||||
while IFS='=' read -r var_key _; do
|
||||
|
||||
### Accept any of these keys: name, fullname, uid, gid, shell, password, sshpubkey, authentication_* and privileges_*
|
||||
if [[ "${var_key}" =~ ^user_user([0-9]+)_(name|fullname|uid|gid|shell|password|sshpubkey|authentication_[A-Za-z0-9_]+|privileges_[A-Za-z0-9_]+)$ ]]; then
|
||||
var_index=${BASH_REMATCH[1]}
|
||||
(( var_index > VAR_USER_MAX )) && VAR_USER_MAX=var_index
|
||||
fi
|
||||
|
||||
done < "${VAR_PRESEED}"
|
||||
|
||||
### If nothing matched, default to 0 (only user 0).
|
||||
|
||||
@@ -97,6 +97,30 @@ ciss_secret_varname_from_path() {
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f ciss_secret_varname_from_path
|
||||
|
||||
#######################################
|
||||
# Wipes the specified file securely.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: File to wipe
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
ciss_secrets_wiper() {
|
||||
### Declare Arrays, HashMaps, and Variables.
|
||||
declare var_file="${1:-}"
|
||||
|
||||
if [[ -f "${var_file}" ]]; then
|
||||
: >| "${var_file}"
|
||||
shred -vfzu -n 5 "${var_file}" > /dev/null 2>&1 || rm -f -- "${var_file}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f ciss_secrets_wiper
|
||||
|
||||
#######################################
|
||||
# Purpose:
|
||||
# High-performance parsing of only "*.value" keys from 'SECRETS.yaml' into Bash globals.
|
||||
@@ -120,11 +144,25 @@ readonly -f ciss_secret_varname_from_path
|
||||
#######################################
|
||||
yaml_secret() {
|
||||
### Declare Arrays, HashMaps, and Variables.
|
||||
declare -r SOPS_AGE_KEY_FILE="/root/.config/sops/age/keys.txt"
|
||||
declare secrets_encrypted="" secrets_yaml="${CISS_SECRETS_SOURCE}" \
|
||||
__path="" __path_wo_prefix="" __pipe_fd="" __umask="" __value="" __varname=""
|
||||
declare -r SOPS_AGE_KEY_FILE="${CISS_SECRETS_AGE}"
|
||||
declare -a __names=()
|
||||
declare secrets_encrypted="" secrets_if="${CISS_SECRETS_SOURCE}" secrets_of="${DIR_CNF}/SECRETS_DECRYPTED.yaml" \
|
||||
__SECRETS="${DIR_CNF}/SECRETS_BASH.var" \
|
||||
__base="" __name="" __umask="" __path_wo_prefix="" __val="" __varname=""
|
||||
|
||||
__umask=$(umask)
|
||||
umask 0077
|
||||
|
||||
secrets_encrypted="$(yq -r '.secrets.x_files // false' -- "${secrets_if}")" || secrets_encrypted="false"
|
||||
do_log "debug" "file_only" "1256() 'secrets_encrypted' according to secrets.x_files: '${secrets_encrypted}'."
|
||||
|
||||
if grep -qE '(^|\s)sops:\s*$' -- "${secrets_if}" 2>/dev/null || grep -q 'ENC\[' -- "${secrets_if}" 2>/dev/null; then
|
||||
|
||||
secrets_encrypted="true"
|
||||
do_log "debug" "file_only" "1256() 'secrets_encrypted' according to heuristic mode: '${secrets_encrypted}'."
|
||||
|
||||
fi
|
||||
|
||||
secrets_encrypted="$(yq -r '.secrets.x_files // false' -- "${secrets_yaml}")" || secrets_encrypted="false"
|
||||
|
||||
if [[ "${secrets_encrypted}" == "true" ]]; then
|
||||
|
||||
@@ -137,61 +175,67 @@ yaml_secret() {
|
||||
|
||||
[[ -r "${SOPS_AGE_KEY_FILE}" ]] || return "${ERR_MISSING_AGE_KEY}"
|
||||
|
||||
fi
|
||||
sops -d --input-type=yaml --output-type=yaml -- "${secrets_if}" >| "${secrets_of}"
|
||||
|
||||
__umask=$(umask)
|
||||
umask 0077
|
||||
[[ -r "${secrets_of}" ]] || return "${ERR_DECRYPTION_SOPS}"
|
||||
|
||||
### Create the producer as a process substitution.
|
||||
if [[ "${secrets_encrypted}" == "true" ]]; then
|
||||
|
||||
### Decrypt once, stream into yq; avoid storing full doc in memory; emits '<path>\0<value>\0' for each 'secrets.*.value'
|
||||
# shellcheck disable=SC2016,SC2312
|
||||
exec {__pipe_fd}< <(
|
||||
sops -d --input-type=yaml --output-type=yaml -- "${secrets_yaml}" | yq -r -N -0 'leaf_paths as $p
|
||||
| select($p[0]=="secrets" and $p[-1]=="value")
|
||||
| ($p[0:-1] | join(".")), ((getpath($p)//"") | tostring)
|
||||
' -
|
||||
)
|
||||
|
||||
else
|
||||
|
||||
# shellcheck disable=SC2016,SC2312
|
||||
exec {__pipe_fd}< <( yq -r -N -0 'path(..) as $p | select($p[0]=="secrets" and $p[-1]=="value") | ($p[0:-1]|join("."))' -- "${secrets_yaml}" )
|
||||
ciss_secrets_wiper "${secrets_if}" && mv "${secrets_of}" "${secrets_if}"
|
||||
|
||||
fi
|
||||
|
||||
### Single consumer: read NUL-delimited pairs and assign variables.
|
||||
### Loop invariant: next read is PATH, then VALUE. Stop cleanly at EOF.
|
||||
while :; do
|
||||
yq -o=shell "${secrets_if}" >| "${__SECRETS}" && ciss_secrets_wiper "${secrets_if}"
|
||||
|
||||
### Read path (up to NUL); break on EOF.
|
||||
IFS= read -r -d '' __path <&"${__pipe_fd}" || break
|
||||
echo "${__path}"
|
||||
LC_ALL=C sed -n -E '
|
||||
/^[[:space:]]*(#|$)/b
|
||||
s/^[[:space:]]*export[[:space:]]+//
|
||||
/^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*_value=/p
|
||||
' -- "${__SECRETS}" >| "${__SECRETS}.value_only"
|
||||
|
||||
### Read value (up to NUL); if missing (odd count), treat as empty
|
||||
IFS= read -r -d '' __value <&"${__pipe_fd}" || __value=""
|
||||
echo "${__value}"
|
||||
ciss_secrets_wiper "${__SECRETS}"
|
||||
|
||||
### Drop the leading 'secrets.' prefix for naming.
|
||||
__path_wo_prefix="${__path#secrets.}"
|
||||
mv -f -- "${__SECRETS}.value_only" "${__SECRETS}"
|
||||
|
||||
# shellcheck disable=SC1091 source=./${__SECRETS}
|
||||
source "${__SECRETS}"
|
||||
|
||||
### Iterate only variables ending in '_value'.
|
||||
# shellcheck disable=SC2312
|
||||
mapfile -t __names < <(compgen -A variable 'secrets_*_value')
|
||||
|
||||
for __name in "${__names[@]}"; do
|
||||
|
||||
### Value of the generated variable:
|
||||
__val="${!__name}"
|
||||
echo "${__val}"
|
||||
|
||||
### Strip suffix and leading namespace:
|
||||
# secrets_db_password_value -> base="secrets_db_password"
|
||||
__base="${__name%_value}"
|
||||
echo "${__base}"
|
||||
# secrets_db_password -> path_wo_prefix="db_password"
|
||||
__path_wo_prefix="${__base#secrets_}"
|
||||
echo "${__path_wo_prefix}"
|
||||
|
||||
### Canonical CISS name:
|
||||
__varname="$(ciss_secret_varname_from_path "${__path_wo_prefix}")"
|
||||
echo "${__varname}"
|
||||
|
||||
### Assign to a global variable, preserving content verbatim (including newlines).
|
||||
### Assign as global (verbatim, preserves newlines).
|
||||
unset -v "${__varname}"
|
||||
declare -g "${__varname}"
|
||||
printf -v "${__varname}" '%s' "${__value}"
|
||||
echo "declare -g ${__varname}"
|
||||
|
||||
printf -v "${__varname}" '%s' "${__val}"
|
||||
echo "printf -v ${__varname} ${__val}"
|
||||
|
||||
### Track in the map (without .value)
|
||||
CISS_SECRETS_MAP["${__path_wo_prefix}"]="${__varname}"
|
||||
|
||||
done
|
||||
|
||||
### Close the producer FD
|
||||
exec {__pipe_fd}<&-
|
||||
### Hygiene: remove the intermediate variables to reduce secret surface, e.g., unset 'secrets_*_value' after transfer.
|
||||
for __name in "${__names[@]}"; do
|
||||
unset -v "${__name}"
|
||||
done
|
||||
|
||||
umask "${__umask}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user