Stop Exposing Your Kubernetes API: Secure kubectl Access in Under 30 Minutes
Andrew Baumbach
•
Product Marketing Engineer
•

Replace public API endpoints and jumpbox hops with identity-based kubectl access. This walkthrough covers Twingate deployment, RBAC mapping, and CNI-level pod isolation.
If your cluster's API server sits behind 0.0.0.0/0 with an OIDC token as the only thing between it and the internet, you already know the argument for changing that.
The question is whether the replacement is worth the effort. This is a walkthrough for making it worth the effort in an afternoon.
Why the default kubectl setup is a problem
There are three common ways teams reach the Kubernetes API server, and all three have real drawbacks:
Public API endpoint with authorized networks. EKS, GKE, and AKS all support IP allowlists. This falls apart the moment engineers work from coffee shops, home connections with dynamic IPs, or anywhere behind CGNAT. Teams end up either widening the allowlist or maintaining a corporate VPN just to get a predictable egress IP.
Private endpoint plus VPN. Works, but you're now maintaining a VPN concentrator, distributing WireGuard configs or OpenVPN profiles, and getting a network-level tunnel where you only wanted API access. Anyone on the VPN can port-scan the VPC.
Bastion host with SSH tunneling.
kubectlthrough anssh -Lport-forward. Works for one engineer. Doesn't scale to a team of thirty, and the audit trail is a mess of~/.ssh/authorized_keysfiles.
The pattern underneath all three: you're granting network access when what you actually want is application access to one specific endpoint (the API server) for one specific identity.
What we're building
A setup where:
The Kubernetes API server has no public endpoint
Engineers run
kubectlnormally from their laptops, no VPN, no jumpboxAuthentication ties to your existing IdP (Okta, Google Workspace, Entra ID, JumpCloud)
Authorization inside the cluster uses standard Kubernetes RBAC mapped to IdP groups
Pod-to-pod traffic is restricted by CNI network policies, independent of who's connecting from outside
Every kubectl session is logged and attributable to a specific user
Three layers, each with a clear job:
Layer | Tool | What it controls |
|---|---|---|
Access | Twingate | Who can reach the API server endpoint |
Authorization | Kubernetes RBAC | What actions that identity can perform |
Pod isolation | CNI NetworkPolicy | What pods can talk to each other |
Skip any one of these and you have a gap. Do all three and a compromised laptop can't pivot into the cluster's internal network.
Prerequisites
A Kubernetes cluster (this guide assumes EKS, but GKE, AKS, and self-managed work the same way)
Cluster admin access to install a Helm chart
A Twingate account (the free tier covers up to 5 users)
An IdP already connected to Twingate (Okta, Google, Entra, JumpCloud, or OIDC)
kubectl,helm, and the Twingate Client installed locally
If you're on a managed cluster with a public endpoint, we'll disable that at the end. Don't do it first, or you'll lock yourself out.
Step 1: Create a Remote Network and Connectors in Twingate
A Remote Network in Twingate represents a network segment where private Resources live. For a Kubernetes cluster, that's the VPC (or equivalent) where the API server and nodes run.
In the Twingate admin console:
Go to Network → Remote Networks → Add Remote Network
Name it something meaningful —
prod-eks-us-east-1beatsnetwork-1Choose the location that matches your cluster's cloud provider
Once created, add two Connectors. Two isn't a suggestion; it's what keeps kubectl working during Connector restarts and upgrades.
Copy the access tokens for both Connectors. You'll pass these to the Helm chart in the next step.
Step 2: Deploy Connectors into the cluster
Connectors are the outbound-only proxies that give Twingate a foothold in your VPC. They dial out to Twingate's edge, nothing dials in.
Deploying them as pods inside the cluster you're trying to secure is the cleanest option, with no separate VMs to patch.
Add the Twingate Helm repo:
Deploy the first Connector. Replace the network name and tokens with your own:
helm install connector-1 twingate/connector \ --namespace twingate \ --create-namespace \ --set network=your-network.twingate.com \ --set accessToken=<CONNECTOR_1_ACCESS_TOKEN> \ --set refreshToken=<CONNECTOR_1_REFRESH_TOKEN>
Deploy the second Connector with its own tokens:
helm install connector-2 twingate/connector \ --namespace twingate \ --set network=your-network.twingate.com \ --set accessToken=<CONNECTOR_2_ACCESS_TOKEN> \ --set refreshToken=<CONNECTOR_2_REFRESH_TOKEN>
Confirm both pods are running and marked "Connected" in the Twingate admin console:
If a pod is stuck in CrashLoopBackOff, the usual culprit is the network egress path. Connectors need outbound access on 443 and UDP for QUIC-based peer-to-peer. If your cluster egresses through a NAT gateway with restrictive rules, allow outbound to *.twingate.com.
Step 3: Register the API server as a Resource
Now the interesting part. In Twingate, a Resource is a specific endpoint (a hostname, IP, or CIDR) that authorized users can reach through a Connector.
This is what makes the access model resource-based instead of network-based: adding a user to the "Kubernetes access" group doesn't put them on the VPC, it lets them talk to exactly one endpoint.
Find your cluster's internal API server address. For EKS with a private endpoint enabled:
You'll get something like https://ABC123DEF456.gr7.us-east-1.eks.amazonaws.com. Note the hostname.
In the Twingate admin console:
Go to Network → Resources → Add Resource
Assign it to the
prod-eks-us-east-1Remote NetworkSet the address to the API server hostname
Restrict the port to 443
Save
Then create a Group in Twingate called something like kubernetes-prod-users, assign your engineers (or sync from your IdP), and grant that group access to the Resource. If you're using an IdP-synced group, changes to group membership propagate without touching Twingate directly.
Step 4: Test kubectl from a laptop
Install the Twingate Client if you haven't yet, sign in, and verify the Client shows the Resource as accessible.
Then update your kubeconfig. On EKS:
And try a simple call:
If it works, you're routing through Twingate. If it doesn't, the two things to check are:
The Twingate Client is signed in and the Resource shows as available
Your kubeconfig points at the private endpoint, not a stale public one
The Twingate Client intercepts DNS queries that match a Resource address and routes them through the Connector. You don't need to change kubectl, kubelogin, or any tooling. The endpoint hostname resolves through the Client automatically.
Step 5: Map identities to RBAC
Twingate got you to the API server. Kubernetes RBAC decides what you can do once you're there. These are separate concerns, and mixing them up leads to over-permissive clusters.
If you're using EKS with IAM authentication, you can map an IAM role or user to a Kubernetes group in aws-auth. If you're using OIDC (the cleaner path for tying kubectl identity to your IdP), configure the API server's OIDC flags to trust your IdP's issuer and use kubectl oidc-login or similar for token exchange.
A minimal RBAC setup for a read-only developer group:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: developer-readonly rules: - apiGroups: [""] resources: ["pods", "services", "configmaps"] verbs: ["get", "list", "watch"] - apiGroups: ["apps"] resources: ["deployments", "replicasets"] verbs: ["get", "list", "watch"]
Apply it:
The key idea: Twingate answers "can this identity reach the API server?" RBAC answers "what can this identity do with the API server?" You need both.
Step 6: Lock down pod traffic with NetworkPolicy
Even with tight external access, an app-level compromise inside the cluster can move sideways unless pod traffic is restricted. This is a CNI concern, not a Twingate one, but it belongs in the same threat model.
Assuming you're running Calico, Cilium, or another CNI that supports NetworkPolicy, start with a default-deny policy in each application namespace:
Then explicitly allow the traffic each service actually needs. For a web tier that talks to a database:
If you skip this step, an attacker who lands in one pod can freely reach every other pod in the cluster. External access controls don't help once someone's already inside.
Step 7: Turn off the public API endpoint
This is the moment the setup actually matters. In the AWS console, or with the CLI:
For GKE, the equivalent is enabling a private cluster and disabling the public endpoint. For AKS, use az aks update with --enable-private-cluster.
Give it a few minutes for the API server config to converge, then test kubectl again with the Twingate Client running. If it works, you're done. If it doesn't, re-enable public access, check your Resource and Group configuration in Twingate, and try again.
What you actually get
No public API endpoint. The cluster is invisible from the internet. There's nothing to scan, nothing to brute-force.
No VPN. Engineers don't sit on the VPC. A compromised laptop doesn't have
10.0.0.0/8reachability.Identity-first access. Someone leaves the company, you disable them in your IdP, and their kubectl access is gone.
Audit trail per session. Twingate logs which user connected to which Resource and when. Combined with Kubernetes audit logs, you can reconstruct who did what and from where.
Multiple clusters, same pattern. Add a Resource per cluster, put them in the same or different Groups. No new VPN, no new bastion.
Common mistakes
Forgetting the second Connector. One Connector is a single point of failure. When it restarts for a version bump, kubectl hangs. Always deploy at least two.
Leaving authorized_networks in place. If you were previously using an IP allowlist, remove it after moving to private endpoints. Leaving it in a partial state creates confusing failure modes when someone's IP changes.
Mixing up Twingate Groups and Kubernetes Groups. Twingate Groups control who can reach the endpoint. Kubernetes Groups (via OIDC claims or aws-auth) control what they can do. Use both. Name them clearly so nobody grants cluster-admin by editing the wrong config.
Treating NetworkPolicy as optional. External access controls don't stop lateral movement inside the cluster. If you don't have default-deny NetworkPolicies, one compromised pod is a whole-cluster compromise.
For homelab clusters
The same pattern works on a k3s or microk8s cluster running on a Raspberry Pi or a spare desktop. Deploy a single Connector (or two, if you have the RAM), register the API server IP as a Resource, and you can kubectl into your homelab from anywhere without exposing the endpoint to your ISP's dynamic IP or running Tailscale on every device. The free tier of Twingate covers this comfortably for personal use.
Closing
For deployment specifics, RBAC integration options, and the Twingate Kubernetes Operator (which lets you manage Resources declaratively via CRDs), see the Twingate Kubernetes documentation.
New to Twingate? You can use Twingate for free for up to 5 users, request a personalized demo, or reach out to the team over on the Twingate subreddit.
Rapidly implement a modern Zero Trust network that is more secure and maintainable than VPNs.
Stop Exposing Your Kubernetes API: Secure kubectl Access in Under 30 Minutes
Andrew Baumbach
•
Product Marketing Engineer
•

Replace public API endpoints and jumpbox hops with identity-based kubectl access. This walkthrough covers Twingate deployment, RBAC mapping, and CNI-level pod isolation.
If your cluster's API server sits behind 0.0.0.0/0 with an OIDC token as the only thing between it and the internet, you already know the argument for changing that.
The question is whether the replacement is worth the effort. This is a walkthrough for making it worth the effort in an afternoon.
Why the default kubectl setup is a problem
There are three common ways teams reach the Kubernetes API server, and all three have real drawbacks:
Public API endpoint with authorized networks. EKS, GKE, and AKS all support IP allowlists. This falls apart the moment engineers work from coffee shops, home connections with dynamic IPs, or anywhere behind CGNAT. Teams end up either widening the allowlist or maintaining a corporate VPN just to get a predictable egress IP.
Private endpoint plus VPN. Works, but you're now maintaining a VPN concentrator, distributing WireGuard configs or OpenVPN profiles, and getting a network-level tunnel where you only wanted API access. Anyone on the VPN can port-scan the VPC.
Bastion host with SSH tunneling.
kubectlthrough anssh -Lport-forward. Works for one engineer. Doesn't scale to a team of thirty, and the audit trail is a mess of~/.ssh/authorized_keysfiles.
The pattern underneath all three: you're granting network access when what you actually want is application access to one specific endpoint (the API server) for one specific identity.
What we're building
A setup where:
The Kubernetes API server has no public endpoint
Engineers run
kubectlnormally from their laptops, no VPN, no jumpboxAuthentication ties to your existing IdP (Okta, Google Workspace, Entra ID, JumpCloud)
Authorization inside the cluster uses standard Kubernetes RBAC mapped to IdP groups
Pod-to-pod traffic is restricted by CNI network policies, independent of who's connecting from outside
Every kubectl session is logged and attributable to a specific user
Three layers, each with a clear job:
Layer | Tool | What it controls |
|---|---|---|
Access | Twingate | Who can reach the API server endpoint |
Authorization | Kubernetes RBAC | What actions that identity can perform |
Pod isolation | CNI NetworkPolicy | What pods can talk to each other |
Skip any one of these and you have a gap. Do all three and a compromised laptop can't pivot into the cluster's internal network.
Prerequisites
A Kubernetes cluster (this guide assumes EKS, but GKE, AKS, and self-managed work the same way)
Cluster admin access to install a Helm chart
A Twingate account (the free tier covers up to 5 users)
An IdP already connected to Twingate (Okta, Google, Entra, JumpCloud, or OIDC)
kubectl,helm, and the Twingate Client installed locally
If you're on a managed cluster with a public endpoint, we'll disable that at the end. Don't do it first, or you'll lock yourself out.
Step 1: Create a Remote Network and Connectors in Twingate
A Remote Network in Twingate represents a network segment where private Resources live. For a Kubernetes cluster, that's the VPC (or equivalent) where the API server and nodes run.
In the Twingate admin console:
Go to Network → Remote Networks → Add Remote Network
Name it something meaningful —
prod-eks-us-east-1beatsnetwork-1Choose the location that matches your cluster's cloud provider
Once created, add two Connectors. Two isn't a suggestion; it's what keeps kubectl working during Connector restarts and upgrades.
Copy the access tokens for both Connectors. You'll pass these to the Helm chart in the next step.
Step 2: Deploy Connectors into the cluster
Connectors are the outbound-only proxies that give Twingate a foothold in your VPC. They dial out to Twingate's edge, nothing dials in.
Deploying them as pods inside the cluster you're trying to secure is the cleanest option, with no separate VMs to patch.
Add the Twingate Helm repo:
Deploy the first Connector. Replace the network name and tokens with your own:
helm install connector-1 twingate/connector \ --namespace twingate \ --create-namespace \ --set network=your-network.twingate.com \ --set accessToken=<CONNECTOR_1_ACCESS_TOKEN> \ --set refreshToken=<CONNECTOR_1_REFRESH_TOKEN>
Deploy the second Connector with its own tokens:
helm install connector-2 twingate/connector \ --namespace twingate \ --set network=your-network.twingate.com \ --set accessToken=<CONNECTOR_2_ACCESS_TOKEN> \ --set refreshToken=<CONNECTOR_2_REFRESH_TOKEN>
Confirm both pods are running and marked "Connected" in the Twingate admin console:
If a pod is stuck in CrashLoopBackOff, the usual culprit is the network egress path. Connectors need outbound access on 443 and UDP for QUIC-based peer-to-peer. If your cluster egresses through a NAT gateway with restrictive rules, allow outbound to *.twingate.com.
Step 3: Register the API server as a Resource
Now the interesting part. In Twingate, a Resource is a specific endpoint (a hostname, IP, or CIDR) that authorized users can reach through a Connector.
This is what makes the access model resource-based instead of network-based: adding a user to the "Kubernetes access" group doesn't put them on the VPC, it lets them talk to exactly one endpoint.
Find your cluster's internal API server address. For EKS with a private endpoint enabled:
You'll get something like https://ABC123DEF456.gr7.us-east-1.eks.amazonaws.com. Note the hostname.
In the Twingate admin console:
Go to Network → Resources → Add Resource
Assign it to the
prod-eks-us-east-1Remote NetworkSet the address to the API server hostname
Restrict the port to 443
Save
Then create a Group in Twingate called something like kubernetes-prod-users, assign your engineers (or sync from your IdP), and grant that group access to the Resource. If you're using an IdP-synced group, changes to group membership propagate without touching Twingate directly.
Step 4: Test kubectl from a laptop
Install the Twingate Client if you haven't yet, sign in, and verify the Client shows the Resource as accessible.
Then update your kubeconfig. On EKS:
And try a simple call:
If it works, you're routing through Twingate. If it doesn't, the two things to check are:
The Twingate Client is signed in and the Resource shows as available
Your kubeconfig points at the private endpoint, not a stale public one
The Twingate Client intercepts DNS queries that match a Resource address and routes them through the Connector. You don't need to change kubectl, kubelogin, or any tooling. The endpoint hostname resolves through the Client automatically.
Step 5: Map identities to RBAC
Twingate got you to the API server. Kubernetes RBAC decides what you can do once you're there. These are separate concerns, and mixing them up leads to over-permissive clusters.
If you're using EKS with IAM authentication, you can map an IAM role or user to a Kubernetes group in aws-auth. If you're using OIDC (the cleaner path for tying kubectl identity to your IdP), configure the API server's OIDC flags to trust your IdP's issuer and use kubectl oidc-login or similar for token exchange.
A minimal RBAC setup for a read-only developer group:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: developer-readonly rules: - apiGroups: [""] resources: ["pods", "services", "configmaps"] verbs: ["get", "list", "watch"] - apiGroups: ["apps"] resources: ["deployments", "replicasets"] verbs: ["get", "list", "watch"]
Apply it:
The key idea: Twingate answers "can this identity reach the API server?" RBAC answers "what can this identity do with the API server?" You need both.
Step 6: Lock down pod traffic with NetworkPolicy
Even with tight external access, an app-level compromise inside the cluster can move sideways unless pod traffic is restricted. This is a CNI concern, not a Twingate one, but it belongs in the same threat model.
Assuming you're running Calico, Cilium, or another CNI that supports NetworkPolicy, start with a default-deny policy in each application namespace:
Then explicitly allow the traffic each service actually needs. For a web tier that talks to a database:
If you skip this step, an attacker who lands in one pod can freely reach every other pod in the cluster. External access controls don't help once someone's already inside.
Step 7: Turn off the public API endpoint
This is the moment the setup actually matters. In the AWS console, or with the CLI:
For GKE, the equivalent is enabling a private cluster and disabling the public endpoint. For AKS, use az aks update with --enable-private-cluster.
Give it a few minutes for the API server config to converge, then test kubectl again with the Twingate Client running. If it works, you're done. If it doesn't, re-enable public access, check your Resource and Group configuration in Twingate, and try again.
What you actually get
No public API endpoint. The cluster is invisible from the internet. There's nothing to scan, nothing to brute-force.
No VPN. Engineers don't sit on the VPC. A compromised laptop doesn't have
10.0.0.0/8reachability.Identity-first access. Someone leaves the company, you disable them in your IdP, and their kubectl access is gone.
Audit trail per session. Twingate logs which user connected to which Resource and when. Combined with Kubernetes audit logs, you can reconstruct who did what and from where.
Multiple clusters, same pattern. Add a Resource per cluster, put them in the same or different Groups. No new VPN, no new bastion.
Common mistakes
Forgetting the second Connector. One Connector is a single point of failure. When it restarts for a version bump, kubectl hangs. Always deploy at least two.
Leaving authorized_networks in place. If you were previously using an IP allowlist, remove it after moving to private endpoints. Leaving it in a partial state creates confusing failure modes when someone's IP changes.
Mixing up Twingate Groups and Kubernetes Groups. Twingate Groups control who can reach the endpoint. Kubernetes Groups (via OIDC claims or aws-auth) control what they can do. Use both. Name them clearly so nobody grants cluster-admin by editing the wrong config.
Treating NetworkPolicy as optional. External access controls don't stop lateral movement inside the cluster. If you don't have default-deny NetworkPolicies, one compromised pod is a whole-cluster compromise.
For homelab clusters
The same pattern works on a k3s or microk8s cluster running on a Raspberry Pi or a spare desktop. Deploy a single Connector (or two, if you have the RAM), register the API server IP as a Resource, and you can kubectl into your homelab from anywhere without exposing the endpoint to your ISP's dynamic IP or running Tailscale on every device. The free tier of Twingate covers this comfortably for personal use.
Closing
For deployment specifics, RBAC integration options, and the Twingate Kubernetes Operator (which lets you manage Resources declaratively via CRDs), see the Twingate Kubernetes documentation.
New to Twingate? You can use Twingate for free for up to 5 users, request a personalized demo, or reach out to the team over on the Twingate subreddit.
Rapidly implement a modern Zero Trust network that is more secure and maintainable than VPNs.
Stop Exposing Your Kubernetes API: Secure kubectl Access in Under 30 Minutes
Andrew Baumbach
•
Product Marketing Engineer
•

Replace public API endpoints and jumpbox hops with identity-based kubectl access. This walkthrough covers Twingate deployment, RBAC mapping, and CNI-level pod isolation.
If your cluster's API server sits behind 0.0.0.0/0 with an OIDC token as the only thing between it and the internet, you already know the argument for changing that.
The question is whether the replacement is worth the effort. This is a walkthrough for making it worth the effort in an afternoon.
Why the default kubectl setup is a problem
There are three common ways teams reach the Kubernetes API server, and all three have real drawbacks:
Public API endpoint with authorized networks. EKS, GKE, and AKS all support IP allowlists. This falls apart the moment engineers work from coffee shops, home connections with dynamic IPs, or anywhere behind CGNAT. Teams end up either widening the allowlist or maintaining a corporate VPN just to get a predictable egress IP.
Private endpoint plus VPN. Works, but you're now maintaining a VPN concentrator, distributing WireGuard configs or OpenVPN profiles, and getting a network-level tunnel where you only wanted API access. Anyone on the VPN can port-scan the VPC.
Bastion host with SSH tunneling.
kubectlthrough anssh -Lport-forward. Works for one engineer. Doesn't scale to a team of thirty, and the audit trail is a mess of~/.ssh/authorized_keysfiles.
The pattern underneath all three: you're granting network access when what you actually want is application access to one specific endpoint (the API server) for one specific identity.
What we're building
A setup where:
The Kubernetes API server has no public endpoint
Engineers run
kubectlnormally from their laptops, no VPN, no jumpboxAuthentication ties to your existing IdP (Okta, Google Workspace, Entra ID, JumpCloud)
Authorization inside the cluster uses standard Kubernetes RBAC mapped to IdP groups
Pod-to-pod traffic is restricted by CNI network policies, independent of who's connecting from outside
Every kubectl session is logged and attributable to a specific user
Three layers, each with a clear job:
Layer | Tool | What it controls |
|---|---|---|
Access | Twingate | Who can reach the API server endpoint |
Authorization | Kubernetes RBAC | What actions that identity can perform |
Pod isolation | CNI NetworkPolicy | What pods can talk to each other |
Skip any one of these and you have a gap. Do all three and a compromised laptop can't pivot into the cluster's internal network.
Prerequisites
A Kubernetes cluster (this guide assumes EKS, but GKE, AKS, and self-managed work the same way)
Cluster admin access to install a Helm chart
A Twingate account (the free tier covers up to 5 users)
An IdP already connected to Twingate (Okta, Google, Entra, JumpCloud, or OIDC)
kubectl,helm, and the Twingate Client installed locally
If you're on a managed cluster with a public endpoint, we'll disable that at the end. Don't do it first, or you'll lock yourself out.
Step 1: Create a Remote Network and Connectors in Twingate
A Remote Network in Twingate represents a network segment where private Resources live. For a Kubernetes cluster, that's the VPC (or equivalent) where the API server and nodes run.
In the Twingate admin console:
Go to Network → Remote Networks → Add Remote Network
Name it something meaningful —
prod-eks-us-east-1beatsnetwork-1Choose the location that matches your cluster's cloud provider
Once created, add two Connectors. Two isn't a suggestion; it's what keeps kubectl working during Connector restarts and upgrades.
Copy the access tokens for both Connectors. You'll pass these to the Helm chart in the next step.
Step 2: Deploy Connectors into the cluster
Connectors are the outbound-only proxies that give Twingate a foothold in your VPC. They dial out to Twingate's edge, nothing dials in.
Deploying them as pods inside the cluster you're trying to secure is the cleanest option, with no separate VMs to patch.
Add the Twingate Helm repo:
Deploy the first Connector. Replace the network name and tokens with your own:
helm install connector-1 twingate/connector \ --namespace twingate \ --create-namespace \ --set network=your-network.twingate.com \ --set accessToken=<CONNECTOR_1_ACCESS_TOKEN> \ --set refreshToken=<CONNECTOR_1_REFRESH_TOKEN>
Deploy the second Connector with its own tokens:
helm install connector-2 twingate/connector \ --namespace twingate \ --set network=your-network.twingate.com \ --set accessToken=<CONNECTOR_2_ACCESS_TOKEN> \ --set refreshToken=<CONNECTOR_2_REFRESH_TOKEN>
Confirm both pods are running and marked "Connected" in the Twingate admin console:
If a pod is stuck in CrashLoopBackOff, the usual culprit is the network egress path. Connectors need outbound access on 443 and UDP for QUIC-based peer-to-peer. If your cluster egresses through a NAT gateway with restrictive rules, allow outbound to *.twingate.com.
Step 3: Register the API server as a Resource
Now the interesting part. In Twingate, a Resource is a specific endpoint (a hostname, IP, or CIDR) that authorized users can reach through a Connector.
This is what makes the access model resource-based instead of network-based: adding a user to the "Kubernetes access" group doesn't put them on the VPC, it lets them talk to exactly one endpoint.
Find your cluster's internal API server address. For EKS with a private endpoint enabled:
You'll get something like https://ABC123DEF456.gr7.us-east-1.eks.amazonaws.com. Note the hostname.
In the Twingate admin console:
Go to Network → Resources → Add Resource
Assign it to the
prod-eks-us-east-1Remote NetworkSet the address to the API server hostname
Restrict the port to 443
Save
Then create a Group in Twingate called something like kubernetes-prod-users, assign your engineers (or sync from your IdP), and grant that group access to the Resource. If you're using an IdP-synced group, changes to group membership propagate without touching Twingate directly.
Step 4: Test kubectl from a laptop
Install the Twingate Client if you haven't yet, sign in, and verify the Client shows the Resource as accessible.
Then update your kubeconfig. On EKS:
And try a simple call:
If it works, you're routing through Twingate. If it doesn't, the two things to check are:
The Twingate Client is signed in and the Resource shows as available
Your kubeconfig points at the private endpoint, not a stale public one
The Twingate Client intercepts DNS queries that match a Resource address and routes them through the Connector. You don't need to change kubectl, kubelogin, or any tooling. The endpoint hostname resolves through the Client automatically.
Step 5: Map identities to RBAC
Twingate got you to the API server. Kubernetes RBAC decides what you can do once you're there. These are separate concerns, and mixing them up leads to over-permissive clusters.
If you're using EKS with IAM authentication, you can map an IAM role or user to a Kubernetes group in aws-auth. If you're using OIDC (the cleaner path for tying kubectl identity to your IdP), configure the API server's OIDC flags to trust your IdP's issuer and use kubectl oidc-login or similar for token exchange.
A minimal RBAC setup for a read-only developer group:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: developer-readonly rules: - apiGroups: [""] resources: ["pods", "services", "configmaps"] verbs: ["get", "list", "watch"] - apiGroups: ["apps"] resources: ["deployments", "replicasets"] verbs: ["get", "list", "watch"]
Apply it:
The key idea: Twingate answers "can this identity reach the API server?" RBAC answers "what can this identity do with the API server?" You need both.
Step 6: Lock down pod traffic with NetworkPolicy
Even with tight external access, an app-level compromise inside the cluster can move sideways unless pod traffic is restricted. This is a CNI concern, not a Twingate one, but it belongs in the same threat model.
Assuming you're running Calico, Cilium, or another CNI that supports NetworkPolicy, start with a default-deny policy in each application namespace:
Then explicitly allow the traffic each service actually needs. For a web tier that talks to a database:
If you skip this step, an attacker who lands in one pod can freely reach every other pod in the cluster. External access controls don't help once someone's already inside.
Step 7: Turn off the public API endpoint
This is the moment the setup actually matters. In the AWS console, or with the CLI:
For GKE, the equivalent is enabling a private cluster and disabling the public endpoint. For AKS, use az aks update with --enable-private-cluster.
Give it a few minutes for the API server config to converge, then test kubectl again with the Twingate Client running. If it works, you're done. If it doesn't, re-enable public access, check your Resource and Group configuration in Twingate, and try again.
What you actually get
No public API endpoint. The cluster is invisible from the internet. There's nothing to scan, nothing to brute-force.
No VPN. Engineers don't sit on the VPC. A compromised laptop doesn't have
10.0.0.0/8reachability.Identity-first access. Someone leaves the company, you disable them in your IdP, and their kubectl access is gone.
Audit trail per session. Twingate logs which user connected to which Resource and when. Combined with Kubernetes audit logs, you can reconstruct who did what and from where.
Multiple clusters, same pattern. Add a Resource per cluster, put them in the same or different Groups. No new VPN, no new bastion.
Common mistakes
Forgetting the second Connector. One Connector is a single point of failure. When it restarts for a version bump, kubectl hangs. Always deploy at least two.
Leaving authorized_networks in place. If you were previously using an IP allowlist, remove it after moving to private endpoints. Leaving it in a partial state creates confusing failure modes when someone's IP changes.
Mixing up Twingate Groups and Kubernetes Groups. Twingate Groups control who can reach the endpoint. Kubernetes Groups (via OIDC claims or aws-auth) control what they can do. Use both. Name them clearly so nobody grants cluster-admin by editing the wrong config.
Treating NetworkPolicy as optional. External access controls don't stop lateral movement inside the cluster. If you don't have default-deny NetworkPolicies, one compromised pod is a whole-cluster compromise.
For homelab clusters
The same pattern works on a k3s or microk8s cluster running on a Raspberry Pi or a spare desktop. Deploy a single Connector (or two, if you have the RAM), register the API server IP as a Resource, and you can kubectl into your homelab from anywhere without exposing the endpoint to your ISP's dynamic IP or running Tailscale on every device. The free tier of Twingate covers this comfortably for personal use.
Closing
For deployment specifics, RBAC integration options, and the Twingate Kubernetes Operator (which lets you manage Resources declaratively via CRDs), see the Twingate Kubernetes documentation.
New to Twingate? You can use Twingate for free for up to 5 users, request a personalized demo, or reach out to the team over on the Twingate subreddit.
Solutions
Solutions
The VPN replacement your workforce will love.
Solutions