Join our Newsletter — 33% off our NHI Course
Home› Guides› containerd Security: Harden the Runtime, Socket and Image…
Guide Cloud & Workload Identity

containerd Security: Harden the Runtime, Socket and Image Pulls

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 8 November 2026 13 min read
On this page

containerd runs the containers on most Kubernetes nodes, and anything that can reach its socket can create, inspect and exec into every container on the host, so the runtime's configuration is part of the node's privileged access surface. Since Kubernetes removed dockershim in v1.24, many clusters talk to containerd directly through its CRI plugin, and the hardening work has moved into /etc/containerd/config.toml, the registry host files and the runtime handlers. This guide covers socket protection, the CRI settings that matter for security, registry trust and image verification, sandboxed runtime handlers, logging and a rollout order, with exact keys from the containerd documentation. It is written for platform, node and security engineers running containerd under Kubernetes or standalone with ctr or nerdctl. Concepts common to every runtime are in the Container Runtime Security Guide; this page goes deeper on containerd itself.

This is an independent guide from NHI Mgmt Group, based on the containerd project's public documentation as of November 2026. It is not sponsored or reviewed by the containerd project. Product features change, so confirm settings against the containerd project's current documentation.

Key takeaways

  • The containerd socket is root on the node. /run/containerd/containerd.sock is owned by UID 0 and GID 0 by default; keep it that way and never mount it into workloads.
  • containerd namespaces are not a boundary. The project describes them as an administrative construct that clients can switch trivially, so any socket client reaches the k8s.io namespace and every other.
  • Know your CRI version block. containerd 2.x uses io.containerd.cri.v1.runtime and io.containerd.cri.v1.images; 1.x used io.containerd.grpc.v1.cri. Settings in the wrong block do nothing.
  • Registry trust lives in hosts.toml. Set config_path to /etc/containerd/certs.d, keep skip_verify = false, and never give a public mirror the resolve capability.
  • Add a sandboxed runtime for untrusted code. Define a gVisor (io.containerd.runsc.v1) or Kata (io.containerd.kata.v2) handler and select it per pod with a Kubernetes RuntimeClass.

How identity and access work in containerd

containerd has no user accounts. Access is decided by who can open its Unix sockets, which are protected by file ownership. The ops documentation lists the defaults:

InterfaceDefaultWhat a client can doGuidance
gRPC API socket/run/containerd/containerd.sock, uid = 0, gid = 0Full control of images, containers, tasks and snapshots in every namespaceKeep root-only; do not set a shared group
Debug socket/run/containerd/debug.sock, uid = 0, gid = 0Daemon diagnosticsKeep root-only; leave unset where not needed
Metrics127.0.0.1:1234, path /v1/metricsRead Prometheus metricsKeep on loopback or scrape through a node agent
CRI streaming serverstream_server_address = '127.0.0.1', stream_server_port = '0'Serves exec, attach and port-forward streamsKeep on loopback; enable TLS if it must move
CRI TCP servicedisable_tcp_service = trueCRI over TCPLeave disabled

Clients include the kubelet (through the CRI plugin), ctr, nerdctl, crictl and any agent given the socket. Each client picks a containerd namespace on every call, for example with ctr -n or CONTAINERD_NAMESPACE. Because namespaces are not a security feature, separation between tenants must come from Kubernetes RBAC, admission control and node isolation, not from containerd namespaces.

Rootless containerd

containerd can run as an unprivileged user with RootlessKit setting up user, mount and optional network namespaces. The containerd-rootless-setuptool.sh install script from the nerdctl project is the documented easy path, and the socket then sits under the user's runtime directory, such as /run/user/1001/containerd/containerd.sock. Requirements to plan for: the overlayfs snapshotter in a user namespace needs kernel 5.11 or later (Ubuntu and Debian kernels excepted), fuse-overlayfs works from kernel 4.18, and cgroup control needs cgroup v2 with systemd. Rootless mode suits build hosts and developer machines where users need a runtime without root.

Admin roles and privileged access

Three groups hold privileged access to a containerd node, and each needs its own control.

  • Node administrators: root on the node means control of containerd and every container. Route node access through a PAM or bastion workflow with session recording, and prefer immutable node images so changes come through the pipeline. See the Linux Server Hardening Guide.
  • Configuration owners: whoever edits config.toml, the files in /etc/containerd/certs.d or the verifier bin_dir can change which images run and how. Manage these through configuration management with review, and monitor them with file integrity monitoring.
  • Socket holders: monitoring, logging and security agents often request the socket. Each is effectively root on the node. Inventory them, prefer agents that read through the CRI with documented minimal calls, and keep the list short.

The root (/var/lib/containerd) and state (/run/containerd) directories are internal to containerd; the ops documentation says they should not be tampered with and notes that external tools watching them have caused errors. Protect them with root-only permissions and do not mount them into containers.

Privileged containers and host devices

For each runtime handler, privileged_without_host_devices defaults to false, which means a privileged pod receives host devices. Set it to true on handlers where privileged pods should not see /dev from the host. Block privileged pods outside system namespaces with Pod Security Admission. See the Kubernetes Admission Control Guide.

Integrations, API tokens and service accounts

containerd itself holds few secrets, but the credentials it uses to pull images and the clients it serves are machine identities that need ownership.

Registry hosts and credentials

Point the images plugin at a hosts directory: in containerd 2.x set config_path = "/etc/containerd/certs.d" under [plugins."io.containerd.cri.v1.images".registry]. Each registry gets a directory containing hosts.toml, and changes apply without a restart.

hosts.toml fieldPurposeSecure setting
serverFallback upstream for the namespaceYour approved registry or pull-through cache
capabilitiespull, resolve, pushPublic mirrors: pull only. Private mirrors: pull and resolve. The docs state a public mirror should never be trusted to resolve tags
caCA file or files for the registryYour internal CA for private registries
clientClient certificate and key pairsUse for mTLS to private registries
skip_verifySkips chain and hostname checksfalse (default); docs limit true to testing
headerCustom request headersAvoid static tokens here; they sit in plain files
_default directoryFallback for unmatched registriesPoint at your mirror to keep pulls inside approved sources

In Kubernetes, prefer image pull secrets per namespace, or a kubelet credential provider that issues short-lived registry tokens, over static credentials on the node. Our recommendation: any static node-level registry credential should be a read-only robot account, rotated at least every 90 days. See the Container Registry Security Guide.

Image verification

containerd can call external verifiers before an image is pulled. Enable the [plugins."io.containerd.image-verifier.v1.bindir"] plugin with:

  • bin_dir: directory of verifier executables. If it is missing or empty, verification does not block pulls, so confirm it is populated.
  • max_verifiers: how many verifiers to call (negative for no limit); verifiers are sorted by name.
  • per_verifier_timeout: time limit per verifier, such as "10s".

Each verifier receives -name, -digest and -stdin-media-type plus the OCI descriptor on standard input. Exit code 0 allows the pull; anything else blocks it, and all verifiers must allow (logical AND). A verifier error or timeout also blocks. A typical verifier wraps signature checks such as Sigstore cosign. Confirm in the documentation for your containerd version which pull paths call the verifier, and keep cluster-level signature enforcement at admission as well. See the Sigstore Guide.

Encrypted images

The images plugin supports image decryption, with image_decryption.key_model = 'node' in the 2.x defaults; leaving the key out disables decryption. Where you use encrypted images, the node's decryption keys become secrets to protect and rotate like any other.

Logging and detection

  • Daemon logs: under systemd, containerd logs to the journal. Forward journalctl -u containerd to the SIEM. Raise the log level only for troubleshooting.
  • Events: ctr events streams task and container events; agents use the same API to record create, start, exec and delete.
  • Kubernetes audit: pods/exec, pods/attach and privileged pod creation are best caught in the API server audit log. See the Kubernetes Audit Log Detection Guide.
  • Host audit: watch connections to /run/containerd/containerd.sock from processes other than the kubelet and known agents, and changes to /etc/containerd/, with auditd or eBPF sensors. See the Linux Logging and auditd for Defenders Guide.
BehaviourSignalATT&CK
Commands run in containers through the runtime, bypassing the API serverctr, nerdctl or crictl exec processes on a nodeT1609
Containers started outside Kubernetes on a nodeTasks in namespaces other than k8s.ioT1610
Container granted host accessPrivileged create, host PID or host path mounts of /T1611
Socket read for secrets or environmentUnknown process opening the containerd socketT1552.007
Image from an unapproved sourceVerifier denials, pulls bypassing your mirrorT1525

Metrics that show the controls work: nodes with a non-root socket group or socket mounted into pods (target zero); nodes with any skip_verify = true (target zero); share of untrusted workloads on a sandboxed RuntimeClass; verifier denials per week and their resolution; days from a containerd or runc release to fleet rollout.

Hardening baseline

CRI runtime settings (containerd 2.x)

Key (io.containerd.cri.v1.runtime)Documented defaultRecommendedReason
enable_selinuxfalsetrue on SELinux hostsApplies SELinux labels to containers
disable_apparmorfalsefalseKeeps AppArmor profiles active on AppArmor hosts
restrict_oom_score_adjfalsetrue unless rootlessStops containers lowering their OOM score below containerd's
disable_proc_mountfalsefalseKeeps default masked /proc paths
device_ownership_from_security_contextfalsetrue where non-root pods use devicesGives devices the pod's UID and GID rather than root
ignore_image_defined_volumesfalseConsider trueStops images creating volumes the pod spec did not declare
enable_unprivileged_portstrueKeep, with NetworkPolicyLets non-root containers bind low ports without NET_BIND_SERVICE
runc.options.SystemdCgroupConfirm for your versiontrue on systemd hostsMatches the kubelet's systemd cgroup driver

Sandboxed runtime handlers

Alongside the default runc handler (runtime_type = 'io.containerd.runc.v2'), define handlers such as runtimes.gvisor with runtime_type = "io.containerd.runsc.v1" or runtimes.kata with runtime_type = "io.containerd.kata.v2". In Kubernetes, a RuntimeClass (stable since v1.20) whose handler names that runtime lets pods opt in with spec.runtimeClassName, trading some overhead for stronger isolation. Our recommendation: run multi-tenant, CI build and AI agent code execution workloads on a sandboxed class, and keep node pools for them separate. See the AI Agent Sandboxing and Code Execution Guide.

Host baseline

  • Keep socket uid and gid at 0 and directories /var/lib/containerd and /run/containerd root-only.
  • Keep metrics on 127.0.0.1 and the CRI stream server on loopback; if streaming must listen elsewhere, set enable_tls_streaming = true with x509_key_pair_streaming files.
  • Pin the sandbox (pause) image through pinned_images.sandbox to an image in your own registry.
  • Keep containerd, its shims and runc on supported releases, apply runtime security updates promptly, and follow containerd's security advisories.

Rollout order

  1. Inventory nodes by containerd version, config block (2.x or 1.x), runtime handlers and every process or pod with socket access.
  2. Remove socket mounts from workloads and replace agents that need them with CRI-scoped alternatives where possible.
  3. Move registry settings to certs.d with hosts.toml, remove skip_verify, and route _default to your mirror.
  4. Apply the CRI runtime baseline to a canary node pool, run conformance and application tests, then promote by pool.
  5. Add the sandboxed handler and RuntimeClass on a dedicated pool; move untrusted workloads.
  6. Enable the image verifier on canary nodes, review denials, then enforce fleet-wide. Roll back by redeploying the previous node image or config.toml and restarting containerd; with KillMode=process, running containers keep running during the restart.

Common misconfigurations

  • Socket mounted into a pod. Build tools, log shippers or dashboards given containerd.sock hold root on the node. Fix: remove the mount; use rootless build tools or CRI-scoped agents.
  • Settings in the 1.x block on a 2.x install. Hardening appears present but is ignored. Fix: migrate the configuration and check the effective config with containerd config dump.
  • skip_verify = true left from a lab. Fix: install the registry CA through ca and keep verification on.
  • A public mirror allowed to resolve. Tag resolution then depends on that mirror. Fix: capabilities = ["pull"] for public mirrors and pin images by digest.
  • Treating containerd namespaces as tenancy. Fix: separate tenants with Kubernetes controls and node pools.
  • Untrusted code on runc alongside sensitive workloads. Fix: sandboxed RuntimeClass and dedicated nodes.
Control areaNIST SP 800-53 Rev 5CIS Controls v8.1ISO/IEC 27001:2022 Annex A
Socket and node privileged accessAC-3, AC-66 Access Control ManagementA.8.2, A.8.18
CRI and runtime configurationCM-6, CM-7, SC-394 Secure ConfigurationA.8.9
Registry trust and image verificationSI-7, SR-11, SC-82 Inventory and Control of Software AssetsA.8.19, A.8.24
Registry credentialsIA-55 Account ManagementA.5.17
Runtime loggingAU-2, AU-128 Audit Log ManagementA.8.15, A.8.16

Practitioner checklist

  • Confirm the containerd socket and debug socket are owned by root with no shared group, and no pod mounts them.
  • List every agent with socket access and record an owner and justification for each.
  • Check that hardening keys sit in the correct block for your containerd version and verify with containerd config dump.
  • Configure /etc/containerd/certs.d with hosts.toml, skip_verify = false and pull-only public mirrors.
  • Enable SELinux or AppArmor integration, restrict_oom_score_adj and privileged_without_host_devices where appropriate.
  • Define a gVisor or Kata handler and a RuntimeClass for untrusted workloads on dedicated nodes.
  • Enable the image verifier plugin with a populated bin_dir, plus admission-time signature checks.
  • Keep metrics and the CRI stream server on loopback and the CRI TCP service disabled.
  • Forward containerd journal logs and alert on runtime CLI use and unknown socket clients.
  • Track containerd and runc security advisories and patch node images on a set cadence.

Standards and references

Related NHI Mgmt Group resources: Container Runtime Security Guide · Kubernetes Security Guide · Kubernetes Security Checklist · Container Image Security Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 8 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org