Security Considerations

This page collects the security-relevant properties of the extension in one place. The details are on the individual provider pages; what follows is the overview and the rules that hold everywhere.

Evaluation is in-process

No provider in this extension performs a remote call to evaluate a flag. The remote-sync providers (flagd, Flipt, GO Feature Flag, Unleash) fetch the flag definitions from their server in the background and evaluate them locally; the configuration providers have no server at all.

The consequence that matters here is that no flag evaluation discloses anything. Targeting keys and any other attributes you put into the evaluation context are used locally and are never sent anywhere, and the extension reports no evaluation telemetry back to the flag server. Whatever you are allowed to put into an evaluation context is therefore not constrained by what you are willing to share with the flag server vendor.

An outage of the flag server does not change the values being served: the provider keeps evaluating the definitions it synchronized last, so losing the server cannot silently flip a flag. Flag evaluation may return a default value: for flags that were never synchronized, when the server rejects the provider’s credentials (see Application health), and, for the WebAssembly-based providers, when an evaluation times out waiting for an engine instance from a saturated pool.

Credentials

Only the flag server connection is authenticated, and only by those providers whose server supports it:

Provider Authentication

flagd

None. The flagd sync protocol defines no authentication mechanism; use TLS and network-level controls.

Flipt

quarkus.openfeature.flipt.auth-type and auth-token (client token). Authentication is off unless auth-type is set.

GO Feature Flag

quarkus.openfeature.gofeatureflag.api-key.

Unleash

quarkus.openfeature.unleash.api-key (backend token).

Configuration providers

None. Flag values come from Quarkus configuration; there is no connection.

Setting the token directly in configuration is fine for development and testing. In production, keep it out of application.properties and use a credentials provider such as Vault, through the credentials-provider and credentials-provider-name properties that each of those providers documents. Two things about that mechanism hold for all of them and are easy to get wrong:

  • The directly configured token wins. If both api-key (or auth-token) and credentials-provider are set, the directly configured value is used and the credentials provider is never consulted.

  • A credentials provider that resolves to nothing fails the application. If credentials-provider is set and the provider returns no password entry for that name, startup fails rather than falling back to an unauthenticated connection, which would otherwise hide the misconfiguration until the server rejected the request — or not at all, against a server that does not require authentication. For Flipt, this holds only once quarkus.openfeature.flipt.auth-type is set to client-token: auth-type is what turns authentication on at all, and with its default of none, both auth-token and credentials-provider are ignored without a warning and the connection is unauthenticated.

  • Credentials are read once, during application startup, when the provider is created. Rotating a token in the secret store does not affect a running application; it takes effect on the next restart.

The GO Feature Flag relay proxy authorizes its event stream from an apiKey query parameter and ignores the Authorization header, so that provider has to put the key in the request URI. Over HTTPS this is no more exposed on the wire than a header would be, because the whole request is inside TLS. The difference is at the endpoints: request URIs are written to access logs, and those logs are usually handled far more casually than a secret store. The relay proxy’s own access log includes the full request URI, and its log level decides whether the key is written: a successful connection is logged at debug level, so the valid key appears only when the proxy runs with logLevel: debug, but a connection that fails authorization is logged at warn level, so a rejected key is written out at the default log level. An expired or mistyped key therefore lands in the log of a proxy that is otherwise quiet. The same applies to every hop that terminates TLS on the way to the proxy — a reverse proxy, an L7 load balancer, an API gateway, a service mesh sidecar — each of which sees and usually logs the URI. So avoid terminating TLS between the application and the relay proxy, don’t run the proxy at debug level in production, and rotate a key that has been through a failed connection against a logging proxy.

TLS

Every remote-sync provider configures TLS through the Quarkus TLS registry and nothing else; there are no extension-specific certificate properties to review separately. Each provider page shows the tls-configuration-name property.

For the three HTTP providers, an https:// URL also enables TLS on its own, verifying the server against the JVM’s default trust store. flagd is different: its sync protocol is gRPC, so TLS is enabled only by tls-configuration-name and nothing else. To make sure that difference cannot turn into a silent downgrade, an https:// flagd URL is rejected at startup rather than connected to in cleartext.

For flagd, which has no authentication of its own, TLS is also the only thing that authenticates the server to the application, and mutual TLS the only way to authenticate the application to the server.

Dev UI and Dev Services

The Dev UI only exists in dev mode. Its JSON-RPC service lives in a separate artifact and the build step that registers it is guarded so that it does not run for a production build. There is therefore no endpoint in a production application that lists flags, evaluates them on request, or overrides their values.

Dev Services, which start a containerized flag server, are likewise guarded by launch mode and run in dev and test only. In production you always connect to the server you configured.

The @TestFlag overrides described in Testing use the same mechanism as the Dev UI overrides and are applied only by the JUnit extension, which is not on a production classpath.

Flag values are not a secret store

Flag definitions are replicated into the application and, in dev mode, listed in the Dev UI. With the build-time configuration provider, flag values are additionally baked into the built artifact and cannot be changed without rebuilding.

Do not put credentials, connection strings or other secrets into flag values. Use flags to decide whether a code path runs; use the Quarkus configuration and credentials mechanisms for the secrets that path needs.