Security & Rootless
Comprehensive security features, rootless containers, hardening techniques, and best practices for secure container deployment with Podman.
Rootless Containers
Understanding Rootless Mode
Podman runs in rootless mode by default, providing enhanced security by running containers as a regular user without requiring root privileges.
Setting Up Rootless
# Check if rootless is configured
podman info | grep -A 10 "runRoot"
# Configure user namespaces (if needed)
grep $USER /etc/subuid /etc/subgid
# If not present, add entries
echo "$USER:100000:65536" | sudo tee -a /etc/subuid
echo "$USER:100000:65536" | sudo tee -a /etc/subgid
# Enable user lingering for systemd services
sudo loginctl enable-linger $USER
# Restart user session or reboot
Rootless Configuration
# User configuration directory
~/.config/containers/
# User storage location
~/.local/share/containers/storage/
# User registries configuration
~/.config/containers/registries.conf
# Check rootless status
podman info | grep -E "(runRoot|graphRoot|rootless)"
Rootless Limitations and Workarounds
# Port binding limitations (ports < 1024 require root)
# Workaround: Use port mapping
podman run -p 8080:80 nginx # Map to unprivileged port
# Limited networking capabilities
# Workaround: Use slirp4netns for advanced networking
podman run --network slirp4netns:enable_ipv6=true nginx
# File permissions
# Workaround: Use --userns=keep-id
podman run --userns=keep-id -v $(pwd):/workspace alpine
User Namespaces
User Namespace Mapping
# Check current mapping
podman unshare cat /proc/self/uid_map
podman unshare cat /proc/self/gid_map
# Run with custom UID/GID mapping
podman run --uidmap 0:1000:1000 --gidmap 0:1000:1000 alpine id
# Keep current user ID mapping
podman run --userns=keep-id -v $(pwd):/workspace alpine id
# Use host user namespace (requires root)
sudo podman run --userns=host alpine id
File Permission Handling
# Automatic UID/GID mapping for volumes
podman run --userns=keep-id -v $(pwd):/data alpine touch /data/file
# Fix ownership issues
podman unshare chown -R 0:0 /path/to/directory
# Check file ownership in namespace
podman unshare ls -la /path/to/files
Security Options
Capabilities Management
# Drop all capabilities
podman run --cap-drop ALL alpine
# Add specific capabilities
podman run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
podman run --cap-drop ALL --cap-add CHOWN --cap-add DAC_OVERRIDE alpine
# List available capabilities
podman run --cap-add ALL alpine sh -c 'cat /proc/self/status | grep Cap'
# Common capability combinations
# Web server
podman run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
# File management
podman run --cap-drop ALL --cap-add CHOWN --cap-add DAC_OVERRIDE alpine
# System monitoring
podman run --cap-drop ALL --cap-add SYS_PTRACE monitoring-app
Security Profiles
# AppArmor profiles
podman run --security-opt apparmor=docker-default nginx
podman run --security-opt apparmor:unconfined nginx # Disable AppArmor
# SELinux labels
podman run --security-opt label=level:s0:c123,c456 nginx
podman run --security-opt label:disable nginx
# Seccomp profiles
podman run --security-opt seccomp=default.json nginx
podman run --security-opt seccomp:unconfined nginx # Disable seccomp
# Disable new privileges
podman run --security-opt no-new-privileges nginx
Read-only Filesystems
# Read-only root filesystem
podman run --read-only nginx
# Read-only with tmpfs for writable areas
podman run --read-only --tmpfs /tmp --tmpfs /var/run nginx
# Read-only with specific writable volumes
podman run --read-only \
--tmpfs /tmp \
--tmpfs /var/cache \
-v app-logs:/var/log \
nginx
Container Hardening
Resource Limits
# Memory limits
podman run --memory=512m nginx
podman run --memory=512m --memory-swap=1g nginx
# CPU limits
podman run --cpus=1.5 nginx
podman run --cpu-shares=512 nginx
podman run --cpuset-cpus=0,1 nginx
# Process limits
podman run --pids-limit=100 nginx
# File descriptor limits
podman run --ulimit nofile=1024:2048 nginx
Network Security
# Disable networking
podman run --network=none alpine
# Bind to localhost only
podman run -p 127.0.0.1:8080:80 nginx
# Use custom isolated network
podman network create --internal secure-net
podman run --network=secure-net nginx
# Disable inter-container communication
podman network create --opt com.docker.network.bridge.enable_icc=false isolated
User Security
# Run as non-root user
podman run -u 1000:1000 nginx
podman run --user nginx nginx
# Create and use specific user
FROM alpine
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
USER appuser
Image Security
Secure Image Building
# Use official, minimal base images
FROM alpine:3.18
FROM node:18-alpine
FROM distroless/java
# Update packages during build
RUN apk update && apk upgrade && \
apk add --no-cache curl && \
rm -rf /var/cache/apk/*
# Create non-root user
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
# Copy files with correct ownership
COPY app /app/
# Switch to non-root user
USER appuser
# Don't store secrets in images
# Use build-time secrets instead
RUN \
API_KEY=$(cat /run/secrets/api_key) && \
# Use API_KEY during build
Image Scanning
# Scan with Trivy
podman run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image myapp:latest
# Scan for high/critical vulnerabilities only
podman run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image --severity HIGH,CRITICAL myapp:latest
# Generate JSON report
podman run --rm -v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd):/output \
aquasec/trivy image --format json --output /output/report.json myapp:latest
# Scan filesystem
podman run --rm -v $(pwd):/workspace \
aquasec/trivy fs /workspace
Content Trust and Signing
# Enable content trust (requires setup)
export PODMAN_CONTENT_TRUST=1
# Sign images (requires signing keys)
podman push --sign-by user@example.com myregistry/myapp:latest
# Verify signatures
podman pull --verify myregistry/myapp:latest
# Policy-based verification
# /etc/containers/policy.json
{
"default": [{"type": "reject"}],
"transports": {
"docker": {
"myregistry.com": [
{
"type": "signedBy",
"keyType": "GPGKeys",
"keyPath": "/path/to/pubkey.gpg"
}
]
}
}
}
System Security
Daemon Security
# User service (rootless)
systemctl --user status podman
systemctl --user enable podman.socket
systemctl --user start podman.socket
# Socket permissions
ls -la /run/user/$UID/podman/podman.sock
# API security
podman system service --cors="*" tcp://127.0.0.1:8080 # Development only
podman system service unix:///tmp/podman.sock # Production
Audit and Logging
# Enable audit logging
podman events --filter type=container
# Log to journal
journalctl -u user@$UID.service -f
# Custom logging
podman run --log-driver=journald nginx
podman run --log-driver=syslog nginx
# Monitor security events
podman events --filter event=die --filter event=kill
Pod Security
Secure Pod Configuration
# Create pod with security options
podman pod create --name secure-pod \
--security-opt label=level:s0:c123,c456 \
--userns=keep-id
# Add containers with additional security
podman run -d --pod secure-pod \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--read-only \
--tmpfs /tmp \
nginx
# Pod with resource limits
podman pod create --name limited-pod \
--memory=1g \
--cpus=2.0 \
--pids-limit=200
Multi-tier Security
# Frontend pod (public-facing)
podman pod create --name frontend \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
-p 80:80
# Backend pod (internal)
podman pod create --name backend \
--cap-drop ALL \
--network=internal-net
# Database pod (most restricted)
podman pod create --name database \
--cap-drop ALL \
--read-only \
--tmpfs /tmp \
--network=db-net
Security Monitoring
Runtime Security
# Monitor container behavior
podman exec container_name ps aux
podman exec container_name netstat -tulpn
podman exec container_name ss -tulpn
# Check for privilege escalation
podman exec container_name cat /proc/self/status | grep -E "(Uid|Gid|Groups)"
# Monitor file changes
podman diff container_name
# Resource monitoring
podman stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.PIDs}}"
Security Benchmarks
# CIS Benchmark compliance (manual checks)
# Check user namespaces
podman info | grep rootless
# Check capabilities
podman inspect container_name | grep -A 10 CapAdd
# Check read-only filesystem
podman inspect container_name | grep ReadonlyRootfs
# Check resource limits
podman inspect container_name | grep -A 20 Resources
Compliance and Best Practices
Security Checklist
# Image security
# ✓ Use official base images
# ✓ Keep images updated
# ✓ Scan for vulnerabilities
# ✓ Use minimal images
# ✓ Don't store secrets in images
# Runtime security
# ✓ Run rootless
# ✓ Use non-root user
# ✓ Drop unnecessary capabilities
# ✓ Use read-only filesystems
# ✓ Set resource limits
# Network security
# ✓ Use custom networks
# ✓ Avoid host networking
# ✓ Bind to specific interfaces
# ✓ Implement network segmentation
# Operational security
# ✓ Regular security updates
# ✓ Monitor container behavior
# ✓ Implement logging
# ✓ Use security profiles
# ✓ Regular compliance checks
Production Security Configuration
# secure-container.yaml (for documentation)
apiVersion: v1
kind: Pod
metadata:
name: secure-app
labels:
security.policy: 'restricted'
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: app
image: myapp:secure
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
resources:
limits:
memory: '512Mi'
cpu: '500m'
requests:
memory: '256Mi'
cpu: '250m'
Emergency Procedures
Security Incident Response
# Immediately stop suspicious container
podman stop suspicious_container
# Preserve container for analysis
podman commit suspicious_container evidence:$(date +%Y%m%d_%H%M%S)
# Export container for forensics
podman export suspicious_container > evidence_$(date +%Y%m%d_%H%M%S).tar
# Remove compromised container
podman rm suspicious_container
# Check for other affected containers
podman ps --filter ancestor=suspicious_image
System Lockdown
# Stop all containers
podman stop $(podman ps -q)
# Remove all containers
podman rm $(podman ps -aq)
# Reset system (use with caution)
podman system reset --force
# Rebuild with security patches
podman pull --all-tags base_image
podman build --no-cache -t app:secure .
Quick Reference
Essential Security Commands
# Rootless operations
podman info | grep rootless
podman unshare ls -la /path
# Security options
podman run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
podman run --read-only --tmpfs /tmp nginx
podman run --security-opt no-new-privileges nginx
# User management
podman run -u 1000:1000 nginx
podman run --userns=keep-id alpine
# Scanning and monitoring
podman run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy image myapp
podman events --filter type=container
Security Best Practices
# Always run rootless
podman run nginx # Rootless by default
# Use minimal privileges
podman run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
# Implement defense in depth
podman run --read-only --tmpfs /tmp --user 1000:1000 \
--cap-drop ALL --memory=512m nginx
# Regular security maintenance
podman image prune -a
podman system prune --volumes
podman pull base_image:latest && podman build --pull -t app .