Add/refactor encrytion- and decryption scripts.

This commit is contained in:
2026-08-03 15:44:51 +02:00
parent 76395661b1
commit 98f5fc8d58
4 changed files with 852 additions and 0 deletions
+203
View File
@@ -0,0 +1,203 @@
#!/usr/bin/env bash
#
# decrypt-interactiv.sh
#
# Zweck:
# - Entschluesselt entweder:
# (A) eine komplett mit ansible-vault verschluesselte Datei (ANSIBLE_VAULT Header)
# (B) einzelne YAML "!vault |" Bloecke in einer Datei (z.B. group_vars/host_vars)
# (C) einen einzelnen verschluesselten String (Argument, Pipe oder interaktiv)
#
# Passwort wird einmalig interaktiv abgefragt.
#
set -euo pipefail
# Temporare Datei fuer das einmalig abgefragte Vault-Passwort.
VAULT_PASS_FILE=""
cleanup() {
# Sicherheitsnetz: Temp-Datei immer entfernen, auch bei Fehler/Abbruch.
if [[ -n "${VAULT_PASS_FILE}" && -f "${VAULT_PASS_FILE}" ]]; then
rm -f "${VAULT_PASS_FILE}"
fi
}
ask_vault_password_once() {
# Passwort nur einmal pro Lauf abfragen und danach wiederverwenden.
if [[ -n "${VAULT_PASS_FILE}" && -f "${VAULT_PASS_FILE}" ]]; then
return 0
fi
if [[ ! -r /dev/tty ]]; then
echo "Error: No interactive TTY available for password prompt." >&2
return 1
fi
local pw1=""
# Wichtig: Passwort direkt vom Terminal lesen, nicht von stdin.
read -rsp "Vault password: " pw1 < /dev/tty
printf '\n' > /dev/tty
if [[ -z "${pw1}" ]]; then
echo "Error: Empty password is not allowed." >&2
return 1
fi
# Passwort in Datei speichern, damit ansible-vault es nicht interaktiv erwartet.
VAULT_PASS_FILE="$(mktemp)"
chmod 600 "${VAULT_PASS_FILE}"
printf '%s' "${pw1}" > "${VAULT_PASS_FILE}"
unset pw1
}
decrypt_with_prompted_password() {
# Alle decrypt-Aufrufe laufen zentral ueber diese Funktion.
ask_vault_password_once
ansible-vault decrypt --vault-password-file "${VAULT_PASS_FILE}" "$@" 2>/dev/null
}
########################################
# Hilfe anzeigen
########################################
show_help() {
cat <<EOF
Usage:
$(basename "$0") [OPTION] [INPUT]
Decrypt modes:
1) Full vault-encrypted file (ANSIBLE_VAULT header):
$(basename "$0") secrets.vault
2) YAML file containing one or multiple "!vault |" blocks:
$(basename "$0") group_vars/all.yml
3) Encrypted vault string:
$(basename "$0") 'secret: !vault | \$ANSIBLE_VAULT;1.1;AES256 ...'
echo 'secret: !vault | ...' | $(basename "$0")
4) Interactive mode (paste, then Ctrl-D):
$(basename "$0")
Options:
-h, --help Show this help and exit
Notes:
- This script prints decrypted values to stdout.
- Vault password is prompted once per script run.
EOF
}
########################################
# Hauptfunktion
########################################
vdecr() {
unset IFS
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
show_help
return 0
fi
if ! command -v ansible-vault >/dev/null 2>&1; then
echo "Error: ansible-vault not found in PATH." >&2
return 1
fi
########################################
# Wenn ein Argument uebergeben wurde
########################################
if [[ -n "${1:-}" ]]; then
# --- Fall 1: Argument ist eine Datei ---
if [[ -f "$1" ]]; then
if [[ "$(head -n1 "$1")" == "\$ANSIBLE_VAULT;1.1;AES256" ]]; then
decrypt_with_prompted_password "$1"
return 0
fi
printf 'Reading vault values from file...\n\n'
local parsing=0
local result=""
local name=""
local blue
local discard
blue="$(tput setaf 4 || true)"
discard="$(tput sgr0 || true)"
# Best-effort Parsing: Datei wird in Tokens zerlegt und !vault-Bloecke
# werden gesammelt, dann rekursiv als Stringinput entschluesselt.
while IFS= read -r token; do
if [[ "$(echo "$token" | grep -c "\!vault")" -gt 0 ]] && [[ $parsing -eq 0 ]]; then
parsing=1
elif [[ $parsing -eq 1 ]] && [[ "$(echo "$token" | grep -c ":")" -eq 0 ]]; then
result="$(printf "%s\n%s" "$result" "$token")"
else
if [[ -n "$result" ]]; then
printf "\n\n%s%s%s\n" "$blue" "$name" "$discard"
printf "%s" "$result" | vdecr
name=""
result=""
parsing=0
fi
fi
if [[ "$(echo "$token" | grep -c ":")" -eq 1 ]]; then
name="$token"
fi
done < <(tr -s '[:space:]' '\n' < "$1")
return 0
fi
# --- Fall 3: Argument ist ein String ---
local str="$1"
# --- Fall 4: String kommt per Pipe (stdin ist kein TTY) ---
elif [[ ! -t 0 ]]; then
local str
str="$(cat)"
# --- Fall 5: Interaktiv ---
else
printf 'Interactive mode. Paste encrypted string and press Ctrl-D two times to confirm.\n'
local str
str="$(cat)"
printf '\n'
fi
########################################
# String entschluesseln
########################################
# YAML-Deko entfernen und den Ciphertext in ein Format bringen,
# das ansible-vault decrypt direkt verarbeiten kann.
printf -- "%s" "$str" | \
sed 's/ /\n/g' | \
sed '/---\|^.*:\|\!vault\||\|^$/d' | \
decrypt_with_prompted_password
printf '\n'
}
# Aufraeumen erfolgt automatisch beim Verlassen des Skripts.
trap cleanup EXIT
########################################
# Main
########################################
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
show_help
exit 0
fi
vdecr "${1:-}"
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/env bash
#
# decrypt-vault-strings-from-file.sh
#
# Zweck:
# - Entschlüsselt entweder:
# (A) eine komplett mit ansible-vault verschlüsselte Datei (ANSIBLE_VAULT Header)
# (B) einzelne YAML "!vault |" Blöcke in einer Datei (z.B. group_vars/host_vars)
# (C) einen einzelnen verschlüsselten String (Argument, Pipe oder interaktiv)
#
# Erwartet eine Vault-Passwortdatei unter: ~/.vault-pass
#
set -euo pipefail
########################################
# Hilfe anzeigen
########################################
show_help() {
cat <<EOF
Usage:
$(basename "$0") [OPTION] [INPUT]
Decrypt modes:
1) Full vault-encrypted file (ANSIBLE_VAULT header):
$(basename "$0") secrets.vault
2) YAML file containing one or multiple "!vault |" blocks:
$(basename "$0") group_vars/all.yml
3) Encrypted vault string:
$(basename "$0") 'secret: !vault | \$ANSIBLE_VAULT;1.1;AES256 ...'
echo 'secret: !vault | ...' | $(basename "$0")
4) Interactive mode (paste, then Ctrl-D):
$(basename "$0")
Options:
-h, --help Show this help and exit
Notes:
- This script prints decrypted values to stdout.
EOF
}
########################################
# Hauptfunktion
########################################
vdecr() {
unset IFS
# --- Help Flag innerhalb der Funktion (falls rekursiv aufgerufen) ---
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
show_help
return 0
fi
# Prüfen ob ansible-vault existiert
if ! command -v ansible-vault >/dev/null 2>&1; then
echo "Error: ansible-vault not found in PATH." >&2
return 1
fi
########################################
# Wenn ein Argument übergeben wurde
########################################
if [[ -n "${1:-}" ]]; then
# --- Fall 1: Argument ist eine Datei ---
if [[ -f "$1" ]]; then
# Wenn Datei eine "komplett verschlüsselte" Vault-Datei ist
# (Header ist die erste Zeile)
if [[ "$(head -n1 "$1")" == "\$ANSIBLE_VAULT;1.1;AES256" ]]; then
# Vollständige Datei entschlüsseln und ausgeben
ansible-vault decrypt "$1" 2>/dev/null
echo "Decrypted file: $1" >&2
return 0
fi
# --- Fall 2: Datei enthält einzelne "!vault" Blöcke (z.B. YAML) ---
printf 'Reading vault values from file...\n\n'
# parsing = 0 -> wir sind nicht im Vault-Block
# parsing = 1 -> wir sammeln gerade Vault-Block-Zeilen
local parsing=0
local result=""
local name=""
# Farbausgabe (blau) für den jeweiligen Key/Name
local blue
local discard
blue="$(tput setaf 4 || true)"
discard="$(tput sgr0 || true)"
# Originalscript hat "for line in \$(cat file)" verwendet,
# was nach Whitespace tokenisiert. Um das Verhalten kontrollierter
# beizubehalten, tokenisieren wir hier ebenfalls nach Whitespace:
#
# - Das hilft, wenn Vault-Blöcke eingerückt sind oder YAML Zeichen enthält,
# weil später ohnehin per sed weiter "normalisiert" wird.
#
# WICHTIG: Das ist nicht "echtes" YAML-Parsing, sondern best-effort.
while IFS= read -r token; do
# Start eines vault blocks erkennen (Token enthält "!vault")
if [[ "$(echo "$token" | grep -c "\!vault")" -gt 0 ]] && [[ $parsing -eq 0 ]]; then
parsing=1
# Im Vault-Block: Token ohne ":" werden gesammelt (Ciphertext-Zeilen)
elif [[ $parsing -eq 1 ]] && [[ "$(echo "$token" | grep -c ":")" -eq 0 ]]; then
result="$(printf "%s\n%s" "$result" "$token")"
# Sonst: Blockende / neuer Bereich
else
# Wenn wir einen gesammelten Block haben -> ausgeben und decrypten
if [[ -n "$result" ]]; then
printf "\n\n%s%s%s\n" "$blue" "$name" "$discard"
# Rekursiver Aufruf: der gesammelte Block wird wie "Stringinput"
# behandelt und im unteren Abschnitt entschlüsselt
printf "%s" "$result" | vdecr
name=""
result=""
parsing=0
fi
fi
# Token mit ":" als "Name" merken (typischerweise YAML key:)
if [[ "$(echo "$token" | grep -c ":")" -eq 1 ]]; then
name="$token"
fi
# Tokenisierung nach Whitespace (ähnlich dem Original)
done < <(tr -s '[:space:]' '\n' < "$1")
echo "Processed vault values from file: $1" >&2
return 0
fi
# --- Fall 3: Argument ist ein String ---
local str="$1"
# --- Fall 4: String kommt per Pipe (stdin ist kein TTY) ---
elif [[ ! -t 0 ]]; then
local str
str="$(cat)"
# --- Fall 5: Interaktiv ---
else
printf 'Interactive mode. Paste encrypted string and press Ctrl-D two times to confirm.\n'
local str
str="$(cat)"
printf '\n'
fi
########################################
# String entschlüsseln
#
# Das Script erwartet, dass der Input evtl. als YAML-Fragment kommt,
# und "normalisiert" ihn so, dass ansible-vault decrypt damit klarkommt:
#
# - ersetzt Spaces durch Newlines
# - entfernt YAML-Deko wie "---", "key:", "!vault", "|", leere Zeilen
# - piped den Rest in "ansible-vault decrypt"
########################################
printf -- "%s" "$str" | \
sed 's/ /\n/g' | \
sed '/---\|^.*:\|\!vault\||\|^$/d' | \
ansible-vault decrypt 2>/dev/null
printf '\n'
}
########################################
# Main
########################################
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
show_help
exit 0
fi
vdecr "${1:-}"
+244
View File
@@ -0,0 +1,244 @@
#!/usr/bin/env bash
#
# encrypt-interactiv.sh
#
# Zweck:
# - Verschluesselt entweder:
# (A) ein komplettes File mit ansible-vault encrypt
# (B) YAML key: value Zeilen als key: !vault | Block
# (C) einen einzelnen String (Argument, Pipe oder interaktiv)
#
# Passwort wird einmalig interaktiv abgefragt.
#
set -euo pipefail
# Temp-Datei fuer das einmalig abgefragte Passwort.
VAULT_PASS_FILE=""
# Vault-ID fuer Encrypt-Operationen (bei Bedarf per ENV ueberschreibbar).
VAULT_ID_LABEL="${VAULT_ID_LABEL:-default}"
cleanup() {
# Temp-Datei immer entfernen, auch bei Fehler/Abbruch.
if [[ -n "${VAULT_PASS_FILE}" && -f "${VAULT_PASS_FILE}" ]]; then
rm -f "${VAULT_PASS_FILE}"
fi
}
ask_vault_password_once() {
# Passwort nur einmal pro Skriptlauf abfragen.
if [[ -n "${VAULT_PASS_FILE}" && -f "${VAULT_PASS_FILE}" ]]; then
return 0
fi
if [[ ! -r /dev/tty ]]; then
echo "Error: No interactive TTY available for password prompt." >&2
return 1
fi
local pw1=""
local pw2=""
# Wichtig: Passwort immer direkt vom Terminal lesen,
# nicht von stdin (stdin kann String/Pipe-Daten enthalten).
read -rsp "Vault password: " pw1 < /dev/tty
printf '\n' > /dev/tty
read -rsp "Confirm password: " pw2 < /dev/tty
printf '\n' > /dev/tty
if [[ -z "${pw1}" ]]; then
echo "Error: Empty password is not allowed." >&2
return 1
fi
if [[ "${pw1}" != "${pw2}" ]]; then
echo "Error: Password confirmation does not match." >&2
return 1
fi
VAULT_PASS_FILE="$(mktemp)"
chmod 600 "${VAULT_PASS_FILE}"
printf '%s' "${pw1}" > "${VAULT_PASS_FILE}"
unset pw1 pw2
}
encrypt_with_prompted_password() {
ask_vault_password_once
ansible-vault encrypt \
--encrypt-vault-id "${VAULT_ID_LABEL}" \
--vault-password-file "${VAULT_PASS_FILE}" \
"$@"
}
encrypt_string_with_prompted_password() {
ask_vault_password_once
ansible-vault encrypt_string \
--encrypt-vault-id "${VAULT_ID_LABEL}" \
--vault-password-file "${VAULT_PASS_FILE}" \
"$@"
}
########################################
# Hilfe anzeigen
########################################
show_help() {
cat <<EOF
Usage:
$(basename "$0") [OPTION] [INPUT]
Modes:
1) File encrypt (in-place):
$(basename "$0") secrets.txt
2) YAML file (key: value -> !vault block):
$(basename "$0") vars.yml
$(basename "$0") -o output.yml vars.yml
3) String encrypt:
$(basename "$0") 'mySecret'
echo 'mySecret' | $(basename "$0")
$(basename "$0") (interactive mode)
Options:
-o FILE Write YAML output to FILE instead of stdout
-h, --help Show this help and exit
Notes:
- Simple "key: value" YAML lines will be converted.
- Already encrypted (!vault) entries are preserved.
- Full vault-encrypted files (ANSIBLE_VAULT header) are detected.
- Vault password is prompted once per script run.
- Vault ID defaults to "default" and can be changed via VAULT_ID_LABEL.
EOF
}
########################################
# Hauptfunktion
########################################
vencr() {
unset IFS
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
show_help
return 0
fi
if ! command -v ansible-vault >/dev/null 2>&1; then
echo "Error: ansible-vault not found in PATH." >&2
return 1
fi
########################################
# Optionales Output-File (-o)
########################################
local out_file=""
if [[ "${1:-}" == "-o" && -n "${2:-}" ]]; then
out_file="$2"
shift 2
fi
########################################
# 1) File als Argument
########################################
if [[ -n "${1:-}" && -f "$1" ]]; then
local f="$1"
if [[ "$(head -n1 "$f")" == "\$ANSIBLE_VAULT;1.1;AES256"* ]]; then
echo "File already encrypted (ANSIBLE_VAULT header found): $f" >&2
return 0
fi
if grep -Eq '^[[:space:]]*[A-Za-z0-9_.-]+:[[:space:]]*[^#].*$' "$f"; then
local tmpout
tmpout="$(mktemp)"
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]]; then
printf '%s\n' "$line" >> "$tmpout"
continue
fi
if echo "$line" | grep -q '\!vault'; then
printf '%s\n' "$line" >> "$tmpout"
continue
fi
if [[ "$line" =~ ^([[:space:]]*)([A-Za-z0-9_.-]+):[[:space:]]*(.+)$ ]]; then
local indent="${BASH_REMATCH[1]}"
local key="${BASH_REMATCH[2]}"
local value="${BASH_REMATCH[3]}"
if [[ "$value" == "|" || "$value" == ">" || "$value" == "" ]]; then
printf '%s\n' "$line" >> "$tmpout"
continue
fi
value="${value%\"}"; value="${value#\"}"
value="${value%\'}"; value="${value#\'}"
# Aus key: value wird ein key: !vault |-Block.
while IFS= read -r enc_line; do
printf '%s%s\n' "$indent" "$enc_line" >> "$tmpout"
done < <(
printf '%s' "$value" |
encrypt_string_with_prompted_password --stdin-name "$key"
)
else
printf '%s\n' "$line" >> "$tmpout"
fi
done < "$f"
if [[ -n "$out_file" ]]; then
mv "$tmpout" "$out_file"
echo "Encrypted YAML written to: $out_file" >&2
else
cat "$tmpout"
rm -f "$tmpout"
fi
return 0
fi
encrypt_with_prompted_password "$f"
echo "Encrypted file in-place: $f" >&2
return 0
fi
########################################
# 2) String-Verschluesselung
########################################
local str=""
local name="secret"
if [[ -n "${1:-}" ]]; then
str="$1"
elif [[ ! -t 0 ]]; then
str="$(cat)"
else
echo "Interactive mode."
read -r -p "Variable name (default: secret): " name_in
if [[ -n "$name_in" ]]; then
name="$name_in"
fi
echo "Paste plaintext and press Ctrl-D to confirm:"
str="$(cat)"
echo
fi
printf '%s' "$str" | encrypt_string_with_prompted_password --stdin-name "$name"
}
trap cleanup EXIT
########################################
# Script starten
########################################
vencr "$@"
+221
View File
@@ -0,0 +1,221 @@
#!/usr/bin/env bash
#
# encrypt-vault-strings-to-file.sh
#
# Gegenstück zu deinem Decrypt-Script.
#
# Funktionen:
# 1. Komplettes File mit ansible-vault encrypt verschlüsseln
# 2. YAML-Datei mit key: value Zeilen in key: !vault | Blöcke umwandeln
# 3. Einzelnen String verschlüsseln (Argument, Pipe oder interaktiv)
#
# Vault-Passwortdatei: ~/.vault-pass
#
set -euo pipefail
########################################
# Hilfe anzeigen
########################################
show_help() {
cat <<EOF
Usage:
$(basename "$0") [OPTION] [INPUT]
Modes:
1) File encrypt (in-place):
$(basename "$0") secrets.txt
2) YAML file (key: value → !vault block):
$(basename "$0") vars.yml
$(basename "$0") -o output.yml vars.yml
Force whole-file encryption for a YAML file:
$(basename "$0") --full-file vars.yml
3) String encrypt:
$(basename "$0") 'mySecret'
echo 'mySecret' | $(basename "$0")
$(basename "$0") (interactive mode)
Options:
-o FILE Write YAML output to FILE instead of stdout
--full-file Encrypt the given file in-place, even if it is .yml/.yaml
-h, --help Show this help and exit
Notes:
- Simple "key: value" YAML lines will be converted.
- Already encrypted (!vault) entries are preserved.
- Full vault-encrypted files (ANSIBLE_VAULT header) are detected.
EOF
}
########################################
# Hauptfunktion
########################################
vencr() {
unset IFS
# --- Help Flag ---
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
show_help
exit 0
fi
########################################
# Optionen: Ausgabe-Datei und Vollverschluesselung fuer YAML-Dateien.
########################################
local out_file=""
local force_full_file=0
while [[ -n "${1:-}" && "${1:-}" == -* ]]; do
case "$1" in
-o)
if [[ -z "${2:-}" ]]; then
echo "Error: -o requires a file name." >&2
return 1
fi
out_file="$2"
shift 2
;;
--full-file)
force_full_file=1
shift
;;
-h|--help)
show_help
exit 0
;;
--)
shift
break
;;
*)
break
;;
esac
done
########################################
# --- 1) File als Argument ---
########################################
if [[ -n "${1:-}" && -f "$1" ]]; then
local f="$1"
# Fall A: Datei ist bereits vollständig vault-verschlüsselt
if [[ "$(head -n1 "$f")" == "\$ANSIBLE_VAULT;1.1;AES256"* ]]; then
echo "File already encrypted (ANSIBLE_VAULT header found): $f" >&2
return 0
fi
# Fall B: YAML mit key: value Zeilen nur fuer typische YAML-Dateien
# (damit z.B. README.md als normale Datei komplett verschluesselt wird).
if [[ $force_full_file -eq 0 ]] && [[ "$f" =~ \.(yml|yaml)$ ]] && grep -Eq '^[[:space:]]*[A-Za-z0-9_.-]+:[[:space:]]*[^#].*$' "$f"; then
local tmpout
tmpout="$(mktemp)"
# Datei zeilenweise lesen
while IFS= read -r line || [[ -n "$line" ]]; do
# Leere Zeilen oder Kommentare unverändert übernehmen
if [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]]; then
printf '%s\n' "$line" >> "$tmpout"
continue
fi
# Bereits verschlüsselte !vault Einträge nicht verändern
if echo "$line" | grep -q '\!vault'; then
printf '%s\n' "$line" >> "$tmpout"
continue
fi
# Einfache key: value Zeilen erkennen
if [[ "$line" =~ ^([[:space:]]*)([A-Za-z0-9_.-]+):[[:space:]]*(.+)$ ]]; then
local indent="${BASH_REMATCH[1]}"
local key="${BASH_REMATCH[2]}"
local value="${BASH_REMATCH[3]}"
# YAML-Blockindikatoren nicht verändern
if [[ "$value" == "|" || "$value" == ">" || "$value" == "" ]]; then
printf '%s\n' "$line" >> "$tmpout"
continue
fi
# Leichte Bereinigung von Quotes
value="${value%\"}"; value="${value#\"}"
value="${value%\'}"; value="${value#\'}"
# Verschlüsselung via ansible-vault encrypt_string
while IFS= read -r enc_line; do
printf '%s%s\n' "$indent" "$enc_line" >> "$tmpout"
done < <(
printf '%s' "$value" |
ansible-vault encrypt_string \
--stdin-name "$key"
)
else
# Nicht passende Zeilen unverändert übernehmen
printf '%s\n' "$line" >> "$tmpout"
fi
done < "$f"
# Output schreiben
if [[ -n "$out_file" ]]; then
mv "$tmpout" "$out_file"
echo "Encrypted YAML written to: $out_file" >&2
else
cat "$tmpout"
rm -f "$tmpout"
fi
return 0
fi
# Fall C: Normale Datei → komplett verschlüsseln (in-place)
ansible-vault encrypt "$f"
echo "Encrypted file in-place: $f" >&2
return 0
fi
########################################
# --- 2) String-Verschlüsselung ---
########################################
local str=""
local name="secret"
# String als Argument
if [[ -n "${1:-}" ]]; then
str="$1"
# String via Pipe
elif [[ ! -t 0 ]]; then
str="$(cat)"
# Interaktiver Modus
else
echo "Interactive mode."
read -r -p "Variable name (default: secret): " name_in
if [[ -n "$name_in" ]]; then
name="$name_in"
fi
echo "Paste plaintext and press Ctrl-D to confirm:"
str="$(cat)"
echo
fi
# Ausgabe als YAML-kompatibler !vault Block
echo "Hallo"
printf '%s' "$str" | ansible-vault encrypt_string --stdin-name "$name"
}
########################################
# Script starten
########################################
vencr "$@"