Application Access

Mezite can publish internal applications — a Grafana dashboard, an internal wiki, a private API, a Redis instance — to your users without a VPN and without opening any inbound port into the network where the app runs. The mezd agent next to the application keeps an outbound reverse tunnel to the proxy; the proxy authenticates the user in the browser via your SSO connector, checks role-based access, and forwards the request down the tunnel.

Each enrolled application gets:

  • A public hostname (https://grafana.apps.example.com) gated by your existing SSO login and, when a role demands it, per-session MFA.
  • Label-based RBAC — users only see and reach the apps their roles allow.
  • A signed identity assertion on every forwarded request, so the backend can trust who the caller is without running its own SSO.
  • An audit trail of every request and session.

Prerequisites

Self-hosted clusters need three things before enrolling apps:

  • An apps domain. Pick a parent domain for app hostnames (e.g. apps.example.com) and publish a wildcard DNS record *.apps.example.com pointing at the proxy.
  • A wildcard TLS certificate covering *.apps.example.com — or a multi-SAN certificate listing each app hostname, if your policy forbids wildcards.
  • Configuration in mezhub.yaml:
mezhub.yaml yaml
proxy:
  apps_domain: apps.example.com
  # Optional: a dedicated keypair for the apps wildcard. If omitted, the
  # proxy's main HTTPS certificate must cover *.apps.example.com in its SANs.
  apps_keypair:
    cert_file: /etc/mezite/apps-wildcard.crt
    key_file: /etc/mezite/apps-wildcard.key
  # Optional: which SSO connector gates browser app access.
  apps_oidc_connector: sso

If apps_domain is unset, application access is inactive — nothing else about the cluster changes.

On managed clusters all of this is pre-provisioned: the apps domain is your cluster's own hostname, the wildcard DNS record and certificate are issued automatically, and apps are reachable at <app>.<cluster>.hub.mezite.com.


Enroll an application

Applications are registered centrally — with mezctl or in the web dashboard under Applications — and served by one or more agents you bind to them. The agent needs network reach to the app's internal address; nothing app-specific is installed on the agent host.

Register an internal web app bash
mezctl apps register \
  --name grafana \
  --uri http://10.0.3.12:3000 \
  --public-addr grafana.apps.example.com \
  --labels env=prod,team=platform \
  --agent-hostname node-1 \
  --agent-hostname node-2
  • --uri is the internal address the agent dials — it never appears in DNS and needs no public route.
  • --public-addr must be a subdomain of the configured apps_domain. The apex itself, hosts outside the domain, and host:port values are rejected.
  • --agent-hostname is repeatable: bind two or more agents and the proxy load-balances across whichever are connected, falling through to the next live agent if a dial fails. An app with zero bound agents stays registered but returns 502.
  • --labels drive RBAC (below). Use --protocol=tcp for non-HTTP services.

Manage the serving set without re-registering via mezctl apps add-agent / mezctl apps rm-agent, and inspect state with mezctl apps ls and mezctl apps get <name>. Take an app offline temporarily with mezctl apps disable <name> (re-enable with enable), or remove it with mezctl apps rm <name>.


Browser access

Users browse straight to the app — https://grafana.apps.example.com — or click Launch on the Apps page of the dashboard. An unauthenticated visit redirects through your SSO connector, then back to the app with a short-lived session scoped to that app's hostname only. Roles that set require_session_mfa are prompted for a WebAuthn assertion before the session is created.

Sessions are revocable server-side: locking a user or revoking the session cuts off app access immediately.


Trusting the caller's identity in your app

Every forwarded request carries a short-lived, asymmetrically signed JWT in the Mezite-Jwt-Assertion header, plus convenience headers X-Mezite-User and X-Mezite-Roles. These headers are always overwritten by the proxy, so a client can never spoof them. The JWT's aud claim is the app's internal URI; sub is the username, and the user's roles ride along as a claim.

Backends verify the JWT against the cluster's public key set:

Fetch the verification keys bash
# Online — from the proxy:
curl https://mezite.example.com/workload-identity/app-jwt-jwks.json

# Offline — for backends with no outbound internet:
mezctl auth export --type=app-jwt > app-jwt-jwks.json

Verification is optional — apps that don't check the JWT still get the SSO gate, RBAC, and audit. Checking it protects against traffic that reaches the backend from elsewhere inside the network.


TCP applications

Non-HTTP services tunnel through a local listener. Register the app with --protocol=tcp, then:

Open a TCP tunnel bash
msh app login redis-internal --port 6380
# Tunnel open: 127.0.0.1:6380 -> redis-internal
redis-cli -p 6380 ping

The tunnel authenticates with the certificate from your msh login session and is authorized per-app at the proxy. Roles that require per-session MFA or a trusted device are refused on the TCP path rather than silently downgraded. List the apps you can reach with msh app ls.


Restricting access with labels

Roles match application labels the same way they match node labels — with app_labels under allow or deny:

role: platform-engineer yaml
spec:
  allow:
    app_labels:
      env: ["prod", "staging"]
      team: ["platform"]

Users without a matching role don't see the app in the launcher or msh app ls, and direct requests are denied with an audit record.


Audit

Application activity lands in the cluster audit log:

  • app.request — every forwarded HTTP request.
  • app.session.start / app.session.end — bracket TCP sessions with bytes and duration.
  • access.denied — RBAC rejections, including TCP attempts against MFA- or device-trust-protected apps.
  • app.enabled / app.disabled — operator toggles.
mezctl audit ls --type=app.request

Next steps

  • RBAC — Role syntax, label matchers, and per-session MFA options.
  • SSO (OIDC) — Configure the connector that gates browser app access.
  • Audit Logging — Query and export application audit events.