Advanced Cloud
Serverless and container security in the cloud — Lambda, ECR, EKS best practices
Serverless and containers changed the operations model, but they did not eliminate the attack surface — they redistributed it. In Lambda, code can still leak secrets or be injected via payload. In containers, an image with critical vulnerabilities reaches production directly if there is no scanning in the pipeline.
AWS Lambda — attack surface and controls
Execution role with least privilege
// Bad — role with excessive permissions
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
// Good — only what the specific function needs
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::reports-prod/input/*"
}
Environment variables encrypted with KMS
# Create function with encrypted variables
aws lambda create-function \
--function-name payment-processor \
--runtime python3.12 \
--role arn:aws:iam::123456789:role/lambda-payment-role \
--kms-key-arn arn:aws:kms:us-east-1:123456789:key/abc-123 \
--environment Variables='{API_KEY_SECRET_ARN=arn:aws:secretsmanager:...}' \
--zip-file fileb://function.zip \
--handler handler.main
Prefer Secrets Manager references over injecting the value directly into the environment variable.
Resource policy — who can invoke
# Allow invocation only from this account's API Gateway
aws lambda add-permission \
--function-name payment-processor \
--statement-id allow-apigw \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:123456789:abc123/prod/POST/payment" \
--source-account 123456789
Input validation
import json, re
def handler(event, context):
# Never trust the payload — always validate
body = json.loads(event.get("body", "{}"))
amount = body.get("amount")
if not isinstance(amount, (int, float)) or amount <= 0 or amount > 1_000_000:
return {"statusCode": 400, "body": "Invalid amount"}
order_id = body.get("order_id", "")
if not re.fullmatch(r"[A-Z0-9\-]{8,32}", order_id):
return {"statusCode": 400, "body": "Invalid order_id"}
# Safe processing...
Amazon ECR — image security
# Enable automatic scan on push
aws ecr put-image-scanning-configuration \
--repository-name my-app \
--image-scanning-configuration scanOnPush=true
# View vulnerability scan results
aws ecr describe-image-scan-findings \
--repository-name my-app \
--image-id imageTag=v1.2.3 \
--query 'imageScanFindings.findingSeverityCounts'
# Lifecycle policy — remove old unscanned images
aws ecr put-lifecycle-policy \
--repository-name my-app \
--lifecycle-policy-text '{
"rules": [{
"rulePriority": 1,
"description": "Expire untagged images older than 7 days",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 7
},
"action": { "type": "expire" }
}]
}'
Amazon EKS — cluster security
Pod Security Admission
# Namespace with restrictive enforcement
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted
IRSA — IAM Roles for Service Accounts
# Associate an IAM role with a Kubernetes ServiceAccount
eksctl create iamserviceaccount \
--cluster my-cluster \
--namespace production \
--name sa-processor \
--attach-policy-arn arn:aws:iam::123456789:policy/processor-policy \
--approve
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-processor
namespace: production
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789:role/processor-role
Network Policy for pod isolation
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-isolation
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes: [Ingress, Egress]
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- port: 5432
Unified checklist
Lambda:
- Execution role with least privilege (no Action: *)
- Sensitive values via Secrets Manager, not env vars
- Resource policy restricted to the legitimate invoker
- Input validation in the handler
- Timeout and concurrency limit configured
- VPC only when necessary (increases cold start)
ECR / Images:
- Scan on push enabled
- Minimal base images (distroless, alpine)
- Non-root user in Dockerfile
- Fixed tag — never :latest in production
- Pipeline blocks images with CRITICAL/HIGH findings
EKS:
- Pod Security Admission enforce: restricted
- IRSA for AWS access (no access keys in pods)
- Network Policies in all namespaces
- RBAC with dedicated ServiceAccounts
- Admission controller (Kyverno, OPA Gatekeeper)
- Runtime security: Falco for anomalous behavior detection