-
Notifications
You must be signed in to change notification settings - Fork 3
Web App Installation
The Gateway needs a Twingate Network and other components (Client and Connector) to function. For a step-by-step setup from scratch, see the Quick Start Guide. See Overview for the underlying concepts.
The Gateway Helm Chart is a subchart of the Twingate Operator Helm Chart. Enable the subchart in your values.yaml to install the Gateway together with the Operator. Note that <network-name> is the name of your Twingate Network i.e. you access Twingate via https://<network-name>.twingate.com.
twingateOperator:
# Existing Twingate Operator configurations
network: "<network-name>"
remoteNetworkId: "<the ID of the Remote Network>"
gateway:
enabled: true # Enable the Gateway subchart
twingate:
network: "<network-name>"
tls:
dnsNames:
# Include the resource alias and address in the TLS certificate served by the Gateway
- "app.int" # resource alias
- "internal-app.default.svc.cluster.local" # resource address
webApp:
# Enable the web app proxy
enabled: true
# Headers injected into every proxied request, carrying the user's identity
requestHeaders:
Authorization: "Bearer {{jwt}}"Apply this configuration to install the Gateway:
helm upgrade twop oci://ghcr.io/twingate/helmcharts/twingate-operator --install --wait -f ./values.yamlThis installs the Gateway with the web app proxy enabled and configures the Operator to reconcile the web app resources you create below.
The Twingate Client reaches the Gateway over TLS, and the Gateway serves a certificate that the Client validates against the hostname it dials. The chart provisions a self-signed certificate into the <release>-gateway-tls Secret (for example twop-gateway-tls) and registers its CA with Twingate, so the Client trusts the Gateway without a public CA. The certificate's Subject Alternative Names (SANs) come from gateway.tls.dnsNames, and the Client accepts the connection only if the hostname it dials is listed there. Each resource's alias and address must therefore be covered by a SAN.
This TLS terminates between the Twingate Client and the Gateway. In the browser you still use plain http://<alias>; the Client intercepts that request and tunnels it to the Gateway over the TLS connection above.
Listing every alias and address individually means reissuing the certificate each time you add an app. Wildcard SANs cover many hostnames at once, so a single certificate can serve all your web apps. Adopt a shared suffix such as acme.internal for your aliases, and cover the cluster-internal addresses with a wildcard:
gateway:
tls:
dnsNames:
- "*.acme.internal" # all app aliases
- "*.svc.cluster.local" # all app addressesEach web app stays a concrete resource with a concrete alias (for example billing.acme.internal) and address (for example billing.default.svc.cluster.local). Both are covered by the wildcards, so adding an app needs no certificate change.
The alias users type must be concrete, but a resource's address may be a wildcard, letting a single Web App resource reach many upstreams that share a naming pattern:
apiVersion: twingate.com/v1beta
kind: TwingateResource
metadata:
name: tenant-app
spec:
type: WebApp
name: "Tenant App"
alias: "tenant-app.acme.internal"
address: "*.svc.cluster.local"
gatewayRef:
name: twop-gateway
downstream:
port: 80
upstream:
port: 8080The wildcard address must be covered by a matching SAN in tls.dnsNames (here, *.svc.cluster.local).
The Gateway forwards the Twingate user's identity to the upstream application by injecting HTTP request headers. Headers are defined in two places.
webApp.requestHeaders apply to every web app the Gateway proxies:
webApp:
requestHeaders:
Authorization: "Bearer {{jwt}}"Note
An application consumes the forwarded identity in one of two ways, so configure the headers for one approach rather than both:
-
Verify the signed JWT: inject
Authorization: Bearer {{jwt}}and validate it (see The forwarded token). -
Trust the Gateway and read plain headers: inject
X-Twingate-User: {{username}}andX-Twingate-Groups: {{groups}}, which the application reads directly.
See the Overview for the trade-offs.
Header values support the following identity placeholders:
-
{{jwt}}- the signed GAT carrying the full identity (see The forwarded token) -
{{username}}- the user's Twingate username -
{{groups}}- the user's groups, comma-separated -
{{clientGeoLatLong}},{{clientGeoCity}},{{clientGeoRegion}},{{clientGeoCountry}}- client geo attributes
requestHeaderRewrites are configured on an individual resource and apply only to that application, letting it receive a header in the exact form it expects. They are applied after the Gateway-wide headers, so they override a header of the same name.
With the Twingate Operator, set them on the TwingateResource as a list of name/value pairs:
requestHeaderRewrites:
- name: X-ACME-Token
value: "Bearer {{ jwt }}"You can also manage these rewrites directly on the resource in the Twingate Admin Console or via the Twingate API.
{{jwt}} injects the GAT itself: a JWT signed by Twingate (ES256) whose claims include the username, groups, device geo, and the resource being accessed. The upstream application verifies it against Twingate's public keys published at https://<network-name>.twingate.com/api/v1/jwk/ec, checking the issuer, the audience (your network), and the token type (GAT). Because the token is signed, the application can trust the identity without a shared secret.
The GAT is a standard JWT in compact form (header.payload.signature). Decoded header:
{
"alg": "ES256",
"typ": "GAT",
"kid": "a1b2c3d4"
}Decoded payload (irrelevant claims are omitted for brevity):
{
"iss": "twingate",
"aud": "acme",
"iat": 1735688700,
"exp": 1735689600,
"ver": "1",
"user": {
"id": "VXNlcjoxMjM0NQ==",
"username": "alex@acme.com",
"groups": ["engineering", "on-call"]
},
"device": {
"id": "RGV2aWNlOjk4NzY=",
"location": {
"lat": 37.77,
"lon": -122.42,
"country": "US",
"region": "California",
"city": "San Francisco"
}
},
"resource": {
"id": "UmVzb3VyY2U6NTU1",
"type": "WEB_APP",
"address": "internal-app.default.svc.cluster.local",
"aliases": ["app.int"],
"gateway_metadata": {
"downstream": { "port": 80 },
"upstream": { "port": 8000 }
}
}
}Create a TwingateResource of type WebApp. The operator reconciles it into a Twingate resource bound to the Gateway via gatewayRef.
apiVersion: twingate.com/v1beta
kind: TwingateResource
metadata:
name: acme-app
spec:
type: WebApp
name: "ACME App"
address: "internal-app.default.svc.cluster.local"
alias: "app.int"
gatewayRef:
name: twop-gateway
downstream:
port: 80 # browser-facing port
upstream:
port: 8000 # port the application listens on
requestHeaderRewrites: # optional per-resource headers
- name: X-ACME-Token
value: "Bearer {{ jwt }}"-
addressis the address of the application that the Gateway can reach. It must be included in the Gateway'stls.dnsNames. -
aliasis the friendly DNS name users type in their browser. It must also be included in the Gateway'stls.dnsNames. -
downstream.portis the browser-facing port;upstream.portis the port the application listens on. -
requestHeaderRewritesare per-resource headers injected in addition to the Gateway-widerequestHeaders.
Apply the resource:
kubectl apply -f resource.yamlAfter a few moments, a resource should be created and visible in Twingate Admin Console. Assign your user or group to the resource.
Instead of a standalone TwingateResource, you can annotate the application's Service and let the operator create the resource for you. Set the ports with the resource.twingate.com/downstreamPort and resource.twingate.com/upstreamPort annotations; upstreamPort defaults to the Service's port when it exposes a single one. See the operator's Annotating a Service guide for the full list.
Copyright © 2025 Twingate.
Kubernetes
SSH
Web App
Operations
Development
Migration