Containers Required
Never give a container control of the host
Never run an app container with --privileged, the host's network or process namespace, or the container engine's socket mounted inside.
How to do it
Each of these hands the container root on the host, so one hole in the app takes the whole machine. If a tool must start containers, such as a CI runner, give it its own machine or a rootless engine. Add only the one capability the app needs, such as NET_BIND_SERVICE, after dropping all. Set no-new-privileges so a program cannot gain rights through setuid.
Example
services:
web:
cap_drop: [ALL]
security_opt: [no-new-privileges:true]
# Never add these to an app container:
# privileged: true
# network_mode: host
# pid: host
# volumes: [/var/run/docker.sock:/var/run/docker.sock]
How to check
For every running container, docker inspect --format '{{.HostConfig.Privileged}} {{.HostConfig.NetworkMode}} {{.HostConfig.PidMode}} {{json .Mounts}}' shows false, no host and no docker.sock.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers operations playbook: https://agentwranglers.se/playbook/operations/containers/no-privileged-containers.