Docker Hardened Image 사용하기

Docker Hardened Image 사용하기

Docker Hardened Image(DHI)를 풀·참조·실행하고, CI/CD 파이프라인과 Kubernetes에서 사용하며, 다양한 변형(dev, static, Socket Firewall, 컴플라이언스/ELS)을 활용하는 방법을 알아봐요.

출처: 문서

본문

Docker Hardened Image(DHI)는 Docker Hub의 다른 이미지처럼 사용할 수 있어요. DHI는 익숙한 사용 패턴을 따릅니다. docker pull로 풀하고, Dockerfile에서 참조하며, docker run으로 컨테이너를 실행해요.

핵심 차이는 DHI가 보안 중심이고 공격 표면을 줄이기 위해 의도적으로 최소화되었다는 점이에요. 이는 일부 변형에 셸이나 패키지 매니저가 포함되지 않고, 기본적으로 non-root 사용자로 실행될 수 있음을 의미해요.

[!IMPORTANT]

DHI Community 이미지를 풀하려면 Docker Hardened Images 레지스트리(dhi.io)에 인증해야 해요. 다음 중 하나로 인증할 수 있어요.

인증하려면 docker login dhi.io를 실행해요.

DHI 채택 시 고려사항(Considerations when adopting DHIs)

Docker Hardened Images는 보안을 개선하기 위해 의도적으로 최소화됐어요. 기존 Dockerfile이나 프레임워크를 DHI를 사용하도록 업데이트한다면, 런타임 이미지에 셸이나 패키지 매니저가 없고 기본적으로 non-root 사용자로 실행되며, 익숙한 이미지와 구성이 다를 수 있음을 유의하세요.

마이그레이션 고려사항의 포괄적인 체크리스트와 상세 안내는 Migrate to Docker Hardened Images를 봐요.

DHI 풀, 실행, 참조

Docker Hardened Images는 구독에 따라 다른 이미지 참조를 사용해요.

구독 이미지 참조 인증
Community dhi.io/<image>:<tag> docker login dhi.io
Select & Enterprise <your-org>/<image>:<tag> docker login

Select와 Enterprise 사용자는 컴플라이언스 변형과 커스터마이즈 기능에 접근하기 위해 저장소를 Docker Hub 조직으로 미러링해야 해요.

인증 후 표준 Docker 명령과 Dockerfile에서 이미지 참조를 사용해요. 예를 들면:

$ docker pull dhi.io/python:3.13
$ docker run --rm dhi.io/python:3.13 python -c "print('Hello from DHI')"
FROM dhi.io/python:3.13
COPY . /app
CMD ["python", "/app/main.py"]

멀티 스테이지 빌드의 경우:

사용 가능한 변형을 검색하는 방법은 Search and evaluate images를 참조해요.

CI/CD 파이프라인에서 DHI 사용

Docker Hardened Images는 CI/CD 파이프라인에서 다른 이미지처럼 작동해요. Dockerfile에서 참조하거나, 파이프라인 단계의 일부로 풀하거나, 빌드와 테스트 중 이를 기반으로 컨테이너를 실행할 수 있어요.

일반적인 컨테이너 이미지와 달리 DHI는 SBOM과 provenance 메타데이터 같은 서명된 attestation도 포함해요. 도구가 지원한다면 이를 파이프라인에 통합해 공급망 보안, 정책 검사, 감사 요구사항을 지원할 수 있어요.

소프트웨어 공급망을 강화하려면 DHI에서 이미지를 빌드할 때 자체 attestation을 추가하는 것을 고려해요. 이렇게 하면 이미지가 어떻게 빌드되었는지 문서화하고, 무결성을 검증하며, Docker Scout 같은 도구로 다운스트림 검증과 정책 적용을 가능하게 해요.

빌드 프로세스 중 attestation을 첨부하는 방법은 Docker Build Attestations을 참조해요.

ORAS로 attestation 발견하기

ORAS를 사용해 Docker Hardened Images에 첨부된 attestation을 발견·검사할 수 있어요. 이는 공급망 보안 검증과 컴플라이언스 검사에서 CI/CD 파이프라인에 특히 유용해요.

자동화된 워크플로에서는 조직 액세스 토큰(OAT)으로 인증해요. OAT는 개별 사용자가 아닌 조직이 소유하므로 CI/CD 파이프라인에 더 적합해요.

ORAS로 attestation을 발견하려면:

  1. Read public repositories 범위로 조직 액세스 토큰을 생성해요.

    다음 예시는 dhi.io의 DHI community 이미지에서 attestation을 발견하는 방법을 보여줘요. 조직으로 미러링된 이미지에서 attestation을 발견한다면, Read public repositories 대신 미러링된 저장소를 읽도록 범위가 지정된 OAT를 생성해요.

  2. 조직 이름을 사용자 이름으로, OAT를 비밀번호로 사용해 dhi.io에 로그인해요.

    [!WARNING]

    다음 예시는 데모 목적으로 자격 증명을 명령줄에 직접 내보내요. 이는 셸 히스토리와 프로세스 목록에 민감한 토큰을 노출해요. 프로덕션 환경에서는 제한된 권한의 파일에서 읽기, 런타임에 로드되는 환경 파일, 시크릿 관리 도구 같은 안전한 방법을 사용해요.

    $ oras login dhi.io -u <YOUR_ORGANIZATION_NAME>
    

    또는 CI/CD 파이프라인에서 비대화형으로, 조직 이름과 토큰을 설정해요.

    $ export DOCKER_ORG="YOUR_ORGANIZATION_NAME"
    $ export OAT="YOUR_ORGANIZATION_ACCESS_TOKEN"
    $ echo $OAT | oras login dhi.io -u "$DOCKER_ORG" --password-stdin
    
  3. DHI 이미지에서 attestation을 발견해요.

    $ oras discover dhi.io/node:24-dev --platform linux/amd64
    

    [!NOTE]

    --platform 플래그가 필요해요. 없으면 oras discover가 멀티 아키텍처 이미지 인덱스로 해석되어, 전체 플랫폼별 attestation 집합 대신 인덱스 수준의 서명만 반환해요.

    성공적인 응답은 SBOM, provenance, 취약점 보고서, 변경 로그 메타데이터를 포함한 이미지에 첨부된 attestation을 나열해요.

컴파일된 실행 파일에 static 이미지 사용

Docker Hardened Images는 극도로 최소화되고 안전한 런타임에서 컴파일된 실행 파일을 실행하도록 특별히 설계된 static 이미지 저장소를 포함해요. non-hardened FROM scratch 이미지와 달리 DHI static 이미지는 attestation과 ca-certificates 같은 필수 패키지를 포함해요.

-dev 또는 다른 빌더 이미지로 바이너리를 컴파일한 다음 출력을 static 이미지로 복사해요.

FROM dhi.io/golang:1.22-dev AS build
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o myapp

FROM dhi.io/static:20230311
COPY --from=build /app/myapp /myapp
ENTRYPOINT ["/myapp"]

더 많은 멀티 스테이지 빌드 패턴은 Go 마이그레이션 예시를 봐요.

프레임워크 기반 애플리케이션에 dev 변형 사용

패키지 매니저나 빌드 도구가 필요한 프레임워크(Python, Node.js, Go 등)로 애플리케이션을 빌드한다면, 개발 또는 빌드 단계에서 -dev 변형을 사용해요. 이 변형에는 로컬 반복과 CI 워크플로를 지원하는 셸, 컴파일러, 패키지 매니저 같은 필수 유틸리티가 포함돼요.

내부 개발 루프나 격리된 CI 단계에서 -dev 이미지를 사용해 생산성을 극대화해요. 프로덕션용 아티팩트를 만들 준비가 되면 공격 표면과 이미지 크기를 줄이기 위해 더 작은 런타임 변형으로 전환해요.

dev 변형을 사용한 상세한 멀티 스테이지 Dockerfile 예시는 마이그레이션 예시를 참조해요.

Socket Firewall 변형으로 패키지 설치 모니터링

의존성 설치 중 공급망 보호를 원한다면 빌드 단계에서 표준 -dev 변형 대신 Socket Firewall 변형을 사용해요. 이 변형에는 Socket이 사전 설치되어 패키지 매니저 활동을 모니터링하고 악성 패키지가 이미지에 도달하기 전에 차단해요.

두 등급이 있어요. Socket Firewall Free에는 -sfw-dev를, Socket Firewall Enterprise에는 -sfw-ent-dev를 사용해요(Enterprise는 Socket의 API 키 필요). 런타임 단계는 어떤 빌드 단계 변형을 사용하든 동일하게 유지돼요.

FROM dhi.io/python:3.13-alpine3.23-sfw-dev AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

FROM dhi.io/python:3.13-alpine3.23
COPY --from=build /app /app
CMD ["python", "app.py"]

Socket Firewall 변형에 대한 자세한 내용은 Available image types를 봐요.

컴플라이언스 및 ELS 변형 사용

DHI Select 또는 DHI Enterprise 구독이 있으면 추가 이미지 변형에 접근할 수 있어요.

  • 컴플라이언스 변형: 규제 요구사항을 위한 FIPS 지원 및 STIG 준비 이미지
  • ELS(Extended Lifecycle Support) 변형(애드온 필요): 수명 종료(EOL) 이미지 버전용 보안 패치

이 변형에 접근하려면 저장소를 Docker Hub 조직으로 미러링해요. ELS의 경우 미러링 설정 시 Mirror end-of-life images를 활성화해요. 미러링이 완료되면 다른 이미지 태그처럼 컴플라이언스 또는 EOL 태그를 사용해요.

Kubernetes에서 사용

Docker Hardened Images를 Kubernetes에 배포할 때 프로세스는 다른 컨테이너 이미지와 비슷하지만 한 가지 핵심 차이가 있어요: DHI 레지스트리에 인증하기 위해 이미지 풀 시크릿(image pull secret)을 구성해야 해요. 이는 dhi.io에서 직접 풀하든, Docker Hub의 미러에서 풀하든, 자체 서드파티 레지스트리에서 풀하든 동일하게 적용돼요.

이미지 풀 시크릿 만들기

액세스 토큰 또는 Docker Desktop 자격 증명을 사용해 이미지 풀 시크릿을 만들 수 있어요.

--docker-server 값의 경우:

  • Docker Hardened Images에서 직접 풀하는 community 이미지는 dhi.io
  • Docker Hub의 미러링된 저장소는 docker.io
  • 서드파티 레지스트리는 레지스트리 호스트 이름

액세스 토큰 사용

개인 액세스 토큰(PAT) 또는 조직 액세스 토큰(OAT)을 사용해 시크릿을 만들어요. 토큰이 저장소에 대해 최소한 읽기 전용 접근 권한을 가지도록 해요.

$ kubectl create -n <kubernetes namespace> secret docker-registry <secret name> --docker-server=<registry server> \
        --docker-username=<registry user> --docker-password=<access token> \
        --docker-email=<registry email>

Docker Desktop 자격 증명 사용

이미 Docker Desktop으로 인증했다면 저장된 자격 증명으로 시크릿을 만들 수 있어요. 이 방법은 Docker Desktop으로 인증한 레지스트리(docker login <registry> 사용)에 대해 작동해요.

$ NS=<namespace>
$ kubectl create -n ${NS} secret docker-registry dhi-pull-secret \
    --docker-server=<registry server> \
    --docker-username=<registry user> \
    --docker-password="$(echo https://<registry server> | docker-credential-desktop get | jq -r .Secret)" \
    --docker-email=<registry email>

이 방법은 Docker Desktop의 자격 증명 저장소에서 자격 증명을 추출해, 로컬 개발을 위해 별도의 액세스 토큰을 만들 필요가 없어요.

이미지 풀 시크릿 테스트

시크릿을 만든 후 imagePullSecrets 구성에서 시크릿을 참조하는 테스트 포드를 배포해 작동을 확인해요.

테스트 포드 만들기:

kubectl apply --wait -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: dhi-test
  namespace: <kubernetes namespace>
spec:
  containers:
  - name: test
    image: bash:5
    command: [ "sh", "-c", "echo 'Hello from DHI in Kubernetes!'" ]
  imagePullSecrets:
  - name: <secret name>
EOF

포드를 확인해 성공적으로 완료되었는지 확인해요.

$ kubectl get -n <kubernetes namespace> pods/dhi-test

성공적인 테스트는 Completed 상태를 보여줘요.

NAME       READY   STATUS      RESTARTS     AGE
dhi-test   0/1     Completed   ...          ...

대신 ErrImagePull 상태가 보이면 시크릿 구성에 문제가 있는 거예요.

NAME       READY   STATUS         RESTARTS   AGE
dhi-test   0/1     ErrImagePull   0          ...

포드 출력이 예상 메시지와 일치하는지 확인해요.

$ kubectl logs -n <kubernetes namespace> pods/dhi-test
Hello from DHI in Kubernetes!

테스트 포드를 정리해요.

$ kubectl delete -n <kubernetes namespace> pods/dhi-test

더 알아보기 (Learn more)