Set up comprehensive Kubernetes security with Falco for runtime threat detection and OPA Gatekeeper for admission control policy enforcement. This tutorial covers installation, configuration, and custom security policies.
Prerequisites
- Kubernetes cluster with admin access
- Helm 3.x installed
- kubectl configured
- Basic understanding of Kubernetes security concepts
What this solves
Kubernetes clusters need layered security to detect runtime threats and enforce admission policies. Falco monitors system calls and container behavior for suspicious activity, while OPA Gatekeeper validates resources against security policies before deployment. Together they provide comprehensive protection against malicious workloads, privilege escalation, and policy violations.
Step-by-step installation
Install Helm for package management
Both Falco and Gatekeeper use Helm charts for installation. Install Helm first to manage the deployments.
curl https://get.helm.sh/helm-v3.12.0-linux-amd64.tar.gz -o helm.tar.gz
tar -zxvf helm.tar.gz
sudo mv linux-amd64/helm /usr/local/bin/helm
helm version
Add Helm repositories
Add the official repositories for Falco and Gatekeeper charts.
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
Install Falco for runtime security monitoring
Deploy Falco as a DaemonSet to monitor all nodes for suspicious activity. The default configuration includes rules for common threats.
kubectl create namespace falco-system
helm install falco falcosecurity/falco \
--namespace falco-system \
--set driver.kind=ebpf \
--set falco.grpc.enabled=true \
--set falco.grpcOutput.enabled=true
Install OPA Gatekeeper for admission control
Deploy Gatekeeper to validate all resource requests against defined policies before they reach the API server.
kubectl create namespace gatekeeper-system
helm install gatekeeper gatekeeper/gatekeeper \
--namespace gatekeeper-system \
--set replicas=3 \
--set auditInterval=60
Verify installations
Check that both systems are running correctly across all nodes.
kubectl get pods -n falco-system
kubectl get pods -n gatekeeper-system
kubectl get validatingadmissionwebhooks
Configure security policies and rules
Create custom Falco rules
Add detection rules for your specific environment. This example detects unauthorized network connections.
apiVersion: v1
kind: ConfigMap
metadata:
name: falco-custom-rules
namespace: falco-system
data:
custom_rules.yaml: |
- rule: Unexpected outbound connection
desc: Detect unexpected outbound network connections
condition: >
outbound and not fd.typechar=4 and not fd.typechar=6 and
not proc.name in (curl, wget, apt, yum, dnf) and
not container.image.repository in (docker.io/library/alpine, gcr.io/distroless)
output: >
Unexpected outbound connection (command=%proc.cmdline connection=%fd.name
user=%user.name container=%container.name image=%container.image.repository)
priority: WARNING
tags: [network, outbound]
- rule: Privileged container spawned
desc: Detect containers running with privileged access
condition: >
spawned_process and container and proc.vpid=1 and
container.privileged=true
output: >
Privileged container spawned (command=%proc.cmdline user=%user.name
container=%container.name image=%container.image.repository)
priority: CRITICAL
tags: [container, privilege]
kubectl apply -f falco-custom-rules.yaml
Create Gatekeeper constraint templates
Define reusable policy templates that can be applied to different resource types.
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: requiresecuritycontext
spec:
crd:
spec:
names:
kind: RequireSecurityContext
validation:
properties:
runAsNonRoot:
type: boolean
readOnlyRootFilesystem:
type: boolean
allowPrivilegeEscalation:
type: boolean
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package requiresecuritycontext
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.runAsNonRoot
msg := "Container must run as non-root user"
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.readOnlyRootFilesystem
msg := "Container must use read-only root filesystem"
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
container.securityContext.allowPrivilegeEscalation != false
msg := "Container must not allow privilege escalation"
}
kubectl apply -f require-security-context-template.yaml
Apply security context constraints
Use the template to enforce security contexts on all pods in production namespaces.
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: RequireSecurityContext
metadata:
name: must-have-security-context
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces: ["production", "staging"]
parameters:
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
kubectl apply -f security-context-constraint.yaml
Create resource limit template
Prevent resource exhaustion attacks by requiring CPU and memory limits on all containers.
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: requireresources
spec:
crd:
spec:
names:
kind: RequireResources
validation:
properties:
limits:
type: array
items:
type: string
requests:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package requireresources
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
required := input.parameters.limits
provided := container.resources.limits
missing := required[_]
not provided[missing]
msg := sprintf("Container missing required resource limit: %v", [missing])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
required := input.parameters.requests
provided := container.resources.requests
missing := required[_]
not provided[missing]
msg := sprintf("Container missing required resource request: %v", [missing])
}
kubectl apply -f require-resources-template.yaml
Apply resource limit constraints
Enforce CPU and memory limits on all production workloads.
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: RequireResources
metadata:
name: must-have-resource-limits
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces: ["production", "staging"]
parameters:
limits: ["cpu", "memory"]
requests: ["cpu", "memory"]
kubectl apply -f resource-limits-constraint.yaml
Set up monitoring and alerting
Configure Falco alerts
Set up webhook notifications to send security alerts to your monitoring system.
apiVersion: v1
kind: ConfigMap
metadata:
name: falco-config-override
namespace: falco-system
data:
falco.yaml: |
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml
- /etc/falco/k8s_audit_rules.yaml
- /etc/falco/rules.d
json_output: true
json_include_output_property: true
http_output:
enabled: true
url: "http://webhook-service.monitoring.svc.cluster.local:8080/falco"
user_agent: "falco/0.35.0"
priority: WARNING
syscall_event_drops:
actions:
- log
- alert
rate: 0.1
max_burst: 1000
kubectl apply -f falco-config-override.yaml
kubectl rollout restart daemonset/falco -n falco-system
Create alerting webhook service
Deploy a simple webhook receiver to process Falco alerts and forward them to your notification system.
apiVersion: apps/v1
kind: Deployment
metadata:
name: security-webhook
namespace: monitoring
spec:
replicas: 2
selector:
matchLabels:
app: security-webhook
template:
metadata:
labels:
app: security-webhook
spec:
containers:
- name: webhook
image: falcosecurity/falcosidekick:2.25.0
ports:
- containerPort: 2801
env:
- name: WEBHOOK_URL
value: "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
- name: DEBUG
value: "true"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: security-webhook
namespace: monitoring
spec:
selector:
app: security-webhook
ports:
- port: 8080
targetPort: 2801
type: ClusterIP
kubectl create namespace monitoring
kubectl apply -f webhook-deployment.yaml
Monitor Gatekeeper violations
Create a monitoring dashboard to track policy violations and system health.
apiVersion: v1
kind: ServiceMonitor
metadata:
name: gatekeeper-metrics
namespace: gatekeeper-system
spec:
selector:
matchLabels:
app: gatekeeper
endpoints:
- port: metrics
interval: 30s
path: /metrics
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: gatekeeper-alerts
namespace: gatekeeper-system
spec:
groups:
- name: gatekeeper.rules
rules:
- alert: GatekeeperViolations
expr: increase(gatekeeper_violations_total[5m]) > 10
for: 2m
labels:
severity: warning
annotations:
summary: "High number of Gatekeeper policy violations"
description: "{{ $value }} policy violations detected in the last 5 minutes"
- alert: GatekeeperDown
expr: up{job="gatekeeper"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Gatekeeper is down"
description: "Gatekeeper admission controller is not responding"
kubectl apply -f gatekeeper-monitoring.yaml
Test security policies
Test Gatekeeper admission control
Verify that policies block non-compliant workloads by trying to deploy a pod without required security context.
apiVersion: v1
kind: Pod
metadata:
name: insecure-test-pod
namespace: production
spec:
containers:
- name: test
image: nginx:latest
ports:
- containerPort: 80
kubectl apply -f test-insecure-pod.yaml
This should fail with a message about missing security context and resource limits.
Test compliant pod deployment
Deploy a pod that meets all security requirements.
apiVersion: v1
kind: Pod
metadata:
name: secure-test-pod
namespace: production
spec:
containers:
- name: test
image: nginx:latest
ports:
- containerPort: 80
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
volumeMounts:
- name: tmp
mountPath: /tmp
- name: var-cache
mountPath: /var/cache/nginx
volumes:
- name: tmp
emptyDir: {}
- name: var-cache
emptyDir: {}
kubectl apply -f test-secure-pod.yaml
Generate test security events
Trigger Falco rules to verify runtime monitoring works correctly.
# Create a test pod that will trigger Falco rules
kubectl run falco-test --image=alpine --rm -it --restart=Never -- sh
Inside the container, run commands that should trigger alerts:
ps aux
netstat -an
find /etc -name "passwd"
wget google.com
exit
Verify your setup
# Check Falco is detecting events
kubectl logs -n falco-system -l app.kubernetes.io/name=falco --tail=50
Verify Gatekeeper policies are active
kubectl get constraints
kubectl get constrainttemplates
Check for policy violations
kubectl describe RequireSecurityContext must-have-security-context
kubectl describe RequireResources must-have-resource-limits
Test webhook connectivity
kubectl logs -n monitoring -l app=security-webhook
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Falco pods failing to start | eBPF driver not supported | Use --set driver.kind=module instead of ebpf |
| Gatekeeper blocking all pods | Constraint too restrictive | Add namespace exclusions or adjust match criteria |
| High CPU usage from Falco | Too many syscall events | Tune rules and add filters for noisy processes |
| Webhook not receiving alerts | Network policy blocking traffic | Allow egress from Falco namespace to webhook service |
| Policies not applying | Gatekeeper not ready | Wait for all admission webhooks to be ready |
Advanced configuration
For production deployments, also consider integrating with Kubernetes RBAC for comprehensive access control and OpenTelemetry monitoring for complete observability across your security stack.
Next steps
- Implement Kubernetes RBAC with service accounts for access control
- Set up Kubernetes container image security scanning with Trivy
- Implement Kubernetes network policies for pod-to-pod security
- Configure Kubernetes secrets management with Vault integration
- Set up Kubernetes monitoring with Prometheus Operator
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Configuration
FALCO_NAMESPACE="${FALCO_NAMESPACE:-falco-system}"
GATEKEEPER_NAMESPACE="${GATEKEEPER_NAMESPACE:-gatekeeper-system}"
HELM_VERSION="${HELM_VERSION:-v3.12.0}"
# Usage
usage() {
echo "Usage: $0 [options]"
echo "Options:"
echo " --falco-namespace NAME Falco namespace (default: falco-system)"
echo " --gatekeeper-namespace NAME Gatekeeper namespace (default: gatekeeper-system)"
echo " --help Show this help"
exit 1
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--falco-namespace) FALCO_NAMESPACE="$2"; shift 2 ;;
--gatekeeper-namespace) GATEKEEPER_NAMESPACE="$2"; shift 2 ;;
--help) usage ;;
*) echo "Unknown option: $1"; usage ;;
esac
done
# Cleanup function
cleanup() {
echo -e "${RED}Error occurred. Cleaning up...${NC}"
helm delete falco -n "$FALCO_NAMESPACE" 2>/dev/null || true
helm delete gatekeeper -n "$GATEKEEPER_NAMESPACE" 2>/dev/null || true
kubectl delete namespace "$FALCO_NAMESPACE" 2>/dev/null || true
kubectl delete namespace "$GATEKEEPER_NAMESPACE" 2>/dev/null || true
}
trap cleanup ERR
# Detect OS and package manager
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian) PKG_MGR="apt"; PKG_INSTALL="apt install -y"; PKG_UPDATE="apt update" ;;
almalinux|rocky|centos|rhel|ol|fedora) PKG_MGR="dnf"; PKG_INSTALL="dnf install -y"; PKG_UPDATE="dnf check-update || true" ;;
amzn) PKG_MGR="yum"; PKG_INSTALL="yum install -y"; PKG_UPDATE="yum check-update || true" ;;
*) echo -e "${RED}Unsupported distro: $ID${NC}"; exit 1 ;;
esac
else
echo -e "${RED}/etc/os-release not found. Cannot detect distribution.${NC}"
exit 1
fi
echo -e "${GREEN}[1/8] Checking prerequisites...${NC}"
# Check if running as root or with sudo
if [ "$EUID" -ne 0 ] && ! sudo -n true 2>/dev/null; then
echo -e "${RED}This script requires root privileges or sudo access${NC}"
exit 1
fi
# Check if kubectl is available
if ! command -v kubectl &> /dev/null; then
echo -e "${RED}kubectl is required but not installed${NC}"
exit 1
fi
# Verify kubectl can connect to cluster
if ! kubectl cluster-info &> /dev/null; then
echo -e "${RED}Cannot connect to Kubernetes cluster. Verify kubectl configuration.${NC}"
exit 1
fi
echo -e "${GREEN}[2/8] Updating package manager...${NC}"
if [ "$EUID" -eq 0 ]; then
$PKG_UPDATE
else
sudo $PKG_UPDATE
fi
echo -e "${GREEN}[3/8] Installing required packages...${NC}"
REQUIRED_PACKAGES=""
case "$PKG_MGR" in
apt) REQUIRED_PACKAGES="curl tar" ;;
dnf|yum) REQUIRED_PACKAGES="curl tar" ;;
esac
if [ "$EUID" -eq 0 ]; then
$PKG_INSTALL $REQUIRED_PACKAGES
else
sudo $PKG_INSTALL $REQUIRED_PACKAGES
fi
echo -e "${GREEN}[4/8] Installing Helm...${NC}"
if ! command -v helm &> /dev/null; then
TEMP_DIR=$(mktemp -d)
cd "$TEMP_DIR"
curl -L "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz" -o helm.tar.gz
tar -zxf helm.tar.gz
if [ "$EUID" -eq 0 ]; then
mv linux-amd64/helm /usr/local/bin/helm
chmod 755 /usr/local/bin/helm
else
sudo mv linux-amd64/helm /usr/local/bin/helm
sudo chmod 755 /usr/local/bin/helm
fi
cd - > /dev/null
rm -rf "$TEMP_DIR"
echo -e "${YELLOW}Helm installed successfully${NC}"
else
echo -e "${YELLOW}Helm already installed${NC}"
fi
echo -e "${GREEN}[5/8] Adding Helm repositories...${NC}"
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
echo -e "${GREEN}[6/8] Installing Falco for runtime security monitoring...${NC}"
kubectl create namespace "$FALCO_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
helm upgrade --install falco falcosecurity/falco \
--namespace "$FALCO_NAMESPACE" \
--set driver.kind=ebpf \
--set falco.grpc.enabled=true \
--set falco.grpcOutput.enabled=true \
--wait --timeout=300s
echo -e "${GREEN}[7/8] Installing OPA Gatekeeper for admission control...${NC}"
kubectl create namespace "$GATEKEEPER_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
helm upgrade --install gatekeeper gatekeeper/gatekeeper \
--namespace "$GATEKEEPER_NAMESPACE" \
--set replicas=3 \
--set auditInterval=60 \
--wait --timeout=300s
echo -e "${GREEN}[8/8] Creating security policies and rules...${NC}"
# Create Falco custom rules
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: falco-custom-rules
namespace: falco-system
data:
custom_rules.yaml: |
- rule: Unexpected outbound connection
desc: Detect unexpected outbound network connections
condition: >
outbound and not fd.typechar=4 and not fd.typechar=6 and
not proc.name in (curl, wget, apt, yum, dnf) and
not container.image.repository in (docker.io/library/alpine, gcr.io/distroless)
output: >
Unexpected outbound connection (command=%proc.cmdline connection=%fd.name
user=%user.name container=%container.name image=%container.image.repository)
priority: WARNING
tags: [network, outbound]
- rule: Privileged container spawned
desc: Detect containers running with privileged access
condition: >
spawned_process and container and proc.vpid=1 and
container.privileged=true
output: >
Privileged container spawned (command=%proc.cmdline user=%user.name
container=%container.name image=%container.image.repository)
priority: CRITICAL
tags: [container, privilege]
EOF
# Create Gatekeeper constraint template
cat << 'EOF' | kubectl apply -f -
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: requiresecuritycontext
spec:
crd:
spec:
names:
kind: RequireSecurityContext
validation:
properties:
runAsNonRoot:
type: boolean
readOnlyRootFilesystem:
type: boolean
allowPrivilegeEscalation:
type: boolean
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package requiresecuritycontext
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.runAsNonRoot
msg := "Container must run as non-root user"
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.readOnlyRootFilesystem
msg := "Container must have read-only root filesystem"
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
container.securityContext.allowPrivilegeEscalation == true
msg := "Container must not allow privilege escalation"
}
EOF
echo -e "${GREEN}Verifying installations...${NC}"
# Wait for pods to be ready
kubectl wait --for=condition=Ready pods --all -n "$FALCO_NAMESPACE" --timeout=300s
kubectl wait --for=condition=Ready pods --all -n "$GATEKEEPER_NAMESPACE" --timeout=300s
# Check pod status
echo -e "${YELLOW}Falco pods:${NC}"
kubectl get pods -n "$FALCO_NAMESPACE"
echo -e "${YELLOW}Gatekeeper pods:${NC}"
kubectl get pods -n "$GATEKEEPER_NAMESPACE"
echo -e "${YELLOW}Validating admission webhooks:${NC}"
kubectl get validatingadmissionwebhooks | grep gatekeeper
echo -e "${GREEN}✓ Kubernetes security scanning with Falco and OPA Gatekeeper installed successfully!${NC}"
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Create constraint policies based on your security requirements"
echo "2. Monitor Falco logs: kubectl logs -n $FALCO_NAMESPACE -l app=falco"
echo "3. Test policies with sample workloads"
echo "4. Review and tune custom Falco rules as needed"
Review the script before running. Execute with: bash install.sh