Class ApiKey

java.lang.Object
org.frontcache.core.ApiKey

public class ApiKey extends Object
The front-cache.api-key check, in one place. This is the management API's only access control - the thing that decides whether a caller may invalidate a cache, dump its keys or read its metrics. There used to be a front-cache.management.port check in front of it, which was never a boundary: it compared the port the caller ADDRESSED, taken from the Host header. It is gone; restricting which connector answers management traffic is the reverse proxy's job. Extracted because there was one implementation in FrontCacheIOServlet and a second endpoint needed the same rule, and two implementations of one security check is one too many - the copy is always the one that goes stale.

The property was front-cache.site-key through 2.8

Renamed, with NO fallback to the old name: a node whose frontcache.properties still says front-cache.site-key reads no key at all and comes up with its management API, dashboard stream and /fc-metrics OPEN. Rename the property when you upgrade. The credential on the wire did not change, so callers - agent, console, peer nodes, Prometheus - need no coordination with this; only the node's own config file does.

Authorization: Bearer <api-key> is the way to send it

One form, everywhere: the agent, the console, FrontCacheClient, node-to-node replication and a Prometheus scrape all use it. It is a standard scheme with standard tooling, and it is the only form Prometheus can send at all - a scrape_config offers basic_auth, authorization and oauth2, and no way to set an arbitrary header - which is what settled the choice between the two.

The legacy x-frontcache-site-key header, for one release

That header was the Frontcache convention through 2.6 and is still accepted, deprecated, with a warning naming the caller. It cannot simply be dropped: frontcache-agent is a published artifact embedded in customer applications, the console is deployed separately from the fleet, and nodes replicate to each other - so at any moment some caller is older than the node it is calling, and a node that refused the old header would break all of them on upgrade rather than at a time anyone chose. Same treatment get-hystrix-configs got in 2.7. Remove once every supported caller is 2.7 or later.

The SSE stream's check is stricter, on purpose

FCUtils.isAuthorizedApiKeyHeader - used by FcSampleSseServlet - is a third spelling of this, and it is NOT the same rule: it goes through FrontCacheEngine.isNodeApiKey, which refuses a request when the node has no API key at all, where this returns true. Folding them together would have to pick one of those, and both directions are a behaviour change on somebody's node - the stream would open where it is closed today, or the management API would close where it is open. Left as it is, and written down here, because the difference is invisible at both call sites.

Read per request

Rather than cached at servlet init, so that reloading a node's configuration changes the key that is actually enforced. A property read per management request costs nothing measurable.
  • Field Details

    • API_KEY_PROPERTY

      public static final String API_KEY_PROPERTY
      Public so a refusal message can name the property the operator has to set.
      See Also:
  • Method Details

    • configured

      public static String configured()
      Returns:
      the configured API key, or "" when the node has none
    • isConfigured

      public static boolean isConfigured()
      Returns:
      true when this node has an API key at all. When it does not, the management API has no access control at all.
    • isAuthorized

      public static boolean isAuthorized(jakarta.servlet.http.HttpServletRequest request)
      Parameters:
      request - the inbound request
      Returns:
      true when the request carries this node's API key, or when the node has no API key configured (the historical default - an unconfigured node is open, and says so at startup rather than refusing traffic on upgrade)
    • isAuthorizedToWrite

      public static boolean isAuthorizedToWrite(jakarta.servlet.http.HttpServletRequest request)
      The stricter rule, for management calls that WRITE.

      Why an un-keyed node is refused here and allowed by isAuthorized(HttpServletRequest)

      An un-keyed node is open, deliberately, and that is tolerable for reads and for cache invalidation. Accepting arbitrary content into conf/guard-rules.conf (redirects, rate limits, allowed redirect hosts), conf/bots.conf (who is classified how, hence what is cached and for how long) or conf/resilience.properties (the timeouts and breakers in front of the origin) is a different thing: it is remote configuration of how the node handles requests. And the un-keyed state is usually accidental - a 2.9.0 upgrade that left front-cache.site-key in the properties file produces a node with no key at all and no failure to notice. Writes must not be what that state silently enables. This is the third rule in the family, alongside FCUtils.isAuthorizedApiKeyHeader for the SSE stream (see the class comment above), and it lives here so there is only one spelling of it.
      Returns:
      true when the node has an API key AND the request carries it
    • presented

      public static String presented(jakarta.servlet.http.HttpServletRequest request)
      The credential the caller presented, or null. Public because the SSE stream reads the same credential and applies a different rule to it (see the class javadoc) - so the two share how a caller SENDS the key while keeping their own answers about what it means.
      Parameters:
      request - the inbound request
      Returns:
      the API key the caller sent, by the current or the legacy route, or null