containerd Security Best Practices

Secure your containerd deployment with industry-standard best practices. This guide covers seccomp profiles, AppArmor and SELinux policies, image signing, and security hardening for production container workloads.

Security Hardening seccomp AppArmor SELinux
Why Container Security Matters

Containers share the host kernel, making security boundaries critical. A container escape can compromise the entire host and all workloads running on it. containerd provides multiple security layers to protect your infrastructure: seccomp profiles, AppArmor/SELinux policies, image signing, and more.

This guide covers security best practices for containerd deployments, from basic hardening to advanced security features. Implementing these practices helps protect against container escapes, privilege escalation, and supply chain attacks.

Security is a layered approach. Combine seccomp, AppArmor/SELinux, and image signing for defense in depth. Start with the basics and gradually implement more advanced controls.
Security Layers in containerd

seccomp

System call filtering. Restricts which kernel syscalls containers can make.
Profile: Default, custom, or unconfined

AppArmor

Mandatory Access Control (MAC). Restricts container capabilities and file access.
Profile: per-container or per-pod

SELinux

Label-based MAC. Provides fine-grained access control for containers.
Profile: targeted, MLS

Image Signing

Verify image integrity and origin. Prevents supply chain attacks.
Tools: Cosign, Notary, Sigstore

Rootless Mode

Run containerd and containers without root privileges.
Security: Reduces attack surface

Secrets Management

Secure handling of sensitive data in containers.
Tools: Kubernetes Secrets, HashiCorp Vault
seccomp: System Call Filtering

seccomp (Secure Computing Mode) restricts the system calls a container can make. This reduces the attack surface by preventing malicious or unnecessary syscalls.

# Default seccomp profile (in /etc/containerd/config.toml) [plugins."io.containerd.grpc.v1.cri".containerd] default_runtime_name = "runc" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] # Enable seccomp SystemdCgroup = true # Use default seccomp profile SeccompProfile = "/usr/share/containerd/seccomp/default.json" # Custom seccomp profile (minimal) { "defaultAction": "SCMP_ACT_ERRNO", "architectures": ["SCMP_ARCH_X86_64"], "syscalls": [ {"names": ["accept", "accept4", "access", "alarm"], "action": "SCMP_ACT_ALLOW"}, {"names": ["mount", "unmount", "umount2"], "action": "SCMP_ACT_ERRNO"} ] } # Disable seccomp (not recommended) # SeccompProfile = "unconfined"
Using "unconfined" disables seccomp entirely. This is not recommended for production workloads. Always use a seccomp profile.
AppArmor: Mandatory Access Control

AppArmor provides MAC policies that restrict container capabilities, file access, and network permissions. It's widely used on Debian/Ubuntu systems.

# Create an AppArmor profile for nginx sudo cat > /etc/apparmor.d/containerd-nginx < # Network permissions network inet tcp, network inet udp, # File access /usr/sbin/nginx r, /etc/nginx/** r, /var/log/nginx/** w, /var/run/nginx.pid rw, /run/nginx.pid rw, # Deny shell access deny /bin/sh r, deny /usr/bin/bash r, } EOF # Load the profile sudo apparmor_parser -r /etc/apparmor.d/containerd-nginx # Apply to container (in config.toml) [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] AppArmorProfile = "containerd-nginx" # Check AppArmor status sudo aa-status | grep containerd
AppArmor profiles are enforced by the kernel. They provide fine-grained control over container permissions. Always test profiles in a staging environment first.
SELinux: Label-Based MAC

SELinux (Security-Enhanced Linux) provides advanced MAC using security labels. It's widely used on RHEL, CentOS, and Fedora systems.

# Check SELinux status sestatus # Set SELinux to enforcing mode sudo setenforce 1 # Configure SELinux for containerd sudo semanage permissive -a container_runtime_t sudo semodule -d container_runtime # Apply SELinux context to containerd directories sudo chcon -R -t container_runtime_t /var/lib/containerd sudo chcon -R -t container_runtime_t /var/run/containerd # Configure containerd for SELinux (in config.toml) [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SELinuxContext = "system_u:system_r:container_runtime_t:s0" # Verify SELinux labels ls -Z /var/lib/containerd
SELinux can be complex to configure. Start with permissive mode (setenforce 0) to test policies before enforcing. Use audit2allow to generate policies from logs.
Image Signing & Verification

Image signing ensures container images come from trusted sources and haven't been tampered with. Use Cosign (Sigstore) or Notary for image signing.

# Install Cosign curl -L https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 -o /usr/local/bin/cosign chmod +x /usr/local/bin/cosign # Sign an image cosign sign --key cosign.key ghcr.io/user/nginx:latest # Verify an image cosign verify --key cosign.pub ghcr.io/user/nginx:latest # Configure containerd for image verification (in config.toml) [plugins."io.containerd.grpc.v1.cri".registry] # Verify signatures [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."ghcr.io".tls] insecure_skip_verify = false
Image signing is a critical supply chain security control. Always verify images before pulling in production. Use Cosign with GitHub Actions or GitLab CI for automated signing.
Rootless containerd for Security

Running containerd and containers without root privileges significantly reduces the impact of a container escape. See the Rootless containerd guide for detailed instructions.

# Enable rootless mode export CONTAINERD_NAMESPACE=default export DOCKER_HOST=unix:///run/user/$UID/docker.sock # Run container without root nerdctl run --rm --user 1000:1000 alpine echo "Hello" # Check user mapping cat /proc/self/uid_map
Rootless mode adds an extra layer of security by eliminating root privileges. It's recommended for development and CI/CD environments.
Capability Management

Linux capabilities are a per-thread permission model. Dropping unnecessary capabilities reduces the attack surface.

# Run container with limited capabilities nerdctl run --rm --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx # Configure capabilities in config.toml [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] # Drop all capabilities by default Capabilities = ["NET_BIND_SERVICE", "CHOWN", "SETUID", "SETGID"] # Common capabilities to drop: # CAP_SYS_ADMIN (mount, filesystem) # CAP_SYS_PTRACE (debugging) # CAP_SYS_TIME (system time) # CAP_MKNOD (device nodes)
Network Security

Restrict container network access using network policies and CNI plugins.

# Network policy (Kubernetes) apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all spec: podSelector: {} policyTypes: - Ingress - Egress # CNI plugin configuration (Calico) # /etc/cni/net.d/10-calico.conflist { "name": "k8s-pod-network", "cniVersion": "0.3.1", "plugins": [ { "type": "calico", "policy": { "type": "k8s" } } ] }
Security Checklist for Production
  • ✓ Enable seccomp - Use default or custom profiles.
  • ✓ Configure AppArmor or SELinux - Enforce MAC policies.
  • ✓ Drop unnecessary capabilities - Use minimal capability sets.
  • ✓ Verify image signatures - Use Cosign or Notary.
  • ✓ Use rootless mode - For development and CI/CD.
  • ✓ Implement network policies - Restrict ingress/egress.
  • ✓ Log and monitor - Use metrics and audit logs.
  • ✓ Keep containerd updated - Apply security patches.
  • ✓ Use minimal base images - Alpine or distroless.
  • ✓ Scan images for vulnerabilities - Trivy, Clair.
This checklist covers the most important security controls for containerd. Implement them gradually and test thoroughly.
Frequently Asked Questions
What is the difference between seccomp and AppArmor?
seccomp restricts system calls (syscalls), while AppArmor restricts file access and capabilities. Both are complementary: use seccomp to filter syscalls and AppArmor for file and network access.
Should I use AppArmor or SELinux?
Choose based on your distribution. Debian/Ubuntu use AppArmor by default. RHEL/CentOS/Fedora use SELinux. Both provide MAC; SELinux offers more granular policies but is more complex.
How do I test seccomp profiles?
Use `strace` to see syscalls used by your container. Start with the default profile, then customize based on observed syscalls. Test in staging before production.
What is image signing and why is it important?
Image signing uses cryptographic signatures to verify image integrity and origin. It prevents supply chain attacks by ensuring you're running trusted images.
Can I use containerd with Kubernetes security contexts?
Yes! Kubernetes SecurityContext allows setting seccomp, AppArmor, SELinux, and capabilities at the pod or container level. containerd respects these settings.
How do I update containerd securely?
Use package managers (apt, yum) to get security updates. For Kubernetes nodes, drain the node before updating, then uncordon. Always test updates in staging first.
What is the minimal seccomp profile for a container?
A minimal profile blocks all syscalls except those explicitly required. This reduces attack surface but may break applications. Start with the default profile and customize based on application needs.
Does containerd support FIPS compliance?
Yes, containerd can be compiled with FIPS-compliant crypto modules. This is important for government and regulated industries. Check the containerd documentation for FIPS-specific builds.
Previous: Performance Tuning Next: Image Signing

Security is a journey, not a destination. Start with the basics, monitor your workloads, and continuously improve your security posture.