Security: Network Policies, Access & Secrets
Default-deny pod traffic, manage cluster access with access entries, and keep real secrets out of etcd and Git.
A hardened EKS cluster locks down three planes: pod-to-pod traffic, who can reach the API, and how secrets are stored.
Network policies (default deny)
By default every pod can talk to every other pod. The VPC CNI now enforces Kubernetes NetworkPolicy natively (enable network policy on the add-on), so you can adopt a default-deny posture and then explicitly allow the flows you need — the same model you'd use with Calico or Cilium.
Cluster access — prefer access entries
- The classic
aws-authConfigMap is powerful and dangerous: one bad edit can lock everyone out. Prefer the newer access entries API (authenticationMode: API), which manages IAM-to-cluster mapping through AWS APIs with no in-cluster ConfigMap to corrupt. - Grant coarse access with AWS-managed access policies, and fine-grained access with normal Kubernetes RBAC bound to the mapped groups.
Secrets that are actually secret
- Turn on envelope encryption of secrets with KMS so etcd never holds plaintext.
- Better still, don't put secrets in etcd at all: mount them from AWS Secrets Manager / Parameter Store via the Secrets Store CSI driver, so rotation happens outside the cluster and nothing lands in Git.
Example
# Default-deny all ingress in a namespace, then allow only what you need
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: prod
spec:
podSelector: {}
policyTypes: ["Ingress"]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-to-api
namespace: prod
spec:
podSelector:
matchLabels: { app: api }
policyTypes: ["Ingress"]
ingress:
- from:
- podSelector:
matchLabels: { app: web }
ports:
- protocol: TCP
port: 8080When to use it
- A security team installs a network policy CNI plugin and applies default-deny policies so no two namespaces can communicate unless explicitly allowed.
- A platform team migrates from aws-auth ConfigMap to EKS Access Entries to manage cluster access via IAM without editing a shared ConfigMap.
- A secrets management team uses AWS Secrets Manager with the External Secrets Operator to sync secrets to Kubernetes, eliminating plaintext Secret YAML in Git.
More examples
Default-deny with namespace allow
Combines a default-deny policy with an explicit allow rule so only the payment-api pod can reach port 5432 in the database namespace.
# Default deny all in namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: payments
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
---
# Allow payments to reach database namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-db
namespace: payments
spec:
podSelector:
matchLabels:
app: payment-api
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: database
ports:
- port: 5432Create EKS Access Entry
Creates an access entry that grants the DeveloperRole read-only view access scoped to the team-a namespace, replacing the aws-auth ConfigMap approach.
aws eks create-access-entry \
--cluster-name prod \
--principal-arn arn:aws:iam::123456789012:role/DeveloperRole \
--type STANDARD
aws eks associate-access-policy \
--cluster-name prod \
--principal-arn arn:aws:iam::123456789012:role/DeveloperRole \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy \
--access-scope type=namespace,namespaces=team-aSync secret from Secrets Manager
An ExternalSecret that pulls a value from AWS Secrets Manager every hour and creates a native Kubernetes Secret, keeping sensitive data out of Git.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: db-credentials
data:
- secretKey: password
remoteRef:
key: prod/db/password
Discussion