Files
SnarfCode/scripts/setup_harvester_authentik_sso.py

121 lines
4.6 KiB
Python

r"""Automate provisioning of Authentik OAuth2/OIDC Provider & Application for Harvester HCI.
Pre-configures:
1. Authentik OAuth2 Provider with endpoints and Harvester redirect URIs:
- https://harvester.spacedock.snarfnet.net/verify-auth
- Regex: https://harvester\.spacedock\.snarfnet\.net.*
2. Authentik Application ('harvester') with brand icon and grouping.
3. Outputs configuration details for Harvester Auth Settings.
"""
import json
import secrets
import ssl
import sys
import urllib.request
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
AK_BASE = "https://authentik.snarfnet.net"
AK_TOKEN = "SnarfNetPermanentAutomationToken2026"
AUTH_FLOW = "38a86ef3-f43a-4756-8f54-ca008a8eef6e"
INVAL_FLOW = "7a8145b8-2476-4636-9901-825f826df805"
CERT_PK = "6d48ba88-4b51-428f-903c-93334988a941"
SCOPES = [
"f521356c-080d-4bd6-b19a-68ab159a5aef", # openid
"8395b909-8e8d-4fc1-a620-5592e10df8fe", # email
"85b89b43-92d8-425c-b9cc-8e00038cad37", # profile
"d2248142-6be2-4833-94f8-e02f5d293b02", # groups
]
def ak_request(endpoint, method="GET", data=None):
url = f"{AK_BASE.rstrip('/')}/api/v3/{endpoint.lstrip('/')}"
body = json.dumps(data).encode("utf-8") if data else None
headers = {
"Authorization": f"Bearer {AK_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
}
req = urllib.request.Request(url, data=body, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=10, context=ctx) as r:
raw = r.read().decode("utf-8")
return r.getcode(), json.loads(raw) if raw else {}
def main():
print("=" * 60)
print("Harvester HCI <-> Authentik OIDC Provisioner")
print("=" * 60)
code, res = ak_request("providers/oauth2/")
existing_provider = next((p for p in res.get("results", []) if p.get("name") == "Harvester"), None)
if existing_provider:
provider_id = existing_provider["pk"]
client_id = existing_provider["client_id"]
client_secret = existing_provider["client_secret"]
print(f"Existing Harvester Provider found (ID {provider_id})")
else:
client_id = f"harvester-{secrets.token_hex(4)}"
client_secret = secrets.token_urlsafe(40)
provider_payload = {
"name": "Harvester",
"authorization_flow": AUTH_FLOW,
"invalidation_flow": INVAL_FLOW,
"property_mappings": SCOPES,
"client_type": "confidential",
"client_id": client_id,
"client_secret": client_secret,
"include_claims_in_id_token": True,
"signing_key": CERT_PK,
"redirect_uris": [
{"matching_mode": "strict", "url": "https://harvester.spacedock.snarfnet.net/verify-auth"},
{"matching_mode": "regex", "url": r"https://harvester\.spacedock\.snarfnet\.net.*"},
],
"sub_mode": "user_username",
"issuer_mode": "per_provider",
}
code, new_prov = ak_request("providers/oauth2/", method="POST", data=provider_payload)
provider_id = new_prov["pk"]
print(f"Created Harvester Provider (ID {provider_id}, client_id: {client_id})")
code, res = ak_request("core/applications/")
existing_app = next((a for a in res.get("results", []) if a.get("slug") == "harvester"), None)
app_payload = {
"name": "Harvester HCI",
"slug": "harvester",
"provider": provider_id,
"group": "Infrastructure & Hypervisors",
"meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/harvester.png",
"meta_description": "Open Source Hyperconverged Infrastructure (HCI)",
"meta_launch_url": "https://harvester.spacedock.snarfnet.net",
"open_in_new_tab": True,
}
if existing_app:
ak_request("core/applications/harvester/", method="PATCH", data=app_payload)
print("Updated existing 'harvester' application.")
else:
ak_request("core/applications/", method="POST", data=app_payload)
print("Created 'harvester' application.")
print("\n" + "=" * 60)
print("OIDC CREDENTIALS FOR HARVESTER AUTHENTICATION:")
print("=" * 60)
print(f"Issuer URL: {AK_BASE}/application/o/harvester/")
print(f"Discovery URL: {AK_BASE}/application/o/harvester/.well-known/openid-configuration")
print(f"Client ID: {client_id}")
print(f"Client Secret: {client_secret}")
print(f"Rancher/Harvester Redirect: https://harvester.spacedock.snarfnet.net/verify-auth")
print("=" * 60)
if __name__ == "__main__":
main()