Improve vault password installer options
This commit is contained in:
@@ -64,26 +64,39 @@ PLACEHOLDER
|
||||
~/.config/vault-pass.txt
|
||||
```
|
||||
|
||||
安裝來源是 repo 內的密碼保護壓縮檔:
|
||||
|
||||
```text
|
||||
secrets/vault-pass.txt.zip
|
||||
```
|
||||
|
||||
執行:
|
||||
執行 installer:
|
||||
|
||||
```bash
|
||||
cd ~/projects/agent-secret-vault
|
||||
./scripts/install-vault-pass.sh
|
||||
```
|
||||
|
||||
安裝過程會要求使用者手動輸入壓縮檔密碼;解壓成功後會寫入:
|
||||
installer 會先判斷 `~/.config/vault-pass.txt` 是否已存在:
|
||||
|
||||
```text
|
||||
~/.config/vault-pass.txt
|
||||
```
|
||||
- 若已存在:保留現有檔案、修正權限為 `600`,並嘗試驗證能否解開 `secrets/vault.yml`。
|
||||
- 若不存在:提示使用者選擇 4 種建立方式。
|
||||
|
||||
權限應為 `600`。
|
||||
### 缺檔時的 4 種方式
|
||||
|
||||
1. **建立新密碼並初始化 placeholder vault**
|
||||
- 適合全新 repo / 全新環境。
|
||||
- installer 會產生新的 `~/.config/vault-pass.txt`。
|
||||
- 若 `secrets/vault.yml` 不存在,會建立加密 placeholder。
|
||||
- 若既有 `secrets/vault.yml` 無法用新密碼解開,installer 不會覆蓋它,避免破壞既有 secrets。
|
||||
|
||||
2. **使用者自行輸入 vault-pass.txt 內容**
|
||||
- installer 會用 hidden input 讀取一行密碼內容。
|
||||
- 寫入 `~/.config/vault-pass.txt`,權限設為 `600`。
|
||||
|
||||
3. **使用者輸入 vault-pass.txt URL,自動下載**
|
||||
- installer 會提示輸入 `http://` 或 `https://` URL。
|
||||
- 用 `curl` 或 `wget` 下載到 `~/.config/vault-pass.txt`。
|
||||
- 只適合可信的一次性下載 URL。
|
||||
|
||||
4. **解壓 repo 內既有密碼保護 zip**
|
||||
- 預設讀取:`secrets/vault-pass.txt.zip`。
|
||||
- zip 內必須包含檔名:`vault-pass.txt`。
|
||||
- installer 會要求使用者在自己的 terminal 手動輸入 zip 密碼。
|
||||
|
||||
### 若壓縮檔不存在
|
||||
|
||||
@@ -94,17 +107,17 @@ mkdir -p ~/projects/agent-secret-vault/secrets
|
||||
cat > ~/projects/agent-secret-vault/secrets/vault-pass.txt.zip.PLACEHOLDER <<'PLACEHOLDER'
|
||||
Missing file: secrets/vault-pass.txt.zip
|
||||
Purpose: password-protected archive containing vault-pass.txt
|
||||
Action: ask human maintainer to provide this archive or commit it to the private repo.
|
||||
Action: ask human maintainer to provide this archive or use installer method 1/2/3.
|
||||
PLACEHOLDER
|
||||
```
|
||||
|
||||
然後回報人類:缺 `secrets/vault-pass.txt.zip`,需要提供。
|
||||
然後回報人類:缺 `secrets/vault-pass.txt.zip`,或請人類選擇 installer 方法 1/2/3。
|
||||
|
||||
### 若要用非預設路徑
|
||||
|
||||
```bash
|
||||
export VAULT_PASS_FILE=/path/to/vault-pass.txt
|
||||
./scripts/install-vault-pass.sh /path/to/vault-pass.txt.zip
|
||||
./scripts/install-vault-pass.sh
|
||||
```
|
||||
|
||||
## 4. 驗證安裝
|
||||
|
||||
@@ -31,13 +31,20 @@ cd agent-secret-vault
|
||||
./scripts/install-vault-pass.sh
|
||||
```
|
||||
|
||||
這會從 repo 內的密碼保護壓縮檔解出:
|
||||
installer 會先檢查:
|
||||
|
||||
```text
|
||||
~/.config/vault-pass.txt
|
||||
```
|
||||
|
||||
過程中需要你手動輸入壓縮檔密碼。
|
||||
如果已存在,會保留並驗證。若不存在,會讓你選 4 種方式:
|
||||
|
||||
1. 建立新 vault password,並在需要時建立加密 placeholder vault。
|
||||
2. 手動輸入 vault-pass.txt 的內容。
|
||||
3. 輸入 vault-pass.txt 的 URL,讓 installer 自動下載。
|
||||
4. 解壓 repo 內既有的密碼保護檔 `secrets/vault-pass.txt.zip`。
|
||||
|
||||
若選第 4 種,請在你自己的 terminal 輸入 zip 密碼;Telegram / chat 不能輸入到 agent 的工具互動提示。
|
||||
|
||||
驗證:
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ set -euo pipefail
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
DEST="${VAULT_PASS_FILE:-$HOME/.config/vault-pass.txt}"
|
||||
ARCHIVE="${1:-$REPO_DIR/secrets/vault-pass.txt.zip}"
|
||||
VAULT_FILE="${VAULT_FILE:-$REPO_DIR/secrets/vault.yml}"
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
@@ -12,53 +13,207 @@ Usage: scripts/install-vault-pass.sh [archive.zip]
|
||||
Installs the Ansible Vault password file to:
|
||||
${VAULT_PASS_FILE:-$HOME/.config/vault-pass.txt}
|
||||
|
||||
The archive must be password-protected. The user will be prompted by unzip/7z.
|
||||
Default archive path:
|
||||
Behavior:
|
||||
1. If the password file already exists, keep it and verify permissions.
|
||||
2. If missing, prompt the user to choose one of four setup methods:
|
||||
[1] Create a new vault password and initialize/re-encrypt vault.yml
|
||||
[2] Paste/type vault-pass.txt content manually
|
||||
[3] Download vault-pass.txt from a user-provided URL
|
||||
[4] Extract vault-pass.txt from a password-protected zip archive
|
||||
|
||||
Default archive path for method [4]:
|
||||
$REPO_DIR/secrets/vault-pass.txt.zip
|
||||
USAGE
|
||||
}
|
||||
|
||||
ensure_dest_dir() {
|
||||
umask 077
|
||||
mkdir -p "$(dirname "$DEST")"
|
||||
chmod 700 "$(dirname "$DEST")" || true
|
||||
}
|
||||
|
||||
secure_dest() {
|
||||
chmod 600 "$DEST"
|
||||
}
|
||||
|
||||
verify_existing() {
|
||||
if [ -f "$DEST" ]; then
|
||||
secure_dest
|
||||
echo "Vault password file already exists: $DEST"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "Missing dependency: $1" >&2
|
||||
echo "Please install it first." >&2
|
||||
exit 3
|
||||
fi
|
||||
}
|
||||
|
||||
create_new_password() {
|
||||
require_cmd ansible-vault
|
||||
require_cmd python3
|
||||
ensure_dest_dir
|
||||
umask 077
|
||||
python3 - <<'PY' > "$DEST"
|
||||
import secrets
|
||||
print(secrets.token_urlsafe(48))
|
||||
PY
|
||||
secure_dest
|
||||
echo "Created new vault password file: $DEST"
|
||||
|
||||
if [ -f "$VAULT_FILE" ]; then
|
||||
if ansible-vault view "$VAULT_FILE" --vault-password-file "$DEST" >/dev/null 2>&1; then
|
||||
echo "Existing vault is already readable with the new password. No re-encryption needed."
|
||||
else
|
||||
cat <<WARN
|
||||
|
||||
WARNING: $VAULT_FILE exists but is not readable with the new password.
|
||||
To avoid destroying existing encrypted secrets, this script will NOT overwrite it automatically.
|
||||
If this is a brand-new install, create a plaintext YAML file and run:
|
||||
./scripts/vault.sh encrypt /path/to/plaintext.yml
|
||||
If this is an existing vault, choose method [2], [3], or [4] with the correct password instead.
|
||||
WARN
|
||||
fi
|
||||
else
|
||||
mkdir -p "$(dirname "$VAULT_FILE")"
|
||||
tmp="$(mktemp)"
|
||||
chmod 600 "$tmp"
|
||||
cat > "$tmp" <<'YAML'
|
||||
# Initial placeholder vault. Replace with real secrets using ./scripts/vault.sh edit.
|
||||
gitea: {}
|
||||
openclaw_alice:
|
||||
http_nodes: {}
|
||||
ssh_nodes: {}
|
||||
YAML
|
||||
cp "$tmp" "$VAULT_FILE"
|
||||
ansible-vault encrypt "$VAULT_FILE" --vault-password-file "$DEST"
|
||||
rm -f "$tmp"
|
||||
echo "Created encrypted placeholder vault: $VAULT_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
manual_create() {
|
||||
ensure_dest_dir
|
||||
cat <<MSG
|
||||
Paste/type the vault password content now, then press Enter.
|
||||
Input is hidden. The content will be written to:
|
||||
$DEST
|
||||
MSG
|
||||
read -r -s pass
|
||||
printf '\n'
|
||||
if [ -z "$pass" ]; then
|
||||
echo "Empty password is not allowed." >&2
|
||||
exit 4
|
||||
fi
|
||||
umask 077
|
||||
printf '%s\n' "$pass" > "$DEST"
|
||||
secure_dest
|
||||
echo "Installed manually provided vault password file: $DEST"
|
||||
}
|
||||
|
||||
download_from_url() {
|
||||
ensure_dest_dir
|
||||
printf 'Enter vault-pass.txt URL: '
|
||||
read -r url
|
||||
if [ -z "$url" ]; then
|
||||
echo "URL is required." >&2
|
||||
exit 4
|
||||
fi
|
||||
case "$url" in
|
||||
http://*|https://*) ;;
|
||||
*) echo "Only http:// or https:// URLs are supported." >&2; exit 4 ;;
|
||||
esac
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
umask 077
|
||||
curl -fsSL "$url" -o "$DEST"
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
umask 077
|
||||
wget -qO "$DEST" "$url"
|
||||
else
|
||||
echo "Missing dependency: curl or wget" >&2
|
||||
exit 3
|
||||
fi
|
||||
if [ ! -s "$DEST" ]; then
|
||||
echo "Downloaded file is empty or missing." >&2
|
||||
exit 4
|
||||
fi
|
||||
secure_dest
|
||||
echo "Downloaded vault password file to: $DEST"
|
||||
}
|
||||
|
||||
extract_from_archive() {
|
||||
require_cmd unzip
|
||||
ensure_dest_dir
|
||||
if [ ! -f "$ARCHIVE" ]; then
|
||||
cat >&2 <<ERR
|
||||
Missing archive: $ARCHIVE
|
||||
|
||||
Create/provide a password-protected archive that contains one file named:
|
||||
vault-pass.txt
|
||||
ERR
|
||||
exit 2
|
||||
fi
|
||||
tmpdir="$(mktemp -d)"
|
||||
cleanup() { rm -rf "$tmpdir"; }
|
||||
trap cleanup EXIT
|
||||
|
||||
# unzip will prompt for the archive password interactively.
|
||||
unzip -q "$ARCHIVE" -d "$tmpdir"
|
||||
|
||||
src="$tmpdir/vault-pass.txt"
|
||||
if [ ! -f "$src" ]; then
|
||||
echo "Archive extracted, but vault-pass.txt was not found inside." >&2
|
||||
exit 4
|
||||
fi
|
||||
|
||||
install -m 600 "$src" "$DEST"
|
||||
echo "Installed vault password file from archive: $DEST"
|
||||
}
|
||||
|
||||
verify_vault_readable_if_possible() {
|
||||
if [ -f "$VAULT_FILE" ] && command -v ansible-vault >/dev/null 2>&1; then
|
||||
if ansible-vault view "$VAULT_FILE" --vault-password-file "$DEST" >/dev/null 2>&1; then
|
||||
echo "Verified: vault.yml is readable with $DEST"
|
||||
else
|
||||
echo "Warning: vault.yml is not readable with $DEST" >&2
|
||||
return 5
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -f "$ARCHIVE" ]; then
|
||||
cat >&2 <<ERR
|
||||
Missing archive: $ARCHIVE
|
||||
|
||||
Create/provide a password-protected archive that contains one file named:
|
||||
vault-pass.txt
|
||||
|
||||
Then rerun:
|
||||
scripts/install-vault-pass.sh $ARCHIVE
|
||||
ERR
|
||||
exit 2
|
||||
if verify_existing; then
|
||||
verify_vault_readable_if_possible || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v unzip >/dev/null 2>&1; then
|
||||
echo "Missing dependency: unzip" >&2
|
||||
echo "Install it with: sudo apt install -y unzip" >&2
|
||||
exit 3
|
||||
fi
|
||||
cat <<MENU
|
||||
Vault password file does not exist:
|
||||
$DEST
|
||||
|
||||
tmpdir="$(mktemp -d)"
|
||||
cleanup() { rm -rf "$tmpdir"; }
|
||||
trap cleanup EXIT
|
||||
Choose setup method:
|
||||
1) Create a new vault password and initialize/re-encrypt vault.yml if needed
|
||||
2) Paste/type vault-pass.txt content manually
|
||||
3) Download vault-pass.txt from a URL
|
||||
4) Extract vault-pass.txt from password-protected zip archive
|
||||
MENU
|
||||
printf 'Enter choice [1-4]: '
|
||||
read -r choice
|
||||
|
||||
umask 077
|
||||
mkdir -p "$(dirname "$DEST")"
|
||||
chmod 700 "$(dirname "$DEST")" || true
|
||||
case "$choice" in
|
||||
1) create_new_password ;;
|
||||
2) manual_create ;;
|
||||
3) download_from_url ;;
|
||||
4) extract_from_archive ;;
|
||||
*) echo "Invalid choice: $choice" >&2; exit 4 ;;
|
||||
esac
|
||||
|
||||
# unzip will prompt for the archive password interactively.
|
||||
unzip -q "$ARCHIVE" -d "$tmpdir"
|
||||
|
||||
src="$tmpdir/vault-pass.txt"
|
||||
if [ ! -f "$src" ]; then
|
||||
echo "Archive extracted, but vault-pass.txt was not found inside." >&2
|
||||
exit 4
|
||||
fi
|
||||
|
||||
install -m 600 "$src" "$DEST"
|
||||
|
||||
echo "Installed vault password file: $DEST"
|
||||
verify_vault_readable_if_possible || true
|
||||
|
||||
Reference in New Issue
Block a user