Amazon ECS 태스크 및 컨테이너 보안 모범 사례 - Amazon Elastic Container Service

Amazon ECS 태스크 및 컨테이너 보안 모범 사례

컨테이너 이미지를 공격에 대한 첫 번째 방어선으로 보아야 합니다. 이미지가 안전하지 않고 잘못 구성되어 있으면 공격자가 컨테이너의 경계를 벗어나 호스트에 액세스할 수 있습니다. 이러한 상황이 발생할 위험을 줄이려면 다음과 같이 해야 합니다.

작업 및 컨텐이너를 설정할 때 다음을 수행하는 것이 좋습니다.

최소로 생성하거나 distroless 이미지 사용

먼저 컨테이너 이미지에서 불필요한 바이너리를 모두 제거합니다. Amazon ECR 퍼블릭 갤러리의 익숙하지 않은 이미지를 사용하는 경우 이미지를 검사하여 각 컨테이너 레이어의 콘텐츠를 참조하는지 확인합니다. 이를 위해 Dive와 같은 애플리케이션을 사용할 수 있습니다.

또는 애플리케이션과 해당 런타임 종속성만 포함하는 distroless 이미지를 사용할 수도 있습니다. 이런 이미지는 패키지 관리자 또는 셸을 포함하지 않습니다. Distroless 이미지는 “스캐너의 신호 대 노이즈 비율을 개선하고 프로비넌스를 필요한 만큼으로 설정하여 부담을 줄입니다”. 자세한 내용을 알아보려면 GitHub 설명서의 distroless를 참조하세요.

Docker에는 scratch라고 하는 예약된 최소 이미지에서 이미지를 생성하는 메커니즘이 있습니다. 자세한 내용은 Docker 설명서의 Creating a simple parent image using scratch를 참조하세요. Go와 같은 언어를 사용하면 정적 연결 바이너리를 생성하고 Dockerfile에서 참조할 수 있습니다. 다음 예제에서 이 작업을 수행하는 방법을 안내합니다.

############################ # STEP 1 build executable binary ############################ FROM golang:alpine AS builder # Install git. # Git is required for fetching the dependencies. RUN apk update && apk add --no-cache git WORKDIR $GOPATH/src/mypackage/myapp/ COPY . . # Fetch dependencies. # Using go get. RUN go get -d -v # Build the binary. RUN go build -o /go/bin/hello ############################ # STEP 2 build a small image ############################ FROM scratch # Copy our static executable. COPY --from=builder /go/bin/hello /go/bin/hello # Run the hello binary. ENTRYPOINT ["/go/bin/hello"] This creates a container image that consists of your application and nothing else, making it extremely secure.

앞의 예제는 다단계 빌드의 예이기도 합니다. 이러한 유형의 빌드는 컨테이너 레지스트리로 푸시되는 최종 이미지의 크기를 최소화하는 데 사용할 수 있으므로 보안 측면에서 매력적입니다. 빌드 도구 및 기타 불필요한 바이너리가 없는 컨테이너 이미지는 이미지의 공격 표면을 줄여 보안 태세를 향상합니다. 다단계 빌드에 대한 자세한 내용은 creating multi-stage builds를 참조하세요.

이미지를 스캔하여 취약성 확인

컨테이너 이미지는 해당하는 가상 머신과 마찬가지로 취약성이 있는 바이너리 및 애플리케이션 라이브러리를 포함하거나 시간에 따라 취약성이 증가할 수 있습니다. 악용을 방지하는 가장 좋은 방법은 이미지 스캐너로 이미지를 정기적으로 스캔하는 것입니다.

Amazon ECR에 저장된 이미지는 푸시할 때 스캔하거나 온디맨드로 스캔(24시간마다 한 번)할 수 있습니다. Amazon ECR 기본 스캔에서는 오픈 소스 이미지 스캔 솔루션인 Clair를 사용합니다. Amazon ECR 고급 스캔은 Amazon Inspector를 사용합니다. 이미지를 스캔한 후, 결과는 Amazon EventBridge의 Amazon ECR 이벤트 스트림에 기록됩니다. 또한 스캔 결과를 Amazon ECR 콘솔 내에서 보거나 DescribeImageScanFindings API를 호출하여 확인할 수 있습니다. HIGH 또는 CRITICAL 취약성이 있는 이미지는 삭제하거나 다시 빌드해야 합니다. 배포된 이미지에서 취약성이 발견되면 가능한 한 빨리 교체해야 합니다.

Docker Desktop Edge 버전 2.3.6.0 이상에서는 로컬 이미지를 스캔할 수 있습니다. 스캔은 애플리케이션 보안 서비스인 Snyk에서 제공합니다. 취약성이 발견되면 Snyk는 Dockerfile에서 취약성이 있는 계층 및 종속성을 식별합니다. 또한 취약성이 적은 더 슬림한 기본 이미지를 사용하거나 특정 패키지를 최신 버전으로 업그레이드하는 것과 같은 안전한 대안도 추천합니다. Docker 스캔을 사용하면 개발자가 이미지를 레지스트리에 푸시하기 전에 잠재적인 보안 문제를 해결할 수 있습니다.

이미지에서 특수 권한 제거

액세스 권한 플래그 setuidsetgid가 지정되면 실행 파일의 소유자 또는 그룹의 권한으로 실행 파일을 실행할 수 있습니다. 이러한 액세스 권한이 있는 바이너리는 권한을 에스컬레이션하는 데 사용될 수 있으므로 이미지에서 모두 제거하세요. 악의적인 목적으로 사용될 수 nccurl과 같은 셸 및 유틸리티를 모두 제거하는 것이 좋습니다. setuidsetgid 액세스 권한이 있는 파일을 찾으려면 다음 명령을 사용할 수 있습니다.

find / -perm /6000 -type f -exec ls -ld {} \;

이러한 파일에서 이러한 특수 권한을 제거하려면 컨테이너 이미지에 다음 지시문을 추가합니다.

RUN find / -xdev -perm /6000 -type f -exec chmod a-s {} \; || true

큐레이팅한 이미지 세트 생성

개발자가 직접 이미지를 생성하도록 하지 말고, 조직의 다양한 애플리케이션 스택에 대해 검증된 이미지 세트를 생성하세요. 이렇게 하면 개발자는 Dockerfile 작성 방법을 배울 필요 없이 코드 작성에 집중할 수 있습니다. 변경 사항이 코드베이스에 병합되면 CI/CD 파이프라인은 자산을 자동으로 컴파일한 다음 아티팩트 리포지토리에 저장할 수 있습니다. 그리고 마지막으로, 아티팩트를 적절한 이미지에 복사한 다음 Amazon ECR과 같은 Docker 레지스트리로 푸시할 수 있습니다. 최소한 기본 이미지 세트를 생성해야 합니다. 그래야만 개발자가 이를 바탕으로 자체 Dockerfile을 생성할 수 있습니다. 이미지를 Docker Hub에서 가져오지 마세요. 이미지에 무엇이 들어 있는지 항상 알 수 있는 것은 아니며, 상위 1000개 이미지 중 약 5분의 1에 취약성이 있습니다. 이러한 이미지의 목록과 취약성은 https://vulnerablecontainers.org/에서 확인할 수 있습니다.

애플리케이션 패키지 및 라이브러리에서 취약성 스캔

이제는 오픈 소스 라이브러리를 일반적으로 사용합니다. 운영 체제 및 OS 패키지와 마찬가지로 이러한 라이브러리에는 취약성이 있을 수 있습니다. 개발 수명 주기의 일부로 이러한 라이브러리를 스캔하고 중요한 취약성이 발견되면 업데이트해야 합니다.

Docker Desktop은 Snyk를 사용하여 로컬 스캔을 수행합니다. Snyk는 오픈 소스 라이브러리의 취약성과 잠재적 라이선스 문제를 찾는 데에도 사용할 수 있습니다. 개발자 워크플로에 직접 통합하여 오픈 소스 라이브러리로 인한 위험을 완화할 수 있습니다. 자세한 정보는 다음 주제를 참조하세요.

정적 코드 분석 수행

컨테이너 이미지를 빌드하기 전에 정적 코드 분석을 수행해야 합니다. 이 분석은 소스 코드를 대상으로 수행되며 코딩 오류뿐 아니라 오류 주입과 같이 악의적인 행위자가 악용할 수 있는 코드를 식별하는 데 사용됩니다. SonarQube는 정적 애플리케이션 보안 테스트(SAST)를 위해 널리 사용되는 옵션으로, 다양한 프로그래밍 언어를 지원합니다.

루트가 아닌 사용자로 컨테이너 실행

루트가 아닌 사용자로 컨테이너를 실행해야 합니다. 기본적으로 컨테이너는 Dockerfile에 USER 지시문이 포함되어 있지 않으면 root 사용자로 실행됩니다. Docker에서 할당하는 기본 Linux 기능에 따라 root로 실행할 수 있는 작업이 제한되지만, 그 효과는 미미합니다. 예를 들어 root로 실행되는 컨테이너는 여전히 디바이스에 액세스할 수 없습니다.

CI/CD 파이프라인의 일부로 Dockerfiles를 lint하여 USER 지시문을 찾고 이 지시문이 없으면 빌드할 수 없게 해야 합니다. 자세한 정보는 다음 주제를 참조하세요.

  • Dockerfile-lint는 파일이 모범 사례를 따르는지 확인하는 데 사용할 수 있는 RedHat의 오픈 소스 도구입니다.

  • Hadolint는 모범 사례를 준수하는 Docker 이미지를 빌드하기 위한 또 다른 도구입니다.

읽기 전용 루트 파일 시스템 사용

읽기 전용 루트 파일 시스템을 사용해야 합니다. 컨테이너의 루트 파일 시스템은 기본적으로 쓰기 가능합니다. RO(읽기 전용) 루트 파일 시스템으로 컨테이너를 구성하면 데이터를 유지할 수 있는 위치를 명시적으로 정의해야 합니다. 이 경우 특별히 권한을 부여받지 않는 한 컨테이너의 파일 시스템에 쓸 수 없으므로 공격 표면이 줄어듭니다.

참고

읽기 전용 루트 파일 시스템을 사용하면 파일 시스템에 쓸 수 있어야 하는 특정 OS 패키지에서 문제가 발생할 수 있습니다. 읽기 전용 루트 파일 시스템을 사용할 계획이라면 사전에 철저히 테스트하세요.

CPU 및 메모리 제한과 함께 작업 구성(Amazon EC2)

CPU 및 메모리 제한과 함께 작업을 구성하여 다음과 같은 위험을 최소화해야 합니다. 작업의 리소스 제한은 작업 내의 모든 컨테이너에서 예약할 수 있는 CPU 및 메모리 양의 상한을 설정합니다. 제한을 설정하지 않으면 작업에서 호스트의 CPU와 메모리에 액세스할 수 있습니다. 이로 인해 공유 호스트에 배포된 작업이 다른 작업의 시스템 리소스를 부족하게 만드는 문제가 발생할 수 있습니다.

참고

AWS Fargate 기반 Amazon ECS 작업에서는 CPU 및 메모리 제한 값이 결제 목적으로 사용되므로 이러한 값을 지정해야 합니다. Amazon ECS Fargate에서는 각 작업이 자체 전용 인스턴스에서 실행되므로 하나의 작업이 시스템 리소스를 모두 독차지하더라도 문제가 되지 않습니다. 메모리 제한을 지정하지 않는 경우 Amazon ECS는 각 컨테이너에 최소 4MB를 할당합니다. 마찬가지로 작업의 CPU 제한을 설정하지 않으면 Amazon ECS 컨테이너 에이전트는 최소 2개 CPU를 할당합니다.

Amazon ECR에서 변경 불가능한 태그 사용

Amazon ECR에서는 변경 불가능한 태그를 사용하여 이미지를 구성할 수 있으며, 구성해야 합니다. 이렇게 하면 이미지의 변경 또는 업데이트된 버전을 동일한 태그를 사용하여 이미지 리포지토리로 푸시하지 못하게 됩니다. 따라서 공격자가 손상된 버전의 이미지를 동일한 태그가 지정된 이미지에 푸시하는 것을 방지할 수 있습니다. 변경 불가능한 태그를 사용하면 결과적으로 변경할 때마다 다른 태그의 새 이미지를 푸시할 수밖에 없게 됩니다.

컨테이너를 privileged로 실행하지 않음(Amazon EC2)

컨테이너를 privileged로 실행하지 않아야 합니다. 백그라운드의 경우 privileged로 실행되는 컨테이너는 호스트에서 확장된 권한으로 실행됩니다. 즉, 이 컨테이너는 호스트에서 root에 할당된 Linux 기능을 모두 상속합니다. 이 파라미터 사용을 엄격하게 제한하거나 금지해야 합니다. Amazon ECS 컨테이너 에이전트 환경 변수 ECS_DISABLE_PRIVILEGEDtrue로 설정하여 privileged가 필요하지 않은 경우 특정 호스트에서 컨테이너가 privileged로 실행되지 않게 하는 것이 좋습니다. 또는 AWS Lambda를 사용하여 작업 정의를 스캔하고 privileged 파라미터를 사용하는지 확인할 수 있습니다.

참고

AWS Fargate 기반 Amazon ECS에서는 컨테이너를 privileged로 실행할 수 없습니다.

컨테이너에서 불필요한 Linux 기능 제거

다음은 Docker 컨테이너에 할당되는 기본 Linux 기능의 목록입니다. 각 기능에 대한 자세한 내용은 Overview of Linux Capabilities를 참조하세요.

CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_FOWNER, CAP_FSETID, CAP_KILL, CAP_SETGID, CAP_SETUID, CAP_SETPCAP, CAP_NET_BIND_SERVICE, CAP_NET_RAW, CAP_SYS_CHROOT, CAP_MKNOD, CAP_AUDIT_WRITE, CAP_SETFCAP

컨테이너에서 위에 나열된 Docker 커널 기능 중 필요하지 않은 것이 있으면 컨테이너에서 삭제하는 것이 좋습니다. 각 Docker 커널 기능에 대한 자세한 내용은 KernalCapabilities를 참조하세요. 다음을 수행하여 사용 중인 기능을 확인할 수 있습니다.

  • OS 패키지 libcap-ng를 설치하고 pscap 유틸리티를 실행하여 각 프로세스에서 사용 중인 기능을 나열합니다.

  • 또한 capsh를 사용하여 프로세스에서 사용 중인 기능의 암호를 해제할 수 있습니다.

고객 관리형 키(CMK)를 사용하여 Amazon ECR로 푸시된 이미지 암호화

고객 관리형 키(CMK)를 사용하여 Amazon ECR로 푸시된 이미지를 암호화해야 합니다. Amazon ECR로 푸시된 이미지는 저장 시 AWS Key Management Service(AWS KMS) 관리형 키를 사용하여 자동으로 암호화됩니다. 대신 자체 키를 사용하고 싶은 경우 이제 Amazon ECR에서는 고객 관리형 키(CMK)를 사용한 AWS KMS 암호화를 지원합니다. CMK를 사용하여 서버 측 암호화를 활성화하기 전에 설명서의 저장된 데이터 암호화에 열거된 고려 사항을 검토하세요.