← Back to DevBytes

GKE Security: IAM Policies and Network Security

Understanding GKE Security: The Shared Responsibility Model

Google Kubernetes Engine (GKE) operates on a shared responsibility model where Google manages the control plane security while you secure worker nodes, workloads, and network access. Two foundational pillars form the bedrock of your cluster security posture: IAM Policies governing who can do what, and Network Security controlling how traffic flows in, out, and within your clusters. Mastering both is essential for running production workloads securely on GKE.

IAM Policies for GKE: The Authentication and Authorization Layer

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

What Are GKE IAM Policies?

IAM policies in GKE operate at two distinct levels: the Cloud IAM layer (Google Cloud's platform-wide identity and access management) and the Kubernetes RBAC layer (cluster-internal role-based access control). Cloud IAM controls what operations principals can perform on your GKE resources via the Google Cloud API—think creating clusters, deleting node pools, or fetching kubeconfig files. Kubernetes RBAC controls what authenticated users and service accounts can do inside the cluster—such as deploying pods, listing secrets, or accessing persistent volumes.

Why IAM Policies Matter

Without properly configured IAM policies, your GKE clusters face critical risks. An overly permissive Cloud IAM role like roles/container.admin granted to a broad group could allow unauthorized cluster deletion. Inside the cluster, a poorly configured RBAC binding might let any developer read production secrets or exec into privileged pods. IAM policies are your first line of defense against privilege escalation, data exfiltration, and accidental infrastructure damage. They enforce the principle of least privilege systematically across your Kubernetes footprint.

Cloud IAM Roles for GKE

Google Cloud provides several predefined roles tailored for GKE operations. Understanding the granularity of these roles is critical:

Service Account vs. User Account Authentication

GKE clusters authenticate principals differently depending on how they connect. Human users typically authenticate via gcloud container clusters get-credentials which uses their Google Cloud user identity. Automated systems and CI/CD pipelines authenticate using Google Cloud Service Accounts. Understanding this distinction helps you design IAM policies that are traceable and auditable—each action is tied to a specific identity, never an anonymous shared key.

How to Configure Cloud IAM Policies for GKE

Granting Cloud IAM roles follows standard Google Cloud patterns. Here's how to grant the container.developer role to a service account at the project level, giving it access to all clusters in that project:

# Grant container.developer role to a service account
gcloud projects add-iam-policy-binding my-gke-project \
    --member="serviceAccount:ci-pipeline@my-gke-project.iam.gserviceaccount.com" \
    --role="roles/container.developer" \
    --condition=None

For more granular control, you can scope the binding to a specific cluster using the cluster resource name. This limits the service account's permissions to exactly one cluster:

# Grant clusterAdmin on a specific cluster only
gcloud container clusters describe production-cluster \
    --zone=us-central1-a \
    --format="value(name)"

# Resource-level IAM binding
gcloud container clusters add-iam-policy-binding production-cluster \
    --zone=us-central1-a \
    --member="serviceAccount:platform-team@my-gke-project.iam.gserviceaccount.com" \
    --role="roles/container.clusterAdmin"

Always verify the effective policy after changes. Use the get-iam-policy command and review the output carefully:

# View current IAM policy on a cluster
gcloud container clusters get-iam-policy production-cluster \
    --zone=us-central1-a

Kubernetes RBAC: Inside-Cluster Authorization

Once a principal authenticates to the cluster, Kubernetes RBAC determines what they can do. RBAC uses four object types: Role (namespaced permissions), ClusterRole (cluster-wide permissions), RoleBinding (grants a Role to subjects in a namespace), and ClusterRoleBinding (grants a ClusterRole cluster-wide).

Here's a practical example. Suppose you want application developers to manage deployments and services in the staging namespace but prevent them from accessing secrets or modifying RBAC objects:

# developer-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: staging
  name: developer-role
rules:
- apiGroups: ["apps", ""]
  resources: ["deployments", "services", "configmaps", "pods", "pods/log"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
  resources: ["pods/portforward", "pods/exec"]
  verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: developer-binding
  namespace: staging
subjects:
- kind: User
  name: jane.doe@example.com
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: developer-role
  apiGroup: rbac.authorization.k8s.io

Apply this configuration with kubectl apply -f developer-role.yaml. Now Jane Doe can deploy and manage applications in staging but cannot list secrets or create other RBAC bindings. Notice we explicitly included pods/exec and pods/portforward as separate resources—these are sensitive operations that should be granted intentionally, not inherited from broader pod permissions.

Mapping Cloud IAM Groups to Kubernetes RBAC

For teams using Google Workspace, you can map Cloud IAM groups directly to Kubernetes RBAC subjects. First, ensure the group has the appropriate Cloud IAM role to access the cluster, then create RBAC bindings referencing the group's email address:

# ClusterRoleBinding for a Google Workspace group
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: readonly-group-binding
subjects:
- kind: Group
  name: dev-team@example.com
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: view
  apiGroup: rbac.authorization.k8s.io

This approach lets you manage team membership in Google Workspace while automatically propagating changes to cluster access—no manual kubectl configuration needed when people join or leave the team.

Workload Identity: Bridging Cloud IAM and Kubernetes Service Accounts

Workload Identity is a critical feature that links a Kubernetes service account to a Google Cloud service account. This allows pods to authenticate to Google Cloud APIs using their Kubernetes identity, eliminating the need to store long-lived service account keys in secrets.

Enable Workload Identity on your cluster (it's enabled by default for new GKE clusters). Then annotate a Kubernetes service account with the corresponding Google Cloud service account:

# Create a Kubernetes service account
kubectl create serviceaccount my-app-sa --namespace=production

# Annotate it for Workload Identity
kubectl annotate serviceaccount my-app-sa \
    --namespace=production \
    iam.gke.io/gcp-service-account=my-app-sa@my-project.iam.gserviceaccount.com

# Grant the Cloud IAM role to the Google Cloud service account
gcloud projects add-iam-policy-binding my-project \
    --member="serviceAccount:my-app-sa@my-project.iam.gserviceaccount.com" \
    --role="roles/storage.objectViewer"

Now any pod running with serviceAccountName: my-app-sa in the production namespace will automatically obtain credentials scoped to the Cloud Storage Object Viewer role. No key files, no secrets—just cryptographically bound identity.

Network Security for GKE: Controlling Traffic Flow

What Is GKE Network Security?

Network security in GKE encompasses three layers: cluster-level network configuration (VPC-native clusters, private clusters, authorized networks), network policy enforcement (Kubernetes NetworkPolicies controlling pod-to-pod traffic), and external access controls (load balancer configurations, Cloud Armor policies, firewall rules). Together these form a defense-in-depth strategy that isolates workloads, restricts lateral movement, and protects against external threats.

Why Network Security Matters

A cluster with unrestricted network access is vulnerable on multiple fronts. Without NetworkPolicies, a compromised pod in one namespace can freely scan and attack pods in all other namespaces. A publicly accessible API server without authorized networks enabled exposes the control plane to credential brute-forcing from any IP on the internet. Externally facing services without proper firewall rules or Cloud Armor policies become targets for DDoS attacks and application-layer exploits. Network security transforms your cluster from an open landscape into a segmented, hardened environment.

VPC-Native Clusters and Private Networking

GKE supports two fundamental networking modes: VPC-native (also called alias IP routing) and legacy routing. Always use VPC-native clusters. In VPC-native mode, pods get IP addresses directly from the VPC subnet, allowing native firewall rules and network policies to apply seamlessly. Private clusters restrict the control plane endpoint to private IP addresses only, preventing direct internet access to the API server.

Creating a private, VPC-native cluster with explicit subnet configuration:

gcloud container clusters create private-prod-cluster \
    --zone=us-central1-a \
    --enable-ip-alias \
    --create-subnetwork="" \
    --cluster-secondary-range-name="pod-range" \
    --services-secondary-range-name="service-range" \
    --enable-private-nodes \
    --enable-private-endpoint \
    --master-ipv4-cidr="172.16.0.0/28" \
    --network="prod-vpc" \
    --subnetwork="prod-subnet" \
    --no-enable-master-authorized-networks

With --enable-private-endpoint, the API server gets only a private IP address. You must then use a jump host, VPN, or Cloud Interconnect to reach it. This is the gold standard for production clusters handling sensitive data.

Authorized Networks: IP Allowlisting for the Control Plane

For clusters where a fully private endpoint isn't feasible, authorized networks let you restrict which IP ranges can reach the API server. This is an essential control even for non-private clusters:

gcloud container clusters create secured-cluster \
    --zone=us-central1-a \
    --enable-master-authorized-networks \
    --master-authorized-networks="203.0.113.0/24,198.51.100.5/32"

You can update authorized networks on existing clusters. Always include your CI/CD pipeline IPs and a dedicated operations network, and never use 0.0.0.0/0 unless you fully understand the implications:

gcloud container clusters update secured-cluster \
    --zone=us-central1-a \
    --enable-master-authorized-networks \
    --master-authorized-networks="203.0.113.0/24,198.51.100.5/32,10.0.0.0/8"

Kubernetes NetworkPolicies: Micro-Segmentation Inside the Cluster

NetworkPolicies are the Kubernetes-native firewall rules that control pod-to-pod communication. They are enforced by the cluster's CNI plugin—GKE supports both Calico and the built-in network policy enforcement when using VPC-native clusters with specific dataplane features enabled.

By default, all pods can communicate freely with all other pods. Your first NetworkPolicy should establish a default deny-all posture in critical namespaces, then explicitly allow only required traffic:

# Default deny-all ingress for a namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}  # Applies to all pods in the namespace
  policyTypes:
  - Ingress
---
# Allow incoming traffic only from the API gateway pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-gateway
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: payment-service
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: api-gateway
      namespaceSelector:
        matchLabels:
          environment: production
    ports:
    - protocol: TCP
      port: 8443

This pair of policies ensures that only pods labeled app: api-gateway in production-namespaced environments can reach the payment service on port 8443. All other traffic, even from within the same namespace, is denied. Apply these with kubectl apply -f network-policies.yaml and verify enforcement by testing connectivity from disallowed pods.

Egress Controls and DNS Policies

NetworkPolicies also control egress traffic—what destinations pods can reach outside the cluster. This is vital for preventing data exfiltration and restricting compromised workloads from calling out to command-and-control servers:

# Restrict egress to only approved external endpoints
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: restrict-egress
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend-worker
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/8
    - ipBlock:
        cidr: 172.16.0.0/12
  - to:
    - namespaceSelector:
        matchLabels:
          environment: production
    ports:
    - protocol: TCP
      port: 5432
  - to:
    - podSelector:
        matchLabels:
          app: redis
      namespaceSelector: {}
    ports:
    - protocol: TCP
      port: 6379

For DNS resolution control, GKE supports egress rules based on DNS names when using the GKE Dataplane V2 with FQDN policy support. This enables precise rules like "allow egress only to *.mycompany.com and the Google Cloud Storage API":

# FQDN-based egress policy (requires GKE Dataplane V2)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-specific-domains
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s.io/network: internal
  - to:
    - fqdn: "*.mycompany.com"
    - fqdn: "storage.googleapis.com"
    ports:
    - protocol: TCP
      port: 443

External Load Balancer Security

Services of type LoadBalancer in GKE create Google Cloud external load balancers. By default, these are exposed to the internet. Use the loadBalancerSourceRanges annotation to restrict source IPs directly on the Service object:

# service-with-source-ranges.yaml
apiVersion: v1
kind: Service
metadata:
  name: restricted-api
  namespace: production
  annotations:
    cloud.google.com/load-balancer-type: "External"
spec:
  type: LoadBalancer
  loadBalancerSourceRanges:
  - "203.0.113.0/24"
  - "198.51.100.5/32"
  selector:
    app: restricted-api
  ports:
  - port: 443
    targetPort: 8443
    protocol: TCP

For internet-facing services, combine this with Google Cloud Armor security policies that provide layer 7 protection including WAF capabilities, rate limiting, and IP-based access control at the load balancer level:

# Create a Cloud Armor security policy
gcloud compute security-policies create production-waf-policy \
    --description="WAF and rate limiting for production services"

# Add a rule allowing only specific IPs
gcloud compute security-policies rules create 1000 \
    --security-policy=production-waf-policy \
    --action=allow \
    --src-ip-ranges="203.0.113.0/24" \
    --description="Allow corporate VPN"

# Add a rate-limiting rule for API endpoints
gcloud compute security-policies rules create 2000 \
    --security-policy=production-waf-policy \
    --action=throttle \
    --rate-limit-threshold-count=100 \
    --rate-limit-threshold-interval-sec=60 \
    --src-ip-ranges="*" \
    --description="Rate limit API calls"

# Add a default deny rule
gcloud compute security-policies rules create 9999 \
    --security-policy=production-waf-policy \
    --action=deny \
    --src-ip-ranges="*" \
    --description="Default deny"

# Attach the policy to the load balancer backend service
gcloud compute backend-services update my-backend-service \
    --security-policy=production-waf-policy \
    --global

Internal Load Balancing and GCLB with Private Service Connect

For services that should never touch the public internet, use internal load balancers. These are created by annotating the Service with the internal load balancer type:

apiVersion: v1
kind: Service
metadata:
  name: internal-payment-service
  namespace: production
  annotations:
    cloud.google.com/load-balancer-type: "Internal"
    networking.gke.io/internal-load-balancer-allow-global-access: "false"
spec:
  type: LoadBalancer
  selector:
    app: payment-processor
  ports:
  - port: 443
    targetPort: 8443
    protocol: TCP

Setting internal-load-balancer-allow-global-access: false restricts the internal load balancer to the same region as the cluster, preventing cross-regional traffic patterns that could increase attack surface.

Best Practices for GKE IAM and Network Security

IAM Best Practices

Network Security Best Practices

Putting It All Together: A Production-Grade Security Configuration

The following example demonstrates a comprehensive setup combining IAM and network security controls for a production cluster running a multi-tier application:

# 1. Create a private VPC-native cluster with Workload Identity enabled
gcloud container clusters create secure-production \
    --zone=us-central1-a \
    --enable-ip-alias \
    --enable-private-nodes \
    --enable-private-endpoint \
    --master-ipv4-cidr="172.16.0.32/28" \
    --enable-master-authorized-networks \
    --master-authorized-networks="10.0.0.0/8,203.0.113.0/24" \
    --workload-pool="my-project.svc.id.goog" \
    --network="production-vpc" \
    --subnetwork="production-subnet" \
    --cluster-secondary-range-name="pod-range" \
    --services-secondary-range-name="service-range"

# 2. Apply default-deny NetworkPolicies in all critical namespaces
cat <

This configuration creates a hardened environment where the API server is completely isolated from the public internet, workloads run with non-root privileges, the payment processor service account has precisely scoped access to a single Pub/Sub topic, and default-deny network policies prevent any unsanctioned lateral communication.

Conclusion

GKE security is not a single configuration checkbox—it is a layered, continuous practice built on the interplay between Cloud IAM policies and network security controls. IAM ensures that identities, whether human operators or automated workloads, operate with the minimum permissions necessary. Network security ensures that even if a workload is compromised, the blast radius remains tightly contained. By combining private clusters, Workload Identity, granular RBAC roles, default-deny NetworkPolicies, and Cloud Armor-protected load balancers, you build a defense-in-depth architecture that protects your Kubernetes infrastructure from both external threats and internal misconfigurations. Regularly audit your IAM bindings, test your NetworkPolicies empirically, and treat security configuration as living code—version controlled, reviewed, and continuously improved alongside your application deployments.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles