-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathvault.bash
More file actions
executable file
·202 lines (173 loc) · 5.61 KB
/
vault.bash
File metadata and controls
executable file
·202 lines (173 loc) · 5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#!/usr/bin/env bash
# vault.bash - Ansible vault helper for variable-level encrypted strings
# Usage: ./vault.bash <command> [args]
set -euo pipefail
readonly VAULT_FILE="environment/localhost/host_vars/localhost.yml"
readonly VAULT_PASS_FILE="vault-pass.secret"
readonly VAULT_ID="localhost"
# Change to project root (where this script lives)
cd "$(dirname "$0")"
usage() {
cat <<'EOF'
Ansible Vault String Helper
Usage: ./vault.bash <command> [args]
Commands:
get <varname> Decrypt and display a single vault variable
get-raw <varname> Show exact bytes (debug whitespace/quote issues)
dump Decrypt and display ALL vault variables
list List all vault-encrypted variable names
encrypt <varname> Encrypt a string and output the vault block
(reads value from stdin or prompts interactively)
set <varname> Encrypt a string and append it to localhost.yml
(reads value from stdin or prompts interactively)
Examples:
./vault.bash get github_ssh_passphrase
./vault.bash dump
./vault.bash list
echo 'secret123' | ./vault.bash encrypt my_variable
./vault.bash set new_secret_var
Notes:
- Vault password file: vault-pass.secret
- Vault ID: localhost
- Host vars: environment/localhost/host_vars/localhost.yml
EOF
}
check_vault_pass() {
if [[ ! -f "$VAULT_PASS_FILE" ]]; then
echo "ERROR: $VAULT_PASS_FILE not found" >&2
echo "Create it first or run run.bash to set up the vault." >&2
exit 1
fi
}
# Decrypt a single vault variable using ansible CLI
# Uses python3 json (stdlib) to parse — immune to ansible output format settings
_decrypt_var() {
local varname="$1"
ANSIBLE_STDOUT_CALLBACK=ansible.builtin.minimal \
ansible localhost -c local \
--vault-id "${VAULT_ID}@${VAULT_PASS_FILE}" \
-m debug -a "msg={{ ${varname} }}" \
-i environment/localhost/hosts.yml 2>/dev/null \
| python3 -c "
import sys, json, re
raw = sys.stdin.read()
m = re.search(r'=>\s*(\{.*\})', raw, re.DOTALL)
if m:
print(json.loads(m.group(1))['msg'], end='')
else:
print(raw, end='')
"
}
cmd_get() {
local varname="${1:?Usage: vault.bash get <varname>}"
check_vault_pass
if ! grep -qP "^${varname}:.*!vault" "$VAULT_FILE" 2>/dev/null; then
echo "ERROR: Variable '$varname' not found as vault-encrypted in $VAULT_FILE" >&2
exit 1
fi
_decrypt_var "$varname"
echo # newline after value
}
# Show exact bytes of a decrypted vault variable (debug whitespace/quote issues)
cmd_get_raw() {
local varname="${1:?Usage: vault.bash get-raw <varname>}"
check_vault_pass
if ! grep -qP "^${varname}:.*!vault" "$VAULT_FILE" 2>/dev/null; then
echo "ERROR: Variable '$varname' not found as vault-encrypted in $VAULT_FILE" >&2
exit 1
fi
echo "=== Decrypted value for '$varname' (cat -A shows exact bytes) ==="
_decrypt_var "$varname" | cat -A
echo
echo "=== Length: $(_decrypt_var "$varname" | wc -c) bytes ==="
}
cmd_dump() {
check_vault_pass
local vault_vars
vault_vars=$(cmd_list)
if [[ -z "$vault_vars" ]]; then
echo "No vault-encrypted variables found in $VAULT_FILE" >&2
return
fi
local varname value
while IFS= read -r varname; do
value=$(_decrypt_var "$varname")
if [[ "${#value}" -gt 80 ]]; then
value="${value:0:77}..."
fi
printf '%-30s %s\n' "$varname" "$value"
done <<< "$vault_vars"
}
cmd_list() {
check_vault_pass
grep -oP '^[a-zA-Z_]\w*(?=:.*!vault)' "$VAULT_FILE" | sort
}
cmd_encrypt() {
local varname="${1:?Usage: vault.bash encrypt <varname>}"
check_vault_pass
local value
if [[ -t 0 ]]; then
# Interactive - prompt for value
read -rsp "Enter value for '$varname': " value
echo >&2
local confirm
read -rsp "Confirm value: " confirm
echo >&2
if [[ "$value" != "$confirm" ]]; then
echo "ERROR: Values do not match" >&2
exit 1
fi
else
# Piped input
value=$(cat)
fi
# Don't pass --vault-id here: ansible.cfg already registers one at
# vault-id "localhost". Passing it again makes ansible-core 2.20+ see
# two identities both named "localhost" and refuse to encrypt without
# --encrypt-vault-id disambiguation. Let ansible.cfg be the source.
printf '%s' "$value" | ansible-vault encrypt_string \
--stdin-name "$varname"
}
cmd_set() {
local varname="${1:?Usage: vault.bash set <varname>}"
check_vault_pass
if grep -q "^${varname}:" "$VAULT_FILE" 2>/dev/null; then
echo "ERROR: Variable '$varname' already exists in $VAULT_FILE" >&2
echo "Edit the file manually to replace it, or use a different name." >&2
exit 1
fi
local value
if [[ -t 0 ]]; then
read -rsp "Enter value for '$varname': " value
echo >&2
local confirm
read -rsp "Confirm value: " confirm
echo >&2
if [[ "$value" != "$confirm" ]]; then
echo "ERROR: Values do not match" >&2
exit 1
fi
else
value=$(cat)
fi
# See note in cmd_encrypt: ansible.cfg registers vault-id "localhost"
# already; passing --vault-id here duplicates it and breaks on
# ansible-core 2.20+.
local encrypted
encrypted=$(printf '%s' "$value" | ansible-vault encrypt_string \
--stdin-name "$varname")
echo "$encrypted" >> "$VAULT_FILE"
echo "Saved '$varname' to $VAULT_FILE (vault-encrypted)" >&2
}
# Main dispatch
case "${1:-}" in
get) shift; cmd_get "$@" ;;
get-raw) shift; cmd_get_raw "$@" ;;
dump) shift; cmd_dump "$@" ;;
list) shift; cmd_list "$@" ;;
encrypt) shift; cmd_encrypt "$@" ;;
set) shift; cmd_set "$@" ;;
-h|--help|help) usage ;;
"") usage; exit 1 ;;
*) echo "Unknown command: $1" >&2; usage; exit 1 ;;
esac