DEV Community

Cheedge Lee
Cheedge Lee

Posted on • Originally published at notes-renovation.hashnode.dev

CKS Notes - some notes on docker(podman)

1. Basic CMD

These basic cmd are same structure for both docker and podman.

# build image
$ docker build -t <NAME> -f /PATH/TO/Dockerfile <BUILD_CONTEXT>

$ docker image ls

# run docker container -d detached
$ docker run --name <NAME> -d <BASE_IMAGE>

# check running contianers
$ docker ps

# check process in container
$ docker exec <CONTAINER_NAME> ps
$ docker rm <CONTAINER_NAME> --force
Enter fullscreen mode Exit fullscreen mode

<BUILD_CONTEXT>: the directory/path that Docker sends to the daemon, containing:

  • files referenced by COPY

  • files referenced by ADD

  • anything needed during build

In short, BUILD_CONTEXT is the dir/path when the Dockerfile try to do the “COPY” or “ADD” operations it will searched dir/path.

1.1 Tags

if you build the same image using docker build -t <SAME_NAME> -f /PATH/TO/Dockerfile <BUILD_CONTEXT>, it will build a new image, the old one’s tag will be removed as <none>.

$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
base-image   latest    2487f22f50cb   5 seconds ago    5.58MB
<none>       <none>    e9a4699a5cbb   17 minutes ago   5.58MB
<none>       <none>    8a8330c3730a   23 minutes ago   5.58MB
Enter fullscreen mode Exit fullscreen mode

1.2 Layers (Caching)

More layers → more caching → faster builds.

Docker creates a new layer for:

  • FROM

  • RUN

  • COPY

  • ADD

BUT NOT for:

  • CMD

  • ENTRYPOINT

  • ENV

  • EXPOSE

  • USER

  • WORKDIR

Notice: CMD and ENTRYPOINT DO NOT create layers. They are metadata, not filesystem layers.

Example:

RUN apk update
RUN apk add curl
RUN apk add git
Enter fullscreen mode Exit fullscreen mode

Each of these caches independently.

If merging them together:

RUN apk update && apk add curl git
Enter fullscreen mode Exit fullscreen mode

Then any small change breaks the entire cache.

So sometimes more layers = faster builds.

Notice: I don’t mean more layers are always good, more layers will create more temporal artifacts. So reduce layers where possible, but caching and maintainability matter more than minimizing layers.

1.3 share PID


docker run --name sidecar --pid=container:app -d busybox sleep infinity
Enter fullscreen mode Exit fullscreen mode
apiVersion: v1
kind: Pod
metadata:
  name: example
spec:
  shareProcessNamespace: true
  containers:
    - name: app
      image: nginx
    - name: sidecar
      image: busybox
      command: ["sleep", "3600"]
Enter fullscreen mode Exit fullscreen mode

2. Dockerfile Best Practice - Multi-stage build

# syntax=docker/dockerfile:1
FROM golang:1.24
WORKDIR /src
COPY <<EOF ./main.go
package main

import "fmt"

func main() {
  fmt.Println("hello, world")
}
EOF
RUN go build -o /bin/hello ./main.go

FROM scratch
COPY --from=0 /bin/hello /bin/hello
CMD ["/bin/hello"]
Enter fullscreen mode Exit fullscreen mode

the second FROM scratch will build on first FROM golang:1.24, as “The COPY --from=0 line copies just the built artifact from the previous stage into this new stage.”

more details check here.

3. Dockerfile Best Practice - Run as non-root user

RUN addgroup -S appgroup && \
    adduser -S appuser -G appgroup -h /home/appuser
# or for Alphine
RUN adduser -D -g '' appuser
Enter fullscreen mode Exit fullscreen mode

Both are secure as long as you do USER appuser.

The explicit version gives more control, but the simpler version is secure enough for typical apps.

  • -D : create user with no password, no shell, minimal config

  • -g '' : empty GECOS field

4. Dockerfile Best Practice - Never echo secrets in Dockerfile

example

Layer 1: RUN echo $TOKEN > /tmp/token
Layer 2: RUN sh -c 'register.sh /tmp/token'
Layer 3: RUN rm /tmp/token
Enter fullscreen mode Exit fullscreen mode

each RUN will create a new layer, so if we use cmd to save “TOKEN”, which means it will be saved inside that layer, even then using RUN rm command. Therefore, DO NOT save secrets in Dockerfile.

# syntax=docker/dockerfile:1.2
RUN --mount=type=secret,id=token \
    sh -c 'cat /run/secrets/token | register.sh'
# or
RUN register.sh $TOKEN
Enter fullscreen mode Exit fullscreen mode

For better practice, secret can be passed into the container during runtime as env variable TOKEN

5. Docker related Security best practice

basic configuration files:

  • /usr/lib/systemd/system/docker.socket

  • /usr/lib/systemd/system/docker.service

if you forgot the path, just check their status, eg. systemctl status docker.socket.

After changing the setting, remember to restart corresponding service.

systemctl daemon-reload 
systemctl restart docker.socket 
systemctl restart docker.service
Enter fullscreen mode Exit fullscreen mode

5.1 Do NOT expose Docker over TCP

Otherwise it will expose ROOT ACCESS without authentication.

Edit /usr/lib/systemd/system/docker.service, remove -H tcp://0.0.0.0:2375 from ExecStart.

# remove -H tcp://0.0.0.0:2375

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
Enter fullscreen mode Exit fullscreen mode

check the TCP

ss -tlnp | grep dockerd
Enter fullscreen mode Exit fullscreen mode

5.2 Enforce TLS if you must expose TCP

If remote Docker API is absolutely required:

  • enable TLS

  • require certificates

  • never use TCP without TLS

Example:

-H tcp://127.0.0.1:2376
--tlsverify
--tlscacert=ca.pem
--tlscert=server-cert.pem
--tlskey=server-key.pem
Enter fullscreen mode Exit fullscreen mode

5.3 Run dockerd as root (default), but avoid giving docker group to users

docker group = root equivalent

in /usr/lib/systemd/system/docker.socket set the

[Unit]
Description=Docker Socket for the API

[Socket]
ListenStream=/run/docker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
# either set SocketGroup=root or docker 

[Install]
WantedBy=sockets.target
Enter fullscreen mode Exit fullscreen mode

Never add regular users to docker:

# check user belonging group
group <USER_NAME> # eg. group docker

# show all groups
getent group
vi /etc/group

# print real and effective user and group IDs
id <USER>
vi /etc/passwd
# $ id ubuntu
# uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),105(lxd)

# delete teh user from group
gpasswd -d <USER> <FROM_GROUP>
gpasswd -d <USER> docker
groupdel docker
# then socket file created with group = root
Enter fullscreen mode Exit fullscreen mode

5.4 Use AppArmor or SELinux

Hardens container isolation.

Ubuntu:

# --security-opt apparmor=default
docker run --security-opt apparmor=myprofile ...
Enter fullscreen mode Exit fullscreen mode

RHEL-based:

# --security-opt label=type:container_t
docker run --security-opt label:type:svirt_lxc_net_t ...
Enter fullscreen mode Exit fullscreen mode

which is similar as Kubernetes-level AppArmor

In Kubernetes, we do NOT call docker run. We must annotate the Pod:

metadata:
  annotations:
    container.apparmor.security.beta.kubernetes.io/mycontainer: localhost/myprofile
Enter fullscreen mode Exit fullscreen mode

5.5 Restrict container capabilities

Capabilities = parts of root’s power.

Remove everything, then add only needed:

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ...
Enter fullscreen mode Exit fullscreen mode

which is similar with Kubernetes-level capabilities

In Pods:

securityContext:
  capabilities:
    drop: ["ALL"]
    add: ["NET_BIND_SERVICE"]
Enter fullscreen mode Exit fullscreen mode

Kubernetes simply passes these to the container runtime (Docker, containerd, CRI-O).

5.6 Make /var/lib/docker read-only

Prevents tampering.

--read-only
Enter fullscreen mode Exit fullscreen mode

5.7 Enable logging limits

Avoid log spamming or disk filling.

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)