도커 컨테이너 보안 가이드: 루트 권한 배제와 격리 기법

💡 핵심 요약 (Executive Summary)

본 가이드는 oci docker Container Security 핵심 가이드 및 실전 아키텍처의 핵심 개념, 실전 구성 명령어 및 아키텍처 베스트 프랙티스를 다룹니다. 클라우드 네이티브 및 인프라 엔지니어링 실무에 즉시 활용 가능하도록 체계적으로 정리했습니다.

📑 Table of Contents
Dockerfile Hardening
Example
Reference & Guidelines (NOTE)

There is a Dockerfile at /root/image/Dockerfile .

It’s a simple container which tries to make a curl call to an imaginary api with a secret token, the call will 404 , but that’s okay.

  1. Use specific version 20.04 for the base image
  2. Remove layer caching issues with apt-get
  3. Remove the hardcoded secret value 2e064aad-3a90–4cde-ad86–16fad1f8943e . The secret value should be passed into the container during runtime as env variable TOKEN
  4. Make it impossible to podman exec , docker exec or kubectl exec into the container using bash

You can build the image using

bash
cd /root/image
podman build .
Solution
bash
cd /root/image
vi Dockerfile
```vim
### before
FROM ubuntu
RUN apt-get update
RUN apt-get -y install curl
ENV URL https://google.com/this-will-fail?secret-token=
CMD ["sh", "-c", "curl --head $URL2e064aad-3a90–4cde-ad86–16fad1f8943e"]

### after
FROM ubuntu:20.04
RUN apt-get update && apt-get -y install curl
ENV URL https://google.com/this-will-fail?secret-token=
RUN rm /usr/bin/bash
CMD ["sh", "-c", "curl --head $URL$TOKEN"]
`

podman build -t app .
podman run -d -e TOKEN=2e064aad-3a90–4cde-ad86–16fad1f8943e app sleep 1d
podman ps | grep app #get container_id
podman exec -it $container_id bash #fails
podman exec -it $container_id sh #works
Container Image Footprint User
Example 1
Reference & Guidelines (NOTE)

There is a given Dockerfile under /opt/ks/Dockerfile .

Using Docker:

  • Build an image named base-image from the Dockerfile.
  • Run a container named c1 from that image.
  • Check under which user the sleep process is running inside the container
Solution
bash
cd /opt/ks
docker build -t base-image .
docker run -d --name c1 base-image
docker ps
```prompt
CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS          PORTS     NAMES
d61223f20e24   base-image   "/bin/sh -c 'sh -c '…"   16 seconds ago   Up 15 seconds             c1
`
docker exec -it c1 ps
```prompt
PID   USER     TIME  COMMAND
    1 root      0:00 sleep 1d
   21 root      0:00 ps
`

Example 2

Reference & Guidelines (NOTE)

– Modify the Dockerfile /opt/ks/Dockerfile to run processes as user appuser
– Update the image base-image with your change
– Build a new container c2 from that image

Solution
bash
vi Dockerfile 
```vim
### before
FROM alpine:3.12.3

RUN adduser -D -g '' appuser

CMD sh -c 'sleep 1d'

### after
FROM alpine:3.12.3

RUN adduser -D -g '' appuser
USER appuser

CMD sh -c 'sleep 1d'
`

docker build -t base-image .
docker run -d --name c2 base-image
docker exec -it c2 ps
```prompt
PID   USER     TIME  COMMAND
    1 appuser   0:00 sleep 1d
    7 appuser   0:00 ps
`
Container Namespaces
Example 1

Run two Docker containers app1 and app2 with the following attributes:

  • they should run image nginx:alpine
  • they should share the same PID kernel namespace
  • they should run command sleep infinity
  • they should run in the background (detached)

Then check which container sees which processes and make sense of why.

  • 특이사항

    – docker, podman 동일함.

  • 동작 원리 이해하기

    – 기본적으로 Docker 컨테이너는 Linux의 네임스페이스(Namespace) 기술을 이용해 프로세스(PID), 네트워크, 파일시스템 등을 철저하게 격리합니다.
    – 일반적인 상황: app1과 app2는 각각 독립된 방(PID 네임스페이스)을 가집니다. 따라서 app1은 app2가 뭘 하는지 전혀 알 수 없습니다.
    – -pid=container:app1을 사용한 상황: app2를 실행할 때 “나는 app1과 같은 PID 방을 쓸래!”라고 지정한 것입니다. 결과적으로 두 컨테이너가 프로세스 뷰를 완벽하게 공유하게 됩니다.

  • 이 기능을 언제 쓰나요? (실무 활용 예시)

    – 실무에서는 주로 사이드카(Sidecar) 패턴이나 디버깅을 할 때 이 기능을 유수하게 사용합니다.
    – 컨테이너 디버깅/모니터링: 운영 중인 컨테이너(app1)에 문제가 생겼을 때, 소스코드나 운영 환경을 건드리지 않고, 디버깅 도구만 담긴 또 다른 컨테이너(app2)를 붙여서 app1 내부의 프로세스 상태를 추적하고 진단할 수 있습니다.
    – 로그 및 메트릭 수집: 메인 애플리케이션 컨테이너가 돌아가고 있을 때, 그 프로세스의 자원 사용량(CPU, 메모리 등)을 실시간으로 감시하는 수집기(Agent) 컨테이너를 같은 PID 네임스페이스로 묶어서 실행합니다. Kubernetes(쿠버네티스)의 팟(Pod) 구조도 내부적으로는 이와 유사하게 프로세스 및 네크워크 환경을 공유하도록 설계되어 있습니다.

Solution
bash
docker run --name app1 -d nginx:alpine sleep infinity
docker exec -it app1 ps aux
```prompt
PID   USER     TIME  COMMAND
    1 root      0:00 sleep infinity
   12 root      0:00 ps aux
`

docker run --name app2 --pid=container:app1 -d nginx:alpine sleep infinity
docker exec -it app2 ps aux
```prompt
PID   USER     TIME  COMMAND
    1 root      0:00 sleep infinity
   18 root      0:00 sleep infinity
   24 root      0:00 ps aux
`
docker exec -it app1 ps aux
```prompt
PID   USER     TIME  COMMAND
    1 root      0:00 sleep infinity
   18 root      0:00 sleep infinity
   30 root      0:00 ps aux
`

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *