The Front Doors: Reverse Proxies, Authentication and Identity
Nothing in an HD instance authenticates a person. The Proxy, the query services, Data Explore and the DMS WebUX all trust a set of identity headers on the requests that reach them, and something in front of them has to establish who the caller is and set those headers. That something is a reverse proxy with an authentication step, and there are two of them, with different audiences. The lab and the reference deployment use Traefik for both; nothing in HD depends on Traefik, and in production both front doors are yours: replace them with the ingress you already run, or configure them, so long as they keep the contract on this page.
Two front doors, two audiences
| The DMS front door | The query front door | |
|---|---|---|
| Who comes through it | administrators, in a browser | query clients: SDK applications in proxy mode, SQL tools, dashboards, assistants, analysts, and the query services themselves. An SDK client in direct mode, and a custom ingester, do not come through it: they register with the DMS as HD_CLIENT nodes and talk to the storage cluster directly (querying) |
| What is behind it | the DMS WebUX (port 3000) | the HD Proxy (6986, or 443 as the alternate listener), the SQL server, the GraphQL services, the MCP server, Data Explore, and any GraphiQL or Grafana pages you publish |
| Identity comes from | your administrators' SSO | your users' SSO for people; mTLS client certificates for services and machine clients |
| Trust relationship | independent of the DMS-CA: it is never given a DMS-issued certificate | presents a client certificate to the Proxy, signed by a CA the Proxy is told to trust |
| In the lab | dms-traefik on port 8081, one route to the WebUX, a stand-in authenticator that answers "admin" for everyone |
traefik on port 80, one path prefix per service, x509-to-http-headers for certificate identities, and a stand-in that injects a fixed identity for the query services |
They are separate on purpose. The DMS front door guards the control plane of the instance and sees only administrators; the query front door sees every user of the data. Sharing one proxy between them is a configuration choice you can make, but the two should never share an authentication policy.
The identity contract
Every request a front door forwards carries these headers, set by the front door from the identity it established, and never taken from the client:
| Header | Carries | Who requires it |
|---|---|---|
X-Forwarded-User |
the caller's unique identifier | the Proxy and Data Explore refuse a request without it (401) |
X-Forwarded-Email |
the caller's email | policy input |
X-Forwarded-Preferred-Username |
a display name | policy input |
X-Forwarded-Groups |
group memberships, comma-separated | policy input |
X-Forwarded-Role |
the caller's role | policy input |
X-Forwarded-For, -Proto, -Host, -Uri, -Method |
the original request | logging and audit |
What each service does with them:
- The HD Proxy feeds the five identity headers, with the query itself,
into the policy the DMS distributes, and rewrites the query accordingly.
It authenticates nothing itself. It also requires the connection from the
front door to be mutual TLS: the front door presents a client certificate,
and the Proxy trusts the CA that signed it through
FERMIHDI_NORTHBOUND_CA_PEM. - Data Explore refuses a request without
X-Forwarded-Userand uses the identity for its own session and audit; the query it then issues goes to the Proxy with that identity. - The DMS WebUX applies no authorization of its own. Whoever the DMS front door lets through is an administrator. Its authentication policy is the whole of the WebUX's access control.
- The SQL server, the GraphQL services and the MCP server apply no authorization of their own either, beyond the optional password on the SQL listener and the optional bearer token on the IP-flow service. They call the Proxy as a service, through the query front door, with a service identity: the person using them is not propagated to the Proxy, so the policy that governs a query through one of these services is the policy for that service's identity. Where per-user policy matters, the caller uses the SDK or the Proxy directly through the front door.
Strip what the client sends. Because the services trust these headers
unconditionally, a front door that forwards a client-supplied
X-Forwarded-User lets the client choose its identity. Every front door must
delete incoming X-Forwarded-* identity headers before setting its own.
How the headers get set
Traefik's ForwardAuth is one way to set them: the proxy makes a sub-request
to an authenticator, which answers 200 with the identity headers, and the
proxy copies them onto the forwarded request. The reference deployment does
exactly that, with an OIDC forward-auth service for people and
x509-to-http-headers for certificate identities, which maps a client
certificate's Common Name to the headers through a small mapping file.
None of that is required. NGINX's auth_request, Envoy's external
authorization, Kong's or an API gateway's plugins, an identity-aware proxy
such as oauth2-proxy, or a cloud load balancer with an identity provider all
do the same job. What the services need is the outcome: an authenticated
caller, an authorization decision, and the headers on the forwarded request.
What you provide, and what you replace
A production deployment is expected to bring its own front doors. Neither the
installer's Ansible layer nor the Helm charts deploy one; the lab's are
stand-ins and the reference deployment under docs/examples is a starting
point. For each front door you provide:
- TLS for browsers and clients, from your own CA or a public one.
- Authentication against your identity provider: OIDC or SAML for people, mutual TLS for services and machine clients.
- Authorization of who may reach what: which groups may open the WebUX at all, which may reach the SQL server, which paths are published.
- The headers, set from the established identity and stripped from the incoming request.
- For the query front door, a client certificate presented to the Proxy,
whose CA is given to the Proxy as
FERMIHDI_NORTHBOUND_CA_PEM; and, when the services behind it call the Proxy, a service identity for each. - For the DMS front door, independence from the DMS-CA. It is the administrator's Traefik, or whatever replaces it, with its own certificates. The DMS never issues it one.
The internal ports the front door forwards to: the WebUX on 3000, the Proxy on 6986 (or its alternate 443), the SQL server's HTTP listener on 8081, the generic GraphQL service on 8083, the IP-flow GraphQL service on 8080, the MCP server on 8082 and its stream on 8083, Data Explore on 4321. The SQL server's PostgreSQL wire (5432) and Arrow Flight SQL (31337) listeners are not HTTP and pass through a TCP-level proxy or are published directly on a network your SQL clients reach.
The lab's stand-ins are not for production
The sandbox exists to exercise the data path, and it fakes the front doors to do it. None of the following may survive into a real deployment:
mock-forward-auth, which answers every request with the same administrator identity, on both front doors.mock-auth-proxy, which stands in for the query front door in front of the Proxy and injects one fixed service identity for every query service.- Traefik's dashboard published without authentication, and its
insecureSkipVerifytowards the services. - Plain HTTP on the entry points.
A deployment that copied the lab's compose file would let anyone who can reach port 8081 administer the DMS. The installer publishes the WebUX on the loopback address by default for this reason, until a front door exists.
Checking a front door
- A request to the Proxy or Data Explore with no identity headers is refused with 401. If it is not, the request did not come through the front door.
- A request that arrives with a client-supplied
X-Forwarded-Userreaches the service with the front door's value, not the client's. - The Proxy's log shows the identity it evaluated on each query; the WebUX front door's access log shows who opened the console.