Skip to content

feat(server): opt-in API token authentication #1052

Description

@acouvreur

Part of the Admin UI epic #156 — stage 2 of 5.

Sablier has no authentication anywhere today — a grep for auth middleware across internal/ returns nothing. That is defensible while the API only serves reverse-proxy strategy calls on an internal port. It stops being defensible the moment /api/config exists (stage 1) and /api/instances/{name}/start exists (stage 5): that combination is a remote control plane for the whole cluster on an unauthenticated port.

Scope

A shared-token middleware, opt-in:

--server.auth.token / SABLIER_SERVER_AUTH_TOKEN
  • Compared with crypto/subtle.ConstantTimeCompare, never ==.
  • Accepted as Authorization: Bearer <token>.
  • Applied to the new introspection routes from stage 1 and the mutating routes from stage 5.
  • Not applied to /health, and not applied to the two strategy endpoints — reverse-proxy integrations (Traefik, nginx, Caddy, the plugins) call those unauthenticated today and must keep working. Breaking them would be a silent outage for every existing user.
  • /metrics follows the same rule as the strategy endpoints: unauthenticated, since it is already opt-in and typically scraped from inside the network.

Enforcement rule

When no token is configured:

  • introspection routes stay available (they are read-only, and this is the current default posture for a local Sablier)
  • mutating routes are not registered at all — not 401, absent

This makes the safe configuration the default one and means a user cannot accidentally expose start/stop control by enabling the UI.

Startup logging should state plainly which posture is active, so it shows up in a support thread.

Definition of done

  • Middleware with constant-time comparison
  • Route registration honours the "no token → no mutating routes" rule
  • Strategy endpoints and /health verified unauthenticated by test
  • Config field documented with the Env: / CLI: / Default: / Since: annotations the rest of pkg/config uses, so it flows into the generated docs
  • Docs page covering how to expose the UI safely

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions