Files
sxchnge/infra/local/scripts/common.sh
T

71 lines
3.1 KiB
Bash

# Shared container-side primitives. Secret JSON travels through stdin or private files.
set -eu
set -o pipefail
umask 077
STATE="$PWD/.local/dev"
TOOL_STATE=$STATE
mkdir -p "$STATE"
WORK=$(mktemp -d)
cleanup() {
rm -rf "$WORK"
chown -R "${LOCAL_UID:-0}:${LOCAL_GID:-0}" "$TOOL_STATE"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
fail() { echo "$*" >&2; exit 1; }
compose() {
if [ -f "$STATE/sendico/settings.env" ]; then
docker compose --project-directory . -f infra/local/compose.yml -f infra/local/sendico/compose.yml --env-file "$STATE/images.env" "$@"
else
docker compose --project-directory . -f infra/local/compose.yml --env-file "$STATE/images.env" "$@"
fi
}
write() {
target=$1; mode=${2:-600}
mkdir -p "$(dirname "$target")"
cat > "$target.tmp"
chmod "$mode" "$target.tmp"
mv "$target.tmp" "$target"
}
# Token is never placed in curl's arguments, errors or response logs.
vault() (
path=$1; method=${2:-GET}; missing=${3:-no}
header=$(mktemp "$WORK/header.XXXXXX")
response=$(mktemp "$WORK/response.XXXXXX")
trap 'rm -f "$header" "$response"' EXIT
printf 'Content-Type: application/json\n' > "$header"
if [ -n "${VAULT_TOKEN:-}" ]; then printf 'X-Vault-Token: %s\n' "$VAULT_TOKEN" >> "$header"; fi
if [ "$method" = GET ]; then
code=$(curl -s --connect-timeout 5 --max-time 30 -H "@$header" -o "$response" -w '%{http_code}' "http://vault:8200/v1/$path") || exit 1
else
code=$(curl -s --connect-timeout 5 --max-time 30 -H "@$header" -X "$method" --data-binary @- -o "$response" -w '%{http_code}' "http://vault:8200/v1/$path") || exit 1
fi
case "$code" in
200|204) if [ -s "$response" ]; then cat "$response"; else printf '{}'; fi;;
404) if [ "$missing" = yes ]; then printf 'null'; else echo "Vault $path returned HTTP $code" >&2; exit 1; fi;;
*) echo "Vault $path returned HTTP $code" >&2; exit 1;;
esac
)
existing() {
[ -f "$STATE/admin/init.json" ] || fail 'Missing .local/dev/admin/init.json; restore matching bootstrap material'
VAULT_TOKEN=$(jq -er '.root_token' "$STATE/admin/init.json")
export VAULT_TOKEN
}
kv() { vault "kv/data/$1" GET yes | jq 'if . == null then null else .data.data end'; }
put() { jq '{data:.}' | vault "kv/data/$1" POST >/dev/null; }
random() { printf '{"bytes":%s,"format":"hex"}' "${1:-32}" | vault sys/tools/random POST | jq -er '.data.random_bytes'; }
ensure() (
path=$1
value=$(kv "$path")
if [ "$value" = null ]; then put "$path"; fi
)
# Never forward mongosh errors: authentication/command errors may include secrets.
mongo() { compose exec -T mongo mongosh --quiet --file /dev/stdin > "$WORK/mongo-result" 2> "$WORK/mongo-error" || return 1; cat "$WORK/mongo-result"; }
admin_auth() { kv infrastructure/mongo/bootstrap | jq -r '"const admin=db.getSiblingDB(\"admin\"); admin.auth(\"local-admin\","+(.password|tojson)+");"'; }
email_valid() { jq -e '(.api_key|type=="string") and (.api_key|test("\\S"))' >/dev/null; }
smtp_valid() { jq -e 'keys==["password"] and (.password|type=="string" and length>0 and (explode|all(.>0 and .!=10 and .!=13)))' >/dev/null; }
export_ca() {
compose exec -T frontend cat /data/caddy/pki/authorities/local/root.crt | write "$STATE/ca.pem" 644
}