重命名为动态 Runner 并记录协议调度路线
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fixed-audience JWT-SVID broker for nested Gitea job containers."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
SPIRE_AGENT = os.environ.get("SPIRE_AGENT", "/opt/spire/bin/spire-agent")
|
||||
SPIRE_SOCKET = os.environ.get(
|
||||
"SPIRE_SOCKET", "/run/spire/agent-sockets/spire-agent.sock"
|
||||
)
|
||||
AUDIENCE = os.environ.get("JWT_AUDIENCE", "zot")
|
||||
MAX_CONCURRENCY = int(os.environ.get("MAX_CONCURRENCY", "4"))
|
||||
semaphore = asyncio.Semaphore(MAX_CONCURRENCY)
|
||||
|
||||
|
||||
def extract_svid(document: object) -> str:
|
||||
if not isinstance(document, list):
|
||||
raise ValueError("unexpected SPIRE response")
|
||||
for item in document:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
svids = item.get("svids")
|
||||
if not isinstance(svids, list):
|
||||
continue
|
||||
for svid in svids:
|
||||
if isinstance(svid, dict) and isinstance(svid.get("svid"), str):
|
||||
return svid["svid"]
|
||||
raise ValueError("SPIRE response contains no JWT-SVID")
|
||||
|
||||
|
||||
async def fetch_svid() -> str:
|
||||
async with semaphore:
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
SPIRE_AGENT,
|
||||
"api",
|
||||
"fetch",
|
||||
"jwt",
|
||||
"-output",
|
||||
"json",
|
||||
"-audience",
|
||||
AUDIENCE,
|
||||
"-socketPath",
|
||||
SPIRE_SOCKET,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=10)
|
||||
if process.returncode != 0:
|
||||
message = stderr.decode(errors="replace").strip()
|
||||
raise RuntimeError(f"SPIRE agent failed: {message}")
|
||||
return extract_svid(json.loads(stdout))
|
||||
|
||||
|
||||
async def token(_: web.Request) -> web.Response:
|
||||
try:
|
||||
value = await fetch_svid()
|
||||
except (RuntimeError, ValueError, json.JSONDecodeError, asyncio.TimeoutError):
|
||||
raise web.HTTPServiceUnavailable(text="identity unavailable\n")
|
||||
return web.Response(
|
||||
text=f"{value}\n",
|
||||
content_type="text/plain",
|
||||
headers={"Cache-Control": "no-store"},
|
||||
)
|
||||
|
||||
|
||||
async def health(_: web.Request) -> web.Response:
|
||||
return web.Response(text="ok\n")
|
||||
|
||||
|
||||
def create_app() -> web.Application:
|
||||
app = web.Application(client_max_size=1024)
|
||||
app.router.add_post("/token", token)
|
||||
app.router.add_get("/healthz", health)
|
||||
return app
|
||||
|
||||
|
||||
def main() -> None:
|
||||
web.run_app(
|
||||
create_app(),
|
||||
host=os.environ.get("LISTEN", "0.0.0.0"),
|
||||
port=int(os.environ.get("PORT", "8788")),
|
||||
access_log=None,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user