IAM secure bootstrap options

This commit is contained in:
Cyber MacGeddon 2026-04-23 19:23:18 +01:00
parent 832a030703
commit 8348b7728b
5 changed files with 266 additions and 27 deletions

View file

@ -40,6 +40,7 @@ tg-get-flow-blueprint = "trustgraph.cli.get_flow_blueprint:main"
tg-get-kg-core = "trustgraph.cli.get_kg_core:main"
tg-get-document-content = "trustgraph.cli.get_document_content:main"
tg-graph-to-turtle = "trustgraph.cli.graph_to_turtle:main"
tg-bootstrap-iam = "trustgraph.cli.bootstrap_iam:main"
tg-invoke-agent = "trustgraph.cli.invoke_agent:main"
tg-invoke-document-rag = "trustgraph.cli.invoke_document_rag:main"
tg-invoke-graph-rag = "trustgraph.cli.invoke_graph_rag:main"

View file

@ -0,0 +1,99 @@
"""
Bootstraps the IAM service. Only works when iam-svc is running in
bootstrap mode with empty tables. Prints the initial admin API key
to stdout.
This is a one-time, trust-sensitive operation. The resulting token
is shown once and never again capture it on use. Rotate and
revoke it as soon as a real admin API key has been issued.
"""
import argparse
import json
import os
import sys
import requests
default_url = os.getenv("TRUSTGRAPH_URL", "http://localhost:8088/")
default_token = os.getenv("TRUSTGRAPH_TOKEN", None)
def bootstrap(url, token):
endpoint = url.rstrip("/") + "/api/v1/iam"
headers = {"Content-Type": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
resp = requests.post(
endpoint,
headers=headers,
data=json.dumps({"operation": "bootstrap"}),
)
if resp.status_code != 200:
raise RuntimeError(
f"HTTP {resp.status_code}: {resp.text}"
)
body = resp.json()
if "error" in body:
raise RuntimeError(
f"IAM {body['error'].get('type', 'error')}: "
f"{body['error'].get('message', '')}"
)
api_key = body.get("bootstrap_admin_api_key")
user_id = body.get("bootstrap_admin_user_id")
if not api_key:
raise RuntimeError(
"IAM response did not contain a bootstrap token — the "
"service may already be bootstrapped, or may be running "
"in token mode."
)
return user_id, api_key
def main():
parser = argparse.ArgumentParser(
prog="tg-bootstrap-iam",
description=__doc__,
)
parser.add_argument(
"-u", "--api-url",
default=default_url,
help=f"API URL (default: {default_url})",
)
parser.add_argument(
"-t", "--token",
default=default_token,
help="Gateway bearer token (default: $TRUSTGRAPH_TOKEN)",
)
args = parser.parse_args()
try:
user_id, api_key = bootstrap(args.api_url, args.token)
except Exception as e:
print("Exception:", e, file=sys.stderr, flush=True)
sys.exit(1)
# Stdout gets machine-readable output (the key). Any operator
# context goes to stderr.
print(f"Admin user id: {user_id}", file=sys.stderr)
print(
"Admin API key (shown once, capture now):",
file=sys.stderr,
)
print(api_key)
if __name__ == "__main__":
main()