#!/bin/bash # SPDX-Version: 3.0 # SPDX-CreationInfo: 2025-06-17; WEIDNER, Marc S.; # SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.installer.git # SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency # SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; # SPDX-FileType: SOURCE # SPDX-License-Identifier: EUPL-1.2 OR LicenseRef-CCLA-1.0 # SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework. # SPDX-PackageName: CISS.debian.installer # SPDX-Security-Contact: security@coresecret.eu guard_sourcing || return "${ERR_GUARD_SOURCE}" ####################################### # Debug helper: list variable names (no values). # Globals: # CISS_SECRETS_MAP # Arguments: # None # Returns: # 0: on success ####################################### ciss_secrets_list_names() { ### Declare Arrays, HashMaps, and Variables. declare var_k="" for var_k in "${!CISS_SECRETS_MAP[@]}"; do printf '%s.value -> %s\n' "${var_k}" "${CISS_SECRETS_MAP[${var_k}]}" done return 0 } ### Prevents accidental 'unset -f'. # shellcheck disable=SC2034 readonly -f ciss_secrets_list_names ####################################### # Unset all previously created secret variables. # Globals: # CISS_SECRETS_MAP # Arguments: # None # Returns: # 0: on success ####################################### ciss_secrets_unset() { ### Declare Arrays, HashMaps, and Variables. declare var_k="" var_v="" guard_trace on for var_k in "${!CISS_SECRETS_MAP[@]}"; do var_v="${CISS_SECRETS_MAP[${var_k}]}" if [[ -v "${var_v}" ]]; then unset -v "${var_v}" 2>/dev/null || true fi done CISS_SECRETS_MAP=() guard_trace off return 0 } ### Prevents accidental 'unset -f'. # shellcheck disable=SC2034 readonly -f ciss_secrets_unset ####################################### # Build the canonical var name from a dotted path (without 'secrets.' and without '.value'). # Globals: # None # Arguments: # 1: Variable path # Returns: # 0: on success ####################################### ciss_secret_varname_from_path() { ### Declare Arrays, HashMaps, and Variables. declare var_path="${1:-}" var_path="${var_path//[^A-Za-z0-9_]/_}" var_path="${var_path^^}" printf 'CISS_SECRET_%s' "${var_path}" return 0 } ### Prevents accidental 'unset -f'. # shellcheck disable=SC2034 readonly -f ciss_secret_varname_from_path ####################################### # Purpose: # High-performance parsing of only "*.value" keys from 'SECRETS.yaml' into Bash globals. # If the file contains SOPS markers, decrypt once (streaming) with sops/age, then yq parses in a single pass. # No base64, plain values preserved (including newlines). No repeated per-key decrypts or yq calls. # Conventions: # Variables: CISS_SECRET_ (PATH excludes "secrets." and trailing ".value") # All with "declare -g" (no export). # Mapping: CISS_SECRETS_MAP["foo.bar"]=CISS_SECRET_FOO_BAR # Security: # No logging of values. No plaintext temp files. Streaming pipeline; no full-doc materialization. # Globals: # CISS_SECRETS_MAP # CISS_SECRETS_SOURCE # DIR_CNF # ERR_MISSING_AGE_KEY # Arguments: # None # Returns: # 0: on success ####################################### 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="" secrets_encrypted="$(yq -r '.secrets.x_files // false' -- "${secrets_yaml}")" || secrets_encrypted="false" if [[ "${secrets_encrypted}" == "true" ]]; then if ! command -v sops >/dev/null 2>&1; then do_log "fatal" "file_only" "1260() SOPS not found but SECRETS.yaml appears to be SOPS-managed." return "${ERR_MISSING_AGE_BIN}" fi [[ -r "${SOPS_AGE_KEY_FILE}" ]] || return "${ERR_MISSING_AGE_KEY}" fi __umask=$(umask) umask 0077 ### Create the producer as a process substitution. if [[ "${secrets_encrypted}" == "true" ]]; then ### Decrypt once, stream into yq; avoid storing full doc in memory; emits '\0\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 ### One-pass producer: emits '\0\0' for each 'secrets.*.value' # -r : raw scalars # -N : no "---" doc separators # -0 : NUL between *each* result # shellcheck disable=SC2016,SC2312 exec {__pipe_fd}< <( yq -r -N -0 'paths as $p | select( ($p[0]=="secrets") and ($p[-1]=="value") and ((getpath($p)|type) != "object") and ((getpath($p)|type) != "array") ) | ($p[0:-1] | join(".")), ((getpath($p)//"") | tostring) ' -- "${secrets_yaml}" ) fi ### Single consumer: read NUL-delimited pairs and assign variables. ### Loop invariant: next read is PATH, then VALUE. Stop cleanly at EOF. while :; do ### Read path (up to NUL); break on EOF. IFS= read -r -d '' __path <&"${__pipe_fd}" || break echo "${__path}" ### Read value (up to NUL); if missing (odd count), treat as empty IFS= read -r -d '' __value <&"${__pipe_fd}" || __value="" echo "${__value}" ### Drop the leading 'secrets.' prefix for naming. __path_wo_prefix="${__path#secrets.}" echo "${__path_wo_prefix}" __varname="$(ciss_secret_varname_from_path "${__path_wo_prefix}")" echo "${__varname}" ### Assign to a global variable, preserving content verbatim (including newlines). unset -v "${__varname}" declare -g "${__varname}" printf -v "${__varname}" '%s' "${__value}" ### Track in the map (without .value) CISS_SECRETS_MAP["${__path_wo_prefix}"]="${__varname}" done ### Close the producer FD exec {__pipe_fd}<&- umask "${__umask}" echo "Inside 1256()" sleep 60 guard_dir; return 0 } ### Prevents accidental 'unset -f'. # shellcheck disable=SC2034 readonly -f yaml_secret # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh