Revert "the orchestrator image serves its own admin GUI" — wrong layer

A pod image is never an orchestrator; the full image always is. Acting on
the Digesta report's second option put orchestrator behaviour into
codai.api.app, which is what a RENTED POD runs, and that is wrong in three
separate ways:

- the GUI pages are not under BearerAuthMiddleware (it guards /v1/* only),
  so every rented pod would have served /admin and /login on its public
  address;
- the first-admin bootstrap would have created an admin account, with a
  password in the pod's log, in each pod's throwaway auth.json;
- initialising the session manager would have broken allow_open_pod, where
  the operator deliberately launches a pod with no token: the pod would
  then refuse every request instead of accepting all of them.

Reverted all three. Bug 1 stays — /health and /v1/health are a genuine
pod-side fix, since surya probes {service_url}/health.

The real Bug 3 is elsewhere and the report's premise was also wrong:
server.engines=0 does not mean "no engines", it means AUTO — "one per
detected GPU, min 1" (config.py:70) — which is why their host kept spawning
engine#0. A true GPU-less orchestrator in the full image needs its own
change, not a flag that already exists.
Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
parent e72edf08
...@@ -731,108 +731,10 @@ from codai.api.model_test import router as model_test_router ...@@ -731,108 +731,10 @@ from codai.api.model_test import router as model_test_router
app.include_router(model_test_router, tags=["Models"]) app.include_router(model_test_router, tags=["Models"])
app.include_router(admin_router, tags=["Admin"]) app.include_router(admin_router, tags=["Admin"])
# The admin router is the admin *API* (POST /login, /admin/api/*). The GUI PAGES # Only the admin *API* (POST /login, /admin/api/*) belongs here. The GUI PAGES
# — GET /admin, /login, /admin/models … — live in frontproxy/ui_pages.py and were # live in frontproxy/ui_pages.py and are registered by the FRONT, deliberately:
# only ever registered on the front app. An install that runs this app directly # this app is what a capability pod runs, and a rented pod must not serve an
# (a GPU-less RunPod orchestrator: `uvicorn codai.api.app:app`, no front, no # admin login page on a public address. A pod image is not an orchestrator.
# engines) therefore served the whole admin API with no way to reach it:
# GET /admin answered 404 and GET /login 405, because only POST /login existed.
# Registering them here gives one image that is both the orchestrator and its GUI.
# Guarded: a missing templates dir or config dir must not take the API down with
# it, which is the same contract the front has.
def _bootstrap_first_admin(sm, cfg_dir):
"""Give a brand-new install one admin account, printed once.
Enforcing tokens (above) would otherwise lock out a fresh config dir
completely: no token means every /v1 call is refused, and no user means
nobody can log in to create one. Nothing auto-creates that first user, so
this does — with a random password, marked must-change — and says so in the
log. Idempotent: it never touches an existing user.
"""
import secrets
try:
data = sm._load_auth_data()
except Exception:
return
if data.get("users"):
if not data.get("tokens"):
print("[api] no API tokens configured — /v1 requests will be refused; "
"create one in Admin -> Tokens", flush=True)
return
password = secrets.token_urlsafe(18)
if not sm.create_user("admin", password, role="admin"):
return
# Force the change on first login: a password that was printed to a
# container log is not a password.
def _mark(auth_data):
for u in auth_data.get("users", []):
if u.get("username") == "admin":
u["must_change_password"] = True
return True, True
return False, False
try:
sm.update_auth_data(_mark)
except Exception:
pass
print("\n" + "=" * 72, flush=True)
print("[api] FIRST RUN: created the admin account for this install", flush=True)
print(f"[api] username: admin", flush=True)
print(f"[api] password: {password}", flush=True)
print("[api] change it at /admin/change-password; this is printed once.", flush=True)
print(f"[api] stored in {cfg_dir}/auth.json", flush=True)
print("=" * 72 + "\n", flush=True)
def _ensure_session_manager():
"""Make sure API tokens are actually ENFORCED under a bare uvicorn start.
BearerAuthMiddleware falls open when admin.routes.session_manager is None and
no CODERAI_API_TOKEN is set: with no user database to check a token against
and no env token configured, it lets the request through rather than locking
out an install that has no auth at all. That is defensible on a loopback
workstation and dangerous for a GPU-less orchestrator started as
`uvicorn codai.api.app:app` and published through a reverse proxy — every
/v1/* call would be served without credentials.
main.py and build_system_app() both initialise it; a bare uvicorn start does
not. Do it here so the tokens on the Tokens page mean what they say.
"""
try:
from codai.admin import routes as _ar
if _ar.session_manager is not None:
return
from codai.platform_paths import legacy_style_config_dir
cfg_dir = getattr(getattr(_ar, "config_manager", None), "config_dir", None) \
or legacy_style_config_dir()
_ar.init_session_manager(cfg_dir)
print(f"[api] API tokens enforced from {cfg_dir}", flush=True)
_bootstrap_first_admin(_ar.session_manager, cfg_dir)
except Exception as exc:
print(f"[api] WARNING: could not initialise the session manager ({exc}); "
f"set CODERAI_API_TOKEN to refuse unauthenticated /v1 requests",
flush=True)
_ensure_session_manager()
def _register_local_ui_pages():
try:
from codai.frontproxy.ui_pages import register_ui_pages
# The same config dir every other entry point uses, so the GUI validates
# sessions against the one auth.json the admin API writes.
from codai.platform_paths import legacy_style_config_dir
from codai.admin import routes as _ar
cfg_dir = getattr(getattr(_ar, "config_manager", None), "config_dir", None) \
or legacy_style_config_dir()
if register_ui_pages(app, cfg_dir):
print(f"[api] admin GUI served locally from {cfg_dir}", flush=True)
except Exception as exc:
print(f"[api] admin GUI not served locally ({exc}); the API is unaffected",
flush=True)
_register_local_ui_pages()
@app.exception_handler(401) @app.exception_handler(401)
......
...@@ -159,44 +159,7 @@ def test_the_ocr_image_bakes_a_surya_venv(): ...@@ -159,44 +159,7 @@ def test_the_ocr_image_bakes_a_surya_venv():
# ------------------------------------- bug 3: one image = orchestrator + GUI # ------------------------------------- bug 3: one image = orchestrator + GUI
def test_the_light_app_serves_the_admin_gui_pages():
"""admin_router is the admin API (POST /login, /admin/api/*). The GUI pages
were only on the front app, so a bare `uvicorn codai.api.app:app`
orchestrator answered 404 for /admin and 405 for GET /login."""
import os
os.environ.setdefault("CODERAI_SKIP_HEAVY", "1")
from fastapi.testclient import TestClient
from codai.api.app import app
c = TestClient(app, raise_server_exceptions=False)
r = c.get("/admin", follow_redirects=False)
assert r.status_code in (200, 302, 303), f"/admin -> {r.status_code}"
r = c.get("/login", follow_redirects=False)
assert r.status_code == 200, f"GET /login -> {r.status_code} (405 = API only)"
for page in ("/admin/models", "/admin/runpod", "/admin/settings"):
assert c.get(page, follow_redirects=False).status_code in (200, 302, 303), page
def test_the_gui_redirect_carries_the_forwarded_prefix():
"""Served under /coderai/, the login redirect must point inside the prefix or
the browser leaves the deployment."""
import os
os.environ.setdefault("CODERAI_SKIP_HEAVY", "1")
from fastapi.testclient import TestClient
from codai.api.app import app
c = TestClient(app, raise_server_exceptions=False)
r = c.get("/admin", headers={"X-Forwarded-Prefix": "/coderai"},
follow_redirects=False)
assert r.status_code in (302, 303)
assert r.headers.get("location", "").startswith("/coderai/"), r.headers.get("location")
def test_the_api_survives_a_gui_registration_failure():
"""A missing templates or config dir must not take the orchestrator down."""
src = (ROOT / "codai/api/app.py").read_text()
blk = src.split("def _register_local_ui_pages")[1].split("_register_local_ui_pages()")[0]
assert "except Exception" in blk
assert "the API is unaffected" in blk
def test_the_forwarded_prefix_middleware_is_installed(): def test_the_forwarded_prefix_middleware_is_installed():
...@@ -207,23 +170,6 @@ def test_the_forwarded_prefix_middleware_is_installed(): ...@@ -207,23 +170,6 @@ def test_the_forwarded_prefix_middleware_is_installed():
# ----------------------- exposed behind a proxy: tokens must be enforced # ----------------------- exposed behind a proxy: tokens must be enforced
def test_api_tokens_are_enforced_under_a_bare_uvicorn_start():
"""BearerAuthMiddleware falls OPEN when admin.routes.session_manager is None
and no CODERAI_API_TOKEN is set. main.py initialises it; `uvicorn
codai.api.app:app` — the GPU-less orchestrator shape — did not, so every
/v1/* call was served without credentials once published through a proxy."""
import os, base64
os.environ.setdefault("CODERAI_SKIP_HEAVY", "1")
from fastapi.testclient import TestClient
from codai.api.app import app
from codai.admin import routes as ar
assert ar.session_manager is not None, "importing the app must initialise it"
c = TestClient(app, raise_server_exceptions=False)
for hdr in ({}, {"Authorization": "Bearer sk-definitely-not-a-token"},
{"Authorization": "Basic " + base64.b64encode(b"g:p").decode()}):
assert c.get("/v1/models", headers=hdr).status_code == 401, hdr
def test_readiness_probes_stay_open_when_tokens_are_enforced(): def test_readiness_probes_stay_open_when_tokens_are_enforced():
"""A probe behind auth is not a probe: surya and every orchestrator health """A probe behind auth is not a probe: surya and every orchestrator health
...@@ -237,51 +183,7 @@ def test_readiness_probes_stay_open_when_tokens_are_enforced(): ...@@ -237,51 +183,7 @@ def test_readiness_probes_stay_open_when_tokens_are_enforced():
assert c.get(path).status_code == 200, path assert c.get(path).status_code == 200, path
def test_the_gui_emits_prefixed_urls_behind_a_sub_path_proxy():
"""Served at https://host/coderai/, every URL the page emits must carry the
prefix or the browser walks out of the deployment on the first click."""
import os, re
os.environ.setdefault("CODERAI_SKIP_HEAVY", "1")
from fastapi.testclient import TestClient
from codai.api.app import app
c = TestClient(app, raise_server_exceptions=False)
html = c.get("/login", headers={"X-Forwarded-Prefix": "/coderai"}).text
assert 'action="/coderai/login"' in html, "the login form would post outside the prefix"
assert "/coderai/static/admin/" in html, "assets would 404 through the proxy"
assert re.search(r"ROOT_PATH\s*=\s*['\"]/coderai", html), "JS fetches would miss the prefix"
def test_a_fresh_install_gets_one_admin_and_is_not_locked_out():
"""Enforcing tokens would otherwise brick a brand-new config dir: no token
means every /v1 call is refused and no user means nobody can log in to make
one. Nothing else auto-creates that first user."""
import json, pathlib, tempfile
from codai.admin.auth import SessionManager
from codai.api.app import _bootstrap_first_admin
d = tempfile.mkdtemp()
sm = SessionManager(pathlib.Path(d))
_bootstrap_first_admin(sm, d)
users = json.load(open(pathlib.Path(d) / "auth.json"))["users"]
assert [u["username"] for u in users] == ["admin"]
assert users[0]["role"] == "admin"
# A password printed to a container log is not a password.
assert users[0]["must_change_password"] is True
def test_the_bootstrap_never_touches_an_existing_user():
import json, pathlib, tempfile
from codai.admin.auth import SessionManager
from codai.api.app import _bootstrap_first_admin
d = tempfile.mkdtemp()
sm = SessionManager(pathlib.Path(d))
_bootstrap_first_admin(sm, d)
before = json.load(open(pathlib.Path(d) / "auth.json"))["users"]
_bootstrap_first_admin(sm, d)
after = json.load(open(pathlib.Path(d) / "auth.json"))["users"]
assert len(after) == 1
assert before[0]["password_hash"] == after[0]["password_hash"]
def test_coderai_locks_every_pod_it_rents(): def test_coderai_locks_every_pod_it_rents():
...@@ -293,3 +195,28 @@ def test_coderai_locks_every_pod_it_rents(): ...@@ -293,3 +195,28 @@ def test_coderai_locks_every_pod_it_rents():
i = src.index('self.api_key = "cra-"') i = src.index('self.api_key = "cra-"')
guard = src[max(0, i - 200):i] guard = src[max(0, i - 200):i]
assert "allow_open_pod" in guard, "generation must be the default, not opt-in" assert "allow_open_pod" in guard, "generation must be the default, not opt-in"
def test_the_pod_app_does_not_serve_the_admin_gui():
"""A pod image is not an orchestrator. codai.api.app is what a rented pod
runs, and /admin, /login and the rest of the GUI are NOT under the Bearer
middleware (it only guards /v1/*) — so registering them here would put an
admin login page on every pod's public address."""
src = (ROOT / "codai/api/app.py").read_text()
assert "register_ui_pages" not in src
assert "_bootstrap_first_admin" not in src, \
"a pod must not create admin accounts in its throwaway auth.json"
# And it must not force token enforcement either: that would break
# allow_open_pod, where the operator deliberately sends no token.
assert "init_session_manager" not in src
def test_the_health_aliases_survive_the_revert():
"""Bug 1 is a pod-side fix and stays: surya probes {service_url}/health."""
import os
os.environ.setdefault("CODERAI_SKIP_HEAVY", "1")
from fastapi.testclient import TestClient
from codai.api.app import app
c = TestClient(app, raise_server_exceptions=False)
for path in ("/healthz", "/health", "/v1/health"):
assert c.get(path).status_code == 200, path
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment