Files
awoooi/agent99-signoz-credential-target.py
Your Name 74707c4d5c
All checks were successful
CD Pipeline / select-latest-carrier (push) Successful in 52s
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m36s
CD Pipeline / revalidate-deploy-carrier (push) Successful in 36s
CD Pipeline / build-and-deploy (push) Successful in 17m5s
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 2s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Successful in 31s
CD Pipeline / revalidate-post-deploy-carrier (push) Successful in 46s
CD Pipeline / post-deploy-checks (push) Successful in 4m54s
fix(observability): provision SigNoz metadata credential safely
2026-07-22 12:56:01 +08:00

444 lines
15 KiB
Python

#!/usr/bin/env python3
"""Host110-only SigNoz v0.113 first-admin and metadata PAT provisioner.
The program is streamed to ``python3 -`` by Agent99. Secrets are generated and
used only in this process; stdout contains a bounded metadata-only JSON object.
"""
from __future__ import annotations
import argparse
import http.client
import json
import os
import secrets
import stat
import sys
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any
BASE_URL = "http://127.0.0.1:8080"
CREDENTIAL_PATH = Path("/etc/awoooi/secrets/signoz-metadata-api-key")
RECOVERY_PASSWORD_PATH = Path(
"/etc/awoooi/secrets/signoz-bootstrap-admin-password.recovery"
)
ADMIN_EMAIL = "awoooi-signoz-admin@example.invalid"
ORG_NAME = "AWOOOI"
PAT_NAME = "awoooi-metadata-export"
MAX_RESPONSE_BYTES = 1024 * 1024
EXPECTED_UID = 0
class ProvisionError(RuntimeError):
pass
def request_json(
method: str,
path: str,
payload: dict[str, Any] | None = None,
*,
bearer: str | None = None,
api_key: str | None = None,
) -> dict[str, Any]:
headers = {"Accept": "application/json"}
body = None
if payload is not None:
headers["Content-Type"] = "application/json"
body = json.dumps(payload, separators=(",", ":")).encode("utf-8")
if bearer:
headers["Authorization"] = f"Bearer {bearer}"
if api_key:
headers["SIGNOZ-API-KEY"] = api_key
request = urllib.request.Request(
f"{BASE_URL}{path}", data=body, headers=headers, method=method
)
try:
with urllib.request.urlopen(request, timeout=15) as response: # nosec B310
raw = response.read(MAX_RESPONSE_BYTES + 1)
except urllib.error.HTTPError as exc:
raise ProvisionError(f"http_{method.lower()}_{path.rsplit('/', 1)[-1]}_{exc.code}") from None
except (urllib.error.URLError, TimeoutError, OSError, http.client.HTTPException):
raise ProvisionError(f"transport_{method.lower()}_{path.rsplit('/', 1)[-1]}_failed") from None
if len(raw) > MAX_RESPONSE_BYTES:
raise ProvisionError("response_too_large")
try:
value = json.loads(raw)
except (UnicodeError, json.JSONDecodeError):
raise ProvisionError("response_json_invalid") from None
if not isinstance(value, dict) or value.get("status") != "success":
raise ProvisionError("response_status_invalid")
return value
def request_version() -> dict[str, Any]:
request = urllib.request.Request(f"{BASE_URL}/api/v1/version", method="GET")
try:
with urllib.request.urlopen(request, timeout=15) as response: # nosec B310
raw = response.read(MAX_RESPONSE_BYTES + 1)
except (
urllib.error.HTTPError,
urllib.error.URLError,
TimeoutError,
OSError,
http.client.HTTPException,
):
raise ProvisionError("version_request_failed") from None
if len(raw) > MAX_RESPONSE_BYTES:
raise ProvisionError("version_response_too_large")
try:
value = json.loads(raw)
except (UnicodeError, json.JSONDecodeError):
raise ProvisionError("version_response_invalid") from None
if not isinstance(value, dict):
raise ProvisionError("version_shape_invalid")
return value
def request_no_content(
method: str,
path: str,
*,
bearer: str | None = None,
api_key: str | None = None,
) -> None:
headers = {"Accept": "application/json"}
if bearer:
headers["Authorization"] = f"Bearer {bearer}"
if api_key:
headers["SIGNOZ-API-KEY"] = api_key
request = urllib.request.Request(
f"{BASE_URL}{path}", headers=headers, method=method
)
try:
with urllib.request.urlopen(request, timeout=15) as response: # nosec B310
status = response.status
raw = response.read(1)
except urllib.error.HTTPError as exc:
raise ProvisionError(f"http_{method.lower()}_{path.rsplit('/', 1)[-1]}_{exc.code}") from None
except (urllib.error.URLError, TimeoutError, OSError, http.client.HTTPException):
raise ProvisionError(f"transport_{method.lower()}_{path.rsplit('/', 1)[-1]}_failed") from None
if status != 204 or raw:
raise ProvisionError("no_content_response_invalid")
def secret_state(path: Path, *, read_value: bool = False) -> tuple[str, str | None]:
try:
metadata = path.lstat()
except FileNotFoundError:
return "absent", None
if not stat.S_ISREG(metadata.st_mode) or metadata.st_uid != EXPECTED_UID:
raise ProvisionError("credential_not_root_regular_file")
if stat.S_IMODE(metadata.st_mode) not in (0o400, 0o600):
raise ProvisionError("credential_mode_invalid")
if metadata.st_size <= 0 or metadata.st_size > 4096:
raise ProvisionError("credential_size_invalid")
if not read_value:
return "present", None
token = path.read_text(encoding="utf-8").strip()
if not token or len(token) > 4096 or any(ch.isspace() for ch in token):
raise ProvisionError("credential_value_invalid")
return "present", token
def credential_state(*, read_value: bool = False) -> tuple[str, str | None]:
return secret_state(CREDENTIAL_PATH, read_value=read_value)
def setup_completed() -> bool:
value = request_version()
data = value.get("data", value)
if not isinstance(data, dict):
raise ProvisionError("version_shape_invalid")
completed = data.get("setupCompleted")
if not isinstance(completed, bool):
raise ProvisionError("setup_state_missing")
return completed
def verify_key(token: str, *, expected_pat_id: str | None = None) -> None:
value = request_json("GET", "/api/v1/dashboards", api_key=token)
if not isinstance(value.get("data"), (list, dict)):
raise ProvisionError("credential_dashboard_readback_invalid")
roles = request_json("GET", "/api/v1/roles", api_key=token)
if not isinstance(roles.get("data"), list):
raise ProvisionError("credential_admin_role_readback_invalid")
if expected_pat_id:
pats = request_json("GET", "/api/v1/pats", api_key=token)
pat_rows = pats.get("data")
if not isinstance(pat_rows, list) or not any(
isinstance(row, dict) and row.get("id") == expected_pat_id
for row in pat_rows
):
raise ProvisionError("credential_admin_pat_readback_invalid")
def write_secret_no_clobber(path: Path, value: str) -> None:
parent = path.parent
parent.mkdir(mode=0o700, parents=True, exist_ok=True)
parent_metadata = parent.lstat()
if not stat.S_ISDIR(parent_metadata.st_mode) or stat.S_ISLNK(parent_metadata.st_mode):
raise ProvisionError("credential_parent_invalid")
temp = parent / f".{path.name}.{os.getpid()}.{secrets.token_hex(8)}.tmp"
fd = -1
try:
fd = os.open(temp, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
os.write(fd, value.encode("utf-8"))
os.fsync(fd)
os.close(fd)
fd = -1
os.chown(temp, 0, 0, follow_symlinks=False)
os.chmod(temp, 0o400, follow_symlinks=False)
os.link(temp, path, follow_symlinks=False)
os.unlink(temp)
except FileExistsError:
raise ProvisionError("credential_destination_exists") from None
finally:
if fd >= 0:
os.close(fd)
try:
temp.unlink()
except FileNotFoundError:
pass
def remove_secret_verified(path: Path) -> None:
try:
metadata = path.lstat()
except FileNotFoundError:
return
if not stat.S_ISREG(metadata.st_mode) or stat.S_ISLNK(metadata.st_mode):
raise ProvisionError("secret_cleanup_target_invalid")
try:
path.unlink()
except OSError:
raise ProvisionError("secret_cleanup_delete_failed") from None
try:
path.lstat()
except FileNotFoundError:
return
raise ProvisionError("secret_cleanup_residue_present")
def organization_id_for_admin() -> str:
value = request_json(
"GET", "/api/v2/sessions/context?email=awoooi-signoz-admin%40example.invalid"
)
data = value.get("data")
orgs = data.get("orgs") if isinstance(data, dict) else None
if not isinstance(orgs, list) or len(orgs) != 1 or not isinstance(orgs[0], dict):
raise ProvisionError("bootstrap_recovery_org_context_invalid")
org_id = orgs[0].get("id") or orgs[0].get("orgId")
if not isinstance(org_id, str) or not org_id:
raise ProvisionError("bootstrap_recovery_org_id_missing")
return org_id
def revoke_pat(access_token: str, pat_id: str, pat_token: str = "") -> bool:
if access_token:
try:
request_no_content(
"DELETE", f"/api/v1/pats/{pat_id}", bearer=access_token
)
return True
except ProvisionError:
pass
if pat_token:
try:
request_no_content(
"DELETE", f"/api/v1/pats/{pat_id}", api_key=pat_token
)
return True
except ProvisionError:
pass
return False
def close_session(access_token: str) -> bool:
try:
request_no_content("DELETE", "/api/v2/sessions", bearer=access_token)
return True
except ProvisionError:
return False
def rollback_created_pat(
access_token: str,
pat_id: str,
pat_token: str,
*,
file_created: bool,
) -> None:
revoked = revoke_pat(access_token, pat_id, pat_token) if pat_id else True
if not revoked:
raise ProvisionError("apply_failed_pat_revoke_unverified_credential_preserved")
if file_created:
remove_secret_verified(CREDENTIAL_PATH)
def apply() -> dict[str, Any]:
state, _ = credential_state()
if state != "absent":
raise ProvisionError("credential_already_present_no_clobber")
recovery_state, recovery_password = secret_state(
RECOVERY_PASSWORD_PATH, read_value=True
)
completed = setup_completed()
if recovery_state == "absent" and completed:
raise ProvisionError("setup_already_completed_without_bootstrap_recovery")
if recovery_state == "absent":
recovery_password = secrets.token_urlsafe(48)
write_secret_no_clobber(RECOVERY_PASSWORD_PATH, recovery_password)
recovery_state = "present"
if recovery_password is None:
raise ProvisionError("bootstrap_recovery_password_missing")
org_id = ""
if not completed:
try:
registration = request_json(
"POST",
"/api/v1/register",
{
"email": ADMIN_EMAIL,
"orgDisplayName": ORG_NAME,
"password": recovery_password,
},
)
registration_data = registration.get("data")
org_id = (
registration_data.get("orgId")
if isinstance(registration_data, dict)
else None
)
if not isinstance(org_id, str) or not org_id:
raise ProvisionError("registration_org_id_missing")
except ProvisionError:
if not setup_completed():
raise
org_id = organization_id_for_admin()
else:
org_id = organization_id_for_admin()
session = request_json(
"POST",
"/api/v2/sessions/email_password",
{"email": ADMIN_EMAIL, "password": recovery_password, "orgId": org_id},
)
recovery_password = ""
session_data = session.get("data")
access_token = session_data.get("accessToken") if isinstance(session_data, dict) else None
if not isinstance(access_token, str) or not access_token:
raise ProvisionError("session_access_token_missing")
pat_id = ""
token = ""
file_created = False
try:
me = request_json("GET", "/api/v1/user/me", bearer=access_token)
me_data = me.get("data")
if not isinstance(me_data, dict) or me_data.get("role") != "ADMIN" or me_data.get("orgId") != org_id:
raise ProvisionError("bootstrap_admin_identity_invalid")
pat = request_json(
"POST",
"/api/v1/pats",
{"name": PAT_NAME, "expiresInDays": 30, "role": "ADMIN"},
bearer=access_token,
)
pat_data = pat.get("data")
pat_id = pat_data.get("id") if isinstance(pat_data, dict) else None
token = pat_data.get("token") if isinstance(pat_data, dict) else None
if not isinstance(pat_id, str) or not pat_id or not isinstance(token, str) or not token:
raise ProvisionError("pat_response_invalid")
verify_key(token, expected_pat_id=pat_id)
write_secret_no_clobber(CREDENTIAL_PATH, token)
file_created = True
_, persisted = credential_state(read_value=True)
if persisted is None:
raise ProvisionError("credential_persist_readback_missing")
verify_key(persisted, expected_pat_id=pat_id)
if not close_session(access_token):
raise ProvisionError("session_cleanup_unverified")
access_token = ""
remove_secret_verified(RECOVERY_PASSWORD_PATH)
token = ""
except Exception as original:
try:
rollback_created_pat(
access_token,
pat_id,
token,
file_created=file_created,
)
except ProvisionError as rollback_error:
raise rollback_error from None
finally:
close_session(access_token)
access_token = ""
raise original
return {
"terminal": "applied_verified",
"setup_completed": True,
"admin_identity_verified": True,
"credential_file_created": True,
"credential_api_verified": True,
"session_cleanup_verified": True,
"bootstrap_recovery_removed": True,
"bootstrap_is_permanent": True,
}
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--mode", choices=("check", "apply", "verify"), required=True)
args = parser.parse_args()
try:
if args.mode == "check":
state, _ = credential_state()
recovery_state, _ = secret_state(RECOVERY_PASSWORD_PATH)
result = {
"terminal": "check_pass_no_write",
"setup_completed": setup_completed(),
"credential_state": state,
"bootstrap_recovery_state": recovery_state,
"production_write": False,
}
elif args.mode == "apply":
result = apply()
else:
state, token = credential_state(read_value=True)
recovery_state, _ = secret_state(RECOVERY_PASSWORD_PATH)
if (
state != "present"
or token is None
or not setup_completed()
or recovery_state != "absent"
):
raise ProvisionError("verify_precondition_failed")
verify_key(token)
token = ""
result = {
"terminal": "verify_pass",
"setup_completed": True,
"credential_state": "present",
"credential_api_verified": True,
}
print(json.dumps(result, sort_keys=True, separators=(",", ":")))
return 0
except ProvisionError as exc:
print(
json.dumps(
{"terminal": "failed", "error_code": str(exc)},
sort_keys=True,
separators=(",", ":"),
)
)
return 1
if __name__ == "__main__":
raise SystemExit(main())