Client agent HTTP server.
Listens on :7070 inside the container. The allocator calls
POST /api/session/start with a fresh KasmVNC password before
handing the seat to a student; the agent rewrites the local
kasmvncpasswd file. KasmVNC re-reads that file on each HTTP
Basic Auth check, so no signal-based reload is needed (and SIGHUP
would in fact terminate Xvnc — its reset path is unsupported).
Auth: Bearer = deployment-wide agent-control token AGENT_TOKEN (same
value the allocator generates at startup and bakes into the client
VM's docker env via OpenTofu). Any caller who knows the token
can rotate the password; that's adequate because the token is only
ever set on the client VM by OpenTofu and only ever sent by the
allocator. /healthz is unauthenticated for ALB / Docker healthchecks.
Attributes
logger = logging.getLogger(__name__)
module-attribute
Functions:
create_app()
Source code in packages/client/src/lablink_client_service/agent/api.py
| def create_app() -> Flask:
app = Flask(__name__)
expected = os.environ.get("AGENT_TOKEN", "")
@app.post("/api/session/start")
def session_start():
# Constant-time-ish check: short-circuit on the prefix to keep
# the path uniform-ish for malformed headers, then compare the
# token. We don't bother with hmac.compare_digest because the
# token's already randomly generated and the agent is one-call-
# per-seat — timing-channel risk is negligible.
auth = request.headers.get("Authorization", "")
if not expected:
logger.error("AGENT_TOKEN env var is not set; rejecting all calls")
return jsonify(error="server misconfigured"), 500
if not auth.startswith("Bearer ") or auth[7:] != expected:
return jsonify(error="unauthorized"), 401
body = request.get_json(silent=True) or {}
password = body.get("password")
if not password:
return jsonify(error="password required"), 400
try:
rotate_kasmvnc_password(password=password)
except Exception as exc:
logger.exception("KasmVNC password rotation failed")
return jsonify(error=f"rotation failed: {exc}"), 500
# Anchor the monitoring agent's session clock at user-assignment
# time. A write failure here is non-fatal: the seat is usable;
# only metric timing is affected (clock will stay at last anchor
# or at agent boot if none has been written yet).
try:
write_anchor(datetime.now(timezone.utc))
except OSError:
logger.exception(
"Failed to write session anchor; metrics may be misaligned"
)
return jsonify(ok=True), 200
@app.get("/healthz")
def healthz():
return "ok", 200
return app
|
main()
Source code in packages/client/src/lablink_client_service/agent/api.py
| def main():
create_app().run(host="0.0.0.0", port=7070)
|