Amazon EKS — Security Hands-On Lab
In this lab, you’ll configure IRSA, create RBAC roles, enable pod security, and mount secrets from AWS Secrets Manager.
Prerequisites
- Running EKS cluster
-
kubectl,awsCLI,eksctl,helmconfigured - AWS Secrets Manager access
Step 1 — Enable OIDC Provider
eksctl utils associate-iam-oidc-provider \
--cluster my-cluster \
--approve
Step 2 — Configure IRSA for S3 Access
Create a policy:
cat > s3-read-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::my-lab-bucket", "arn:aws:s3:::my-lab-bucket/*"]
}
]
}
EOF
aws iam create-policy \
--policy-name EKSLabS3ReadPolicy \
--policy-document file://s3-read-policy.json
Create the IRSA ServiceAccount:
eksctl create iamserviceaccount \
--cluster my-cluster \
--namespace default \
--name s3-reader-sa \
--attach-policy-arn arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):policy/EKSLabS3ReadPolicy \
--approve
Test:
kubectl run s3-test --image=amazon/aws-cli:latest \
--serviceaccount=s3-reader-sa \
--restart=Never \
--command -- aws s3 ls s3://my-lab-bucket/
kubectl logs s3-test
Step 3 — Create RBAC for a Dev Team
# dev-rbac.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev-team
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dev-deployer
namespace: dev-team
rules:
- apiGroups: ["", "apps"]
resources: ["pods", "deployments", "services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-deployer-binding
namespace: dev-team
subjects:
- kind: Group
name: dev-group
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-deployer
apiGroup: rbac.authorization.k8s.io
kubectl apply -f dev-rbac.yaml
# Test with impersonation
kubectl auth can-i create deployments -n dev-team --as-group=dev-group --as=dev-user
# yes
kubectl auth can-i delete namespaces --as-group=dev-group --as=dev-user
# no
Step 4 — Enable Pod Security Standards
kubectl label namespace dev-team \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted
Test — this pod should be rejected:
kubectl run privileged-test --image=nginx:latest \
-n dev-team \
--overrides='{
"spec": {
"containers": [{
"name": "test",
"image": "nginx:latest",
"securityContext": {"privileged": true}
}]
}
}'
# Error: violates PodSecurity "restricted"
This compliant pod should work:
# secure-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: secure-app
namespace: dev-team
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: run
emptyDir: {}
kubectl apply -f secure-pod.yaml
Step 5 — Mount Secrets from Secrets Manager
Create a secret in AWS:
aws secretsmanager create-secret \
--name lab/db-credentials \
--secret-string '{"username":"admin","password":"SuperSecret123"}'
Install ASCP:
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver \
--namespace kube-system --set syncSecret.enabled=true
kubectl apply -f https://raw.githubusercontent.com/aws/secrets-store-csi-driver-provider-aws/main/deployment/aws-provider-installer.yaml
Create IRSA for Secrets Manager access:
cat > sm-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
"Resource": ["arn:aws:secretsmanager:us-east-1:*:secret:lab/*"]
}
]
}
EOF
aws iam create-policy --policy-name EKSLabSMPolicy --policy-document file://sm-policy.json
eksctl create iamserviceaccount \
--cluster my-cluster \
--namespace dev-team \
--name sm-reader-sa \
--attach-policy-arn arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):policy/EKSLabSMPolicy \
--approve
Create SecretProviderClass + Pod:
# secret-mount.yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: lab-db-creds
namespace: dev-team
spec:
provider: aws
parameters:
objects: |
- objectName: "lab/db-credentials"
objectType: "secretsmanager"
---
apiVersion: v1
kind: Pod
metadata:
name: secret-consumer
namespace: dev-team
spec:
serviceAccountName: sm-reader-sa
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: busybox:latest
command: ["sh", "-c", "cat /mnt/secrets/lab/db-credentials && sleep 3600"]
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: secrets
mountPath: /mnt/secrets
readOnly: true
volumes:
- name: secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: lab-db-creds
kubectl apply -f secret-mount.yaml
kubectl logs secret-consumer -n dev-team
# {"username":"admin","password":"SuperSecret123"}
Cleanup
kubectl delete namespace dev-team
kubectl delete pod s3-test
eksctl delete iamserviceaccount --cluster my-cluster --namespace default --name s3-reader-sa
aws secretsmanager delete-secret --secret-id lab/db-credentials --force-delete-without-recovery
helm uninstall csi-secrets-store -n kube-system