V8.00.000.2025.06.17
All checks were successful
🛡️ Shell Script Linting / 🛡️ Shell Script Linting (push) Successful in 1m52s
All checks were successful
🛡️ Shell Script Linting / 🛡️ Shell Script Linting (push) Successful in 1m52s
Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
# SPDX-PackageName: CISS.debian.installer
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
guard_sourcing
|
||||
guard_sourcing || return "${ERR_GUARD_SOURCE}"
|
||||
|
||||
#######################################
|
||||
# Parsing './.preseed/preseed.yaml' and './.preseed/partitioning.yaml'.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# SPDX-PackageName: CISS.debian.installer
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
guard_sourcing
|
||||
guard_sourcing || return "${ERR_GUARD_SOURCE}"
|
||||
|
||||
#######################################
|
||||
# Reading and extracting variables from "${PRESEED}".
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# SPDX-PackageName: CISS.debian.installer
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
guard_sourcing
|
||||
guard_sourcing || return "${ERR_GUARD_SOURCE}"
|
||||
|
||||
#######################################
|
||||
# Extended dynamic network variable checks and declarations depending on preseed.yaml.
|
||||
|
||||
214
func/cdi_1250_yaml/1256_secret_parser.sh
Normal file
214
func/cdi_1250_yaml/1256_secret_parser.sh
Normal file
@@ -0,0 +1,214 @@
|
||||
#!/bin/bash
|
||||
# SPDX-Version: 3.0
|
||||
# SPDX-CreationInfo: 2025-06-17; WEIDNER, Marc S.; <msw@coresecret.dev>
|
||||
# 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.; <msw@coresecret.dev>
|
||||
# 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}" || 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//./_}"
|
||||
var_path="${var_path//-/_}"
|
||||
var_path="${var_path//\//_}"
|
||||
var_path="${var_path// /_}"
|
||||
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_<UPPER_SNAKE_CASE_PATH> (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 secrets_encrypted="" secrets_privkey="" secrets_yaml="${CISS_SECRETS_SOURCE}" \
|
||||
__path="" __path_wo_prefix="" __pipe_fd="" __umask="" __value="" __varname="" __yq_expr=""
|
||||
|
||||
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_KEY}"
|
||||
|
||||
fi
|
||||
|
||||
secrets_privkey="$(yq -r '.secrets.x_files_key // ""' -- "${secrets_yaml}")" || secrets_privkey=""
|
||||
|
||||
[[ -z "${secrets_privkey}" ]] && return "${ERR_MISSING_AGE_KEY}"
|
||||
|
||||
secrets_privkey="${DIR_CNF}/${secrets_privkey}"
|
||||
|
||||
fi
|
||||
|
||||
__umask=$(umask)
|
||||
umask 0077
|
||||
|
||||
### Build a single streaming producer: (sops -d |) yq -rj '...'
|
||||
### yq emits: <path_wo_value>\0<plain_value>\0 -> for each secret.
|
||||
### No newlines between results (-j), only NUL (\u0000) separators -> robust with arbitrary value content.
|
||||
# shellcheck disable=SC2016
|
||||
__yq_expr='
|
||||
paths(scalars) as $p
|
||||
| select($p[0] == "secrets" and $p[-1] == "value")
|
||||
| ($p[0:-1] | join(".")) # E.g. "secrets.db.password".
|
||||
+ "\u0000"
|
||||
+ ((getpath($p) // "") | tostring) # Plain scalar value; coerce non-strings.
|
||||
+ "\u0000"
|
||||
'
|
||||
|
||||
### Create the producer as a process substitution.
|
||||
if [[ "${secrets_encrypted}" == "true" ]]; then
|
||||
|
||||
### Decrypt once, stream into yq; avoid storing full doc in memory.
|
||||
# shellcheck disable=SC1083,SC2312
|
||||
exec {__pipe_fd} < <(
|
||||
SOPS_AGE_KEY_FILE="${secrets_privkey}" sops -d --input-type=yaml --output-type=yaml -- "${secrets_yaml}" | yq -rj "${__yq_expr}" -
|
||||
)
|
||||
|
||||
else
|
||||
|
||||
# shellcheck disable=SC1083,SC2312
|
||||
exec {__pipe_fd} < <( yq -rj "${__yq_expr}" -- "${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
|
||||
|
||||
### Read value (up to NUL); if missing (odd count), treat as empty
|
||||
IFS= read -r -d '' __value <&"${__pipe_fd}" || __value=""
|
||||
|
||||
### Drop the leading 'secrets.' prefix for naming.
|
||||
__path_wo_prefix="${__path#secrets.}"
|
||||
__varname="$(ciss_secret_varname_from_path "${__path_wo_prefix}")"
|
||||
|
||||
### 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}"
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user