ADR-003, .env.example, the access module's headers and the provisioning schema still described the planned npub-QR → UID → serial-reader path. What shipped (#86) is Bolt Card tap-to-enter over the main-process pcscd reader with external_id as the identity, soft entry and verify-at-payment. Nothing ever called availableAccessReaders(): the camera npub-QR reader, the mock reader and the AccessReader seam were dead, so they go; services/access now holds authorize, the card parser and the credential types. The unused 'uid' scan variant goes with them; 'npub' (+PIN) and the 'challenge' seam stay. The ADR gets an amendment section recording the differences, including that open enrollment is not a security boundary and that the audit is still a stub (both tracked as issues). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
69 lines
2.7 KiB
Bash
Executable file
69 lines
2.7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Provision the access-control gate (ADR-003) to a deployed bitSpire ATM.
|
|
# Pushes an access.json to /var/lib/bitspire/ and restarts the service, so the
|
|
# gate can be toggled on a machine without an image rebuild (mirrors
|
|
# provision-branding.sh). Env defaults are overridden by whatever this file sets.
|
|
#
|
|
# Usage:
|
|
# bash provision-access.sh <access.json> # SSH to localhost:2222 (QEMU)
|
|
# bash provision-access.sh <access.json> 192.168.1.50 # a real ATM on the LAN
|
|
# bash provision-access.sh <access.json> 192.168.1.50 22 # custom SSH port
|
|
#
|
|
# access.json schema (all keys optional; omitted keys fall back to env/defaults):
|
|
# {
|
|
# "enabled": true, // master switch for the gate
|
|
# "openEnrollment": true, // admit any Bolt Card (gate is not a security boundary)
|
|
# "devUnlock": false, // on-screen dev/operator unlock (bypasses the gate; default off)
|
|
# "salt": "per-machine", // hashing salt (provision a real one for prod)
|
|
# "allowList": [ // authorized identities (hashed); empty in open mode
|
|
# { "idHash": "<hashId(external_id,salt)>", "role": "user", "pinHash": "<hashPin(pin,salt)>" }
|
|
# ]
|
|
# }
|
|
#
|
|
# To DISABLE the gate again: push a file with {"enabled": false} (or delete
|
|
# /var/lib/bitspire/access.json on the machine) and restart.
|
|
set -euo pipefail
|
|
|
|
ACCESS_FILE="${1:-}"
|
|
ATM_HOST="${2:-localhost}"
|
|
ATM_SSH_PORT="${3:-2222}"
|
|
ATM_USER="bitspire"
|
|
REMOTE_FILE="/var/lib/bitspire/access.json"
|
|
|
|
if [ -z "$ACCESS_FILE" ]; then
|
|
echo "Usage: $0 <access.json> [host] [port]" >&2
|
|
echo " $0 ./access.json (QEMU on localhost:2222)" >&2
|
|
echo " $0 ./access.json 192.168.1.50 (real ATM)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f "$ACCESS_FILE" ]; then
|
|
echo "ERROR: access file not found: $ACCESS_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Fail fast on malformed JSON before touching the machine.
|
|
if command -v jq >/dev/null 2>&1; then
|
|
jq empty "$ACCESS_FILE" || { echo "ERROR: $ACCESS_FILE is not valid JSON" >&2; exit 1; }
|
|
fi
|
|
|
|
echo "=== Provisioning access gate to $ATM_HOST:$ATM_SSH_PORT ==="
|
|
echo "Local file : $ACCESS_FILE"
|
|
echo "Remote file: $REMOTE_FILE"
|
|
cat "$ACCESS_FILE"
|
|
echo ""
|
|
|
|
# Copy over SSH. --rsync-path=sudo because /var/lib/bitspire is owned by the
|
|
# bitspire service user, not the SSH user.
|
|
rsync -avz \
|
|
--rsync-path="sudo rsync" \
|
|
-e "ssh -o StrictHostKeyChecking=no -p $ATM_SSH_PORT" \
|
|
"$ACCESS_FILE" \
|
|
"$ATM_USER@$ATM_HOST:$REMOTE_FILE"
|
|
|
|
# Restart so loadAccessControl() re-reads the file.
|
|
ssh -o StrictHostKeyChecking=no -p "$ATM_SSH_PORT" "$ATM_USER@$ATM_HOST" \
|
|
"sudo systemctl restart bitspire"
|
|
|
|
echo ""
|
|
echo "=== Access gate provisioned. Service restarted. ==="
|