fix: install bws from the private tier, not the manifest
The previous commit put the bws install on the private/bws-secrets row as a tarball channel. That row can never install anything: selected_packages drops every row flagged `private`, which is the invariant that makes --unattended safe to run. The container proved it — unzip installed, bws did not, and the row still could not deliver on its promise. Nor can it be a safe row: the defaults preset ticks every safe package, so that would put a Bitwarden binary and a 12 MB GitHub download on every throwaway public VM, for a tool those machines have no credential to use. So ensure_bws lives in dotup and is called from cmd_private immediately after the token is written. Nothing installs bws unless something is about to hand it a token, and the manifest keeps its invariant. core/unzip stays a real package with the @needs edge — the release is a .zip and 24.04 minimal has no unzip.
This commit is contained in:
+13
-1
@@ -107,8 +107,20 @@ is "dropping uv drops specify-cli" "off" "$(on agents/specify-cli)"
|
|||||||
# is real rather than decorative.
|
# is real rather than decorative.
|
||||||
reset; sh $D toggle p:private/bws-secrets >/dev/null
|
reset; sh $D toggle p:private/bws-secrets >/dev/null
|
||||||
is "ticking bws-secrets pulls in unzip" "on" "$(on core/unzip)"
|
is "ticking bws-secrets pulls in unzip" "on" "$(on core/unzip)"
|
||||||
is "bws-secrets has an install channel" "-tarball" \
|
# bws must NOT be a manifest package. selected_packages drops every private row
|
||||||
|
# ("private is never a package" is what makes --unattended safe), and a safe row
|
||||||
|
# would put a Bitwarden binary on every throwaway public VM via the defaults
|
||||||
|
# preset. So the private tier installs it itself, next to the token.
|
||||||
|
is "bws-secrets stays a non-package" "-" \
|
||||||
"$(awk -F'\t' '$1=="private" && $2=="bws-secrets" {print $4}' "$M")"
|
"$(awk -F'\t' '$1=="private" && $2=="bws-secrets" {print $4}' "$M")"
|
||||||
|
is "no manifest row installs bws" "0" \
|
||||||
|
"$(awk -F'\t' '!/^[#@]/ && NF>=3 && $2=="bws" {c++} END{print c+0}' "$M")"
|
||||||
|
is "the private tier installs bws itself" "1" \
|
||||||
|
"$(grep -c '^ ensure_bws$' $D)"
|
||||||
|
is "…only after a token exists to use" "1" \
|
||||||
|
"$(grep -A1 'bws token written, mode 600' $D | grep -c ensure_bws)"
|
||||||
|
is "bws is checksum-verified" "1" \
|
||||||
|
"$(grep -c 'bws checksum mismatch' $D)"
|
||||||
|
|
||||||
printf '\n\033[1mcredentials never reach a log\033[0m\n'
|
printf '\n\033[1mcredentials never reach a log\033[0m\n'
|
||||||
# `run` echoes its whole argv to stderr. The private init's argv ends in
|
# `run` echoes its whole argv to stderr. The private init's argv ends in
|
||||||
|
|||||||
@@ -379,6 +379,61 @@ norm() { printf '%s\n' "$*" | tr ' ' '\n' | grep . | tr '\n' ' ' | sed 's/ $//';
|
|||||||
# Used wherever a URL that may carry a credential is about to be printed.
|
# Used wherever a URL that may carry a credential is about to be printed.
|
||||||
redact_url() { printf '%s\n' "$1" | sed 's#://[^/@]*@#://<redacted>@#'; }
|
redact_url() { printf '%s\n' "$1" | sed 's#://[^/@]*@#://<redacted>@#'; }
|
||||||
|
|
||||||
|
# The Bitwarden Secrets Manager CLI, installed by the PRIVATE tier only.
|
||||||
|
#
|
||||||
|
# It cannot be a manifest row. `selected_packages` drops every row flagged
|
||||||
|
# `private` -- "private is never a package" is the invariant that lets
|
||||||
|
# --unattended be safe -- so a private row can never install anything. And it
|
||||||
|
# must not be a `safe` row either: the defaults preset ticks every safe package,
|
||||||
|
# which would put a Bitwarden binary and a 12 MB GitHub download on every
|
||||||
|
# throwaway public VM, for a tool those machines have no credential to use.
|
||||||
|
#
|
||||||
|
# So it lives here, called from cmd_private beside the token it exists to read.
|
||||||
|
# Nothing installs bws unless something is about to hand it a token.
|
||||||
|
ensure_bws() {
|
||||||
|
have bws && { say " bws $(bws --version 2>/dev/null | awk '{print $2}') already present"; return 0; }
|
||||||
|
command -v unzip >/dev/null 2>&1 || { err "bws needs unzip; install it and re-run"; return 1; }
|
||||||
|
# musl, not gnu: the gnu build pins a glibc newer than some LTS images
|
||||||
|
# carry, and this has to work on whatever a fresh VM turns out to be.
|
||||||
|
case $(uname -s) in
|
||||||
|
Darwin) t=macos-universal ;;
|
||||||
|
*) case $(uname -m) in
|
||||||
|
x86_64|amd64) t=x86_64-unknown-linux-musl ;;
|
||||||
|
aarch64|arm64) t=aarch64-unknown-linux-musl ;;
|
||||||
|
*) err "no bws build for $(uname -m)"; return 1 ;;
|
||||||
|
esac ;;
|
||||||
|
esac
|
||||||
|
# sdk-sm is a monorepo with per-component tags, so the `latest` release is
|
||||||
|
# usually a python SDK rather than bws. Filter by tag prefix.
|
||||||
|
v=$(curl -fsSL 'https://api.github.com/repos/bitwarden/sdk-sm/releases?per_page=40' 2>/dev/null \
|
||||||
|
| sed -n 's/.*"tag_name": *"bws-v\([0-9.]*\)".*/\1/p' | head -1)
|
||||||
|
[ -n "$v" ] || { err "could not resolve the current bws version"; return 1; }
|
||||||
|
b=https://github.com/bitwarden/sdk-sm/releases/download/bws-v$v
|
||||||
|
say " fetching bws $v"
|
||||||
|
curl -fsSL "$b/bws-$t-$v.zip" -o /tmp/bws.zip 2>/dev/null \
|
||||||
|
|| { err "bws download failed"; return 1; }
|
||||||
|
# A binary about to hold the key to every other credential is worth verifying.
|
||||||
|
if have sha256sum && curl -fsSL "$b/bws-sha256-checksums-$v.txt" -o /tmp/bws.sums 2>/dev/null; then
|
||||||
|
want=$(awk -v f="bws-$t-$v.zip" '$2==f || $2=="*"f {print $1}' /tmp/bws.sums | head -1)
|
||||||
|
got=$(sha256sum /tmp/bws.zip | awk '{print $1}')
|
||||||
|
if [ -n "$want" ] && [ "$want" != "$got" ]; then
|
||||||
|
err "bws checksum mismatch — refusing to install"
|
||||||
|
rm -f /tmp/bws.zip /tmp/bws.sums; return 1
|
||||||
|
fi
|
||||||
|
[ -n "$want" ] && say " bws checksum verified"
|
||||||
|
rm -f /tmp/bws.sums
|
||||||
|
else
|
||||||
|
warn " bws checksums unavailable — installing unverified"
|
||||||
|
fi
|
||||||
|
mkdir -p "$HOME/.local/bin" /tmp/bws.d
|
||||||
|
unzip -oq /tmp/bws.zip -d /tmp/bws.d 2>/dev/null \
|
||||||
|
|| { err "bws extract failed"; rm -rf /tmp/bws.zip /tmp/bws.d; return 1; }
|
||||||
|
install -m 755 "$(find /tmp/bws.d -type f -name bws | head -1)" "$HOME/.local/bin/bws" \
|
||||||
|
|| { err "bws install failed"; rm -rf /tmp/bws.zip /tmp/bws.d; return 1; }
|
||||||
|
rm -rf /tmp/bws.zip /tmp/bws.d
|
||||||
|
say " bws $v installed to ~/.local/bin"
|
||||||
|
}
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
if [ "$DRYRUN" -eq 1 ]; then printf ' + %s\n' "$*"; return 0; fi
|
if [ "$DRYRUN" -eq 1 ]; then printf ' + %s\n' "$*"; return 0; fi
|
||||||
printf '%s + %s%s\n' "$DIM" "$*" "$R" >&2
|
printf '%s + %s%s\n' "$DIM" "$*" "$R" >&2
|
||||||
@@ -574,59 +629,6 @@ install_bespoke() {
|
|||||||
run_sh "${SUDO:+$SUDO }rm -rf /usr/local/go && ${SUDO:+$SUDO }tar -xzf /tmp/go.tgz -C /usr/local" \
|
run_sh "${SUDO:+$SUDO }rm -rf /usr/local/go && ${SUDO:+$SUDO }tar -xzf /tmp/go.tgz -C /usr/local" \
|
||||||
|| { note_fail "$key" "tarball extract failed"; continue; }
|
|| { note_fail "$key" "tarball extract failed"; continue; }
|
||||||
run_sh "rm -f /tmp/go.tgz" ;;
|
run_sh "rm -f /tmp/go.tgz" ;;
|
||||||
private/bws-secrets)
|
|
||||||
# The Bitwarden Secrets Manager CLI. Without it the private tier
|
|
||||||
# fetches nothing: `dotsecrets` shells out to `bws secret get`, so
|
|
||||||
# this row promising seven API keys and not installing the binary
|
|
||||||
# was a promise it could not keep.
|
|
||||||
#
|
|
||||||
# User-level, ~/.local/bin, no sudo. It is a single static binary
|
|
||||||
# and the private tier is per-user by definition.
|
|
||||||
if have bws && [ "$DRYRUN" -eq 0 ]; then
|
|
||||||
say " bws $(bws --version 2>/dev/null | awk '{print $2}') already present — leaving it"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
# musl, not gnu: the gnu build pins a glibc newer than some LTS
|
|
||||||
# images carry, and this has to work on whatever a fresh VM is.
|
|
||||||
case $(uname -s) in
|
|
||||||
Darwin) t=macos-universal ;;
|
|
||||||
*) case $(uname -m) in
|
|
||||||
x86_64|amd64) t=x86_64-unknown-linux-musl ;;
|
|
||||||
aarch64|arm64) t=aarch64-unknown-linux-musl ;;
|
|
||||||
*) note_fail "$key" "no bws build for $(uname -m)"; continue ;;
|
|
||||||
esac ;;
|
|
||||||
esac
|
|
||||||
if [ "$DRYRUN" -eq 1 ]; then v=2.X.Y
|
|
||||||
else
|
|
||||||
# Releases are tagged per-component in this monorepo, so the
|
|
||||||
# `latest` endpoint points at whatever shipped last -- often a
|
|
||||||
# python SDK, not bws. Filter by tag prefix instead.
|
|
||||||
v=$(curl -fsSL 'https://api.github.com/repos/bitwarden/sdk-sm/releases?per_page=40' 2>/dev/null \
|
|
||||||
| sed -n 's/.*"tag_name": *"bws-v\([0-9.]*\)".*/\1/p' | head -1)
|
|
||||||
fi
|
|
||||||
[ -n "$v" ] || { note_fail "$key" "could not resolve the current bws version"; continue; }
|
|
||||||
b=https://github.com/bitwarden/sdk-sm/releases/download/bws-v$v
|
|
||||||
run_sh "curl -fsSL '$b/bws-$t-$v.zip' -o /tmp/bws.zip" \
|
|
||||||
|| { note_fail "$key" "download failed"; continue; }
|
|
||||||
# Bitwarden publishes checksums; a binary that is about to hold the
|
|
||||||
# key to every other credential is worth verifying.
|
|
||||||
if [ "$DRYRUN" -eq 0 ] && have sha256sum; then
|
|
||||||
if curl -fsSL "$b/bws-sha256-checksums-$v.txt" -o /tmp/bws.sums 2>/dev/null; then
|
|
||||||
want=$(awk -v f="bws-$t-$v.zip" '$2==f || $2=="*"f {print $1}' /tmp/bws.sums | head -1)
|
|
||||||
got=$(sha256sum /tmp/bws.zip | awk '{print $1}')
|
|
||||||
if [ -n "$want" ] && [ "$want" != "$got" ]; then
|
|
||||||
note_fail "$key" "checksum mismatch — refusing to install"
|
|
||||||
rm -f /tmp/bws.zip /tmp/bws.sums; continue
|
|
||||||
fi
|
|
||||||
[ -n "$want" ] && say " checksum verified"
|
|
||||||
else
|
|
||||||
warn " bws: checksums unavailable — installing unverified"
|
|
||||||
fi
|
|
||||||
rm -f /tmp/bws.sums
|
|
||||||
fi
|
|
||||||
run_sh "mkdir -p \"\$HOME/.local/bin\" && unzip -oq /tmp/bws.zip -d /tmp/bws.d && install -m 755 \"\$(find /tmp/bws.d -type f -name bws | head -1)\" \"\$HOME/.local/bin/bws\"" \
|
|
||||||
|| { note_fail "$key" "extract failed"; rm -rf /tmp/bws.zip /tmp/bws.d; continue; }
|
|
||||||
run_sh "rm -rf /tmp/bws.zip /tmp/bws.d" ;;
|
|
||||||
core/chezmoi)
|
core/chezmoi)
|
||||||
# Circular by nature: dotup arrives *via* chezmoi. Present already
|
# Circular by nature: dotup arrives *via* chezmoi. Present already
|
||||||
# in every case that matters; here for the one where it is not.
|
# in every case that matters; here for the one where it is not.
|
||||||
@@ -787,6 +789,7 @@ cmd_private() {
|
|||||||
( umask 077; printf '%s\n' "$p_tok" > "$BWS_TOKEN" )
|
( umask 077; printf '%s\n' "$p_tok" > "$BWS_TOKEN" )
|
||||||
chmod 600 "$BWS_TOKEN"
|
chmod 600 "$BWS_TOKEN"
|
||||||
say " bws token written, mode 600"
|
say " bws token written, mode 600"
|
||||||
|
ensure_bws
|
||||||
else err "the blob carried no BWS_ACCESS_TOKEN"; fi
|
else err "the blob carried no BWS_ACCESS_TOKEN"; fi
|
||||||
fi
|
fi
|
||||||
unset p_tok
|
unset p_tok
|
||||||
|
|||||||
@@ -118,4 +118,4 @@ virt libvirt invasive libvirt-daemon-system - daemon + group membership
|
|||||||
virt virt-manager invasive virt-manager -
|
virt virt-manager invasive virt-manager -
|
||||||
@private one password, typed after the install finishes
|
@private one password, typed after the install finishes
|
||||||
private private-repo private - - ~/.local/share/dotfiles-private — agent config, ssh config
|
private private-repo private - - ~/.local/share/dotfiles-private — agent config, ssh config
|
||||||
private bws-secrets private -tarball -tarball installs bws, then 7 API keys into ~/.config/zsh/secrets.zsh
|
private bws-secrets private - - bws + 7 API keys into ~/.config/zsh/secrets.zsh; the tier installs bws itself
|
||||||
|
|||||||
|
Can't render this file because it contains an unexpected character in line 17 and column 49.
|
Reference in New Issue
Block a user