245 lines
6.0 KiB
Bash
Executable File
245 lines
6.0 KiB
Bash
Executable File
#!/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 "$@"
|