Skip to main content

Security

This document covers security considerations and configurations for the cluster.

Security Overview

Network Security

Calico Network Policies

Calico enforces network policies to control pod-to-pod communication.

Default Deny Policy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress

Allow Specific Traffic

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080

Namespace Isolation

Each environment uses separate namespaces:

NamespacePurpose
argocdArgoCD CD
cert-managerCertificate management
metallb-systemLoad balancer
ingress-nginxIngress controller
external-dnsDNS management
calico-systemNetwork policies
monitoringPrometheus, Loki, Grafana
local-path-storageStorage provisioner
defaultUser applications

TLS Security

Certificate Configuration

All ingresses use cert-manager with Let's Encrypt:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- myapp.ssdk8s.xyz
secretName: myapp-tls

Certificate Details

PropertyValue
IssuerLet's Encrypt Production
Validity90 days
RenewalAutomatic at 30 days before expiry
Key Size2048-bit RSA
ChallengeDNS-01 via Cloudflare

Cloudflare API Token

Stored as Kubernetes secret:

kubectl get secret cloudflare-api-token -n cert-manager -o yaml
warning

The Cloudflare API token must have minimal permissions: Zone:DNS:Edit only.

Access Control

OPA Gatekeeper

Gatekeeper enforces cluster-wide policies through admission control:

  • Required Labels - All workloads must have app and team labels
  • Required Resources - All containers must have CPU/memory limits
  • No Privileged Containers - Blocks privileged: true and allowPrivilegeEscalation: true
  • Maximum Resources - Per-namespace resource limits

See OPA Gatekeeper for detailed policy documentation.

Kubernetes RBAC

Service Accounts

Each application should use a dedicated service account:

apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: default

Role-Based Access

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: my-app-role
namespace: default
spec:
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-rolebinding
namespace: default
subjects:
- kind: ServiceAccount
name: my-app
namespace: default
roleRef:
kind: Role
name: my-app-role
apiGroup: rbac.authorization.k8s.io

ArgoCD AppProject

The playground project restricts deployments:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: playground
namespace: argocd
spec:
sourceRepos:
- https://github.com/simran2491/k8s-playground-cluster.git
destinations:
- namespace: '*'
server: https://kubernetes.default.svc
clusterResourceWhitelist:
- group: ''
kind: Namespace
- group: rbac.authorization.k8s.io
kind: ClusterRole
kind: ClusterRoleBinding

Secrets Management

Current Approach

Secrets are stored as Kubernetes Secrets:

kubectl create secret generic my-secret \
--from-literal=username=admin \
--from-literal=password=secret123 \
-n default
warning

Kubernetes Secrets are base64-encoded, not encrypted by default. Enable encryption at rest for production.

Encryption at Rest

To enable encryption at rest, configure the API server:

# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

Future: External Secrets

For production, consider:

  • External Secrets Operator - Sync from AWS Secrets Manager, HashiCorp Vault
  • Sealed Secrets - Encrypt secrets for Git storage
  • SOPS - Encrypt secrets in Git with age/pgp keys

Security Best Practices

Pod Security

apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: app
image: my-app:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL

Image Security

  1. Use specific tags - Avoid latest in production
  2. Scan images - Use Trivy, Clair, or Snyk
  3. Use distroless - Minimal base images

Network Security

  1. Default deny - Start with deny-all policies
  2. Least privilege - Only allow required traffic
  3. Egress filtering - Control outbound traffic

Audit Logging

Enable Kubernetes audit logging:

# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --audit-log-path=/var/log/kubernetes/audit.log
- --audit-policy-file=/etc/kubernetes/audit-policy.yaml

Compliance Considerations

RequirementImplementation
Data EncryptionTLS everywhere, encryption at rest
Access ControlRBAC, AppProject restrictions
Audit TrailKubernetes audit logs, ArgoCD history
Network SegmentationCalico network policies
Secret ManagementKubernetes secrets (consider external)