78 lines
2.7 KiB
Bash
Executable File
78 lines
2.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
LOCAL_KEY="${RENDEZVOUS_SMOKE_LOCAL_KEY:-$ROOT/deploy/compose/secrets/signing-key}"
|
|
|
|
if (( $# != 0 )); then
|
|
printf 'This helper accepts no arguments and mints only the fixed local Compose smoke scope.\n' >&2
|
|
exit 2
|
|
fi
|
|
|
|
command -v python3 >/dev/null || {
|
|
printf 'Missing required command: python3\n' >&2
|
|
exit 2
|
|
}
|
|
|
|
# This is deliberately a local-fixture tool, not a general credential issuer.
|
|
# Python reads the raw key from the protected file; key material never appears in
|
|
# a child process argument, environment value, temporary file, or command output.
|
|
python3 - "$LOCAL_KEY" <<'PY'
|
|
import base64
|
|
import hashlib
|
|
import hmac
|
|
import json
|
|
import os
|
|
import secrets
|
|
import stat
|
|
import sys
|
|
import time
|
|
|
|
key_path = sys.argv[1]
|
|
try:
|
|
metadata = os.lstat(key_path)
|
|
except FileNotFoundError:
|
|
raise SystemExit(f"Local Compose smoke key does not exist: {key_path}")
|
|
|
|
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode):
|
|
raise SystemExit(f"Local Compose smoke key must be a regular non-symlink file: {key_path}")
|
|
parent_path = os.path.dirname(os.path.abspath(key_path))
|
|
parent = os.lstat(parent_path)
|
|
if stat.S_ISLNK(parent.st_mode) or not stat.S_ISDIR(parent.st_mode):
|
|
raise SystemExit(f"Local Compose secret directory must be a non-symlink directory: {parent_path}")
|
|
if parent.st_uid != os.geteuid() or parent.st_mode & 0o077:
|
|
raise SystemExit(f"Local Compose secret directory must be owned by this user with mode 0700: {parent_path}")
|
|
if metadata.st_uid != os.geteuid() or metadata.st_mode & 0o022 or metadata.st_nlink != 1:
|
|
raise SystemExit(f"Local Compose smoke key must be owned by this user, single-linked, and not group/world writable: {key_path}")
|
|
|
|
with open(key_path, "rb") as key_file:
|
|
key = key_file.read(33)
|
|
if len(key) != 32:
|
|
raise SystemExit(f"Local Compose smoke key must be exactly 32 bytes: {key_path}")
|
|
|
|
now = int(time.time())
|
|
payload = {
|
|
"version": 1,
|
|
"issuer": "final-factory-rendezvous-smoke",
|
|
"audience": "rendezvous-service",
|
|
"subject": "local-smoke-host",
|
|
"kind": "dedicatedPublisher",
|
|
"gameId": "space-game",
|
|
"environmentId": "smoke",
|
|
"regions": ["local"],
|
|
"permissions": [],
|
|
"issuedAtUnixSeconds": now,
|
|
"notBeforeUnixSeconds": now,
|
|
"expiresAtUnixSeconds": now + 600,
|
|
"nonce": secrets.token_hex(16),
|
|
}
|
|
|
|
def base64url(value: bytes) -> str:
|
|
return base64.urlsafe_b64encode(value).rstrip(b"=").decode("ascii")
|
|
|
|
encoded = base64url(json.dumps(payload, separators=(",", ":")).encode("utf-8"))
|
|
signed = f"rv1.local-smoke-1.{encoded}"
|
|
signature = base64url(hmac.new(key, signed.encode("ascii"), hashlib.sha256).digest())
|
|
print(f"{signed}.{signature}")
|
|
PY
|