queue

컨테이너 보안

 "한빛미디어 <나는 리뷰어다> 활동을 위해서 책을 제공받아 작성된 서평입니다."

가벼움과 재사용 가능한 코드, 그리고 무엇보다 2013년 도커의 등장 이래로 컨테이너에 대한 관심이 증가하고 있습니다. 컨테이너는 격리된 공간에서 프로세스가 동작하는 기술로, 도커의 초기 광고 문구인 Build Once Run Anywhere 처럼, 한 번만 빌드하고 어디서든 실행할 수 있게 해줍니다. 컨테이너를 이용하면 간단한 한 두개의 명령어를 통해 다종다양한 실행 환경에서 우려되는 각종 오류와 지원 문제를 해결해준다는 점에서도 크나큰 매력이 있습니다.

queue

이 책은 컨테이너 기반 시스템을 구성하는데 쓰이는 여러 기반 기술들과 매커니즘을 소개하고, 리눅스 운영체제에서 구체적으로 어떻게 작용하는지 설명하고 있습니다. 전반부는 시스템 호출과 리눅스 능력 등 컨테이너와 관련된 리눅스 메커니즘을 설명하면서, 컨테이너가 어떻게 작동하고 컨테이너의 구성 요소들이 어떻게 서로 혹은 외부와 통신하는지 다루고 있습니다. 또한 컨테이너 이미지를 안전하게 구축하는 best practice를 사례와 함께 설명하고, 기본 구현 이상으로 컨테이너 보안을 강화하는데 사용할 수 있는 추가적인 리눅스 보안 수단들을 설명해주고 있습니다.

이어지는 후반부에서는, 컨테이너간의 통신 방식과 연결에 제한을 두어 보안을 개선하는 방법을 살펴보고 있습니다. 컨테이너화된 구성 요소들이 서로를 식별하고 보안 네트워크 연결을 설정하는데 사용되는 개인 키/공개 키 방식과 인증서를 사례로 알아보고, 마지막으로 컨테이너의 기능을 활용해 공격을 방지하는 여러 보안 도구들을 소개하고 있습니다.

일반적인 보안 원칙들

  • 최소 권한

최소 권한 원칙 또는 최소 특권 원칙은 사용자 또는 구성 요소가 해당 작업을 진행하는데 반드시 필요한 것에만 접근할 수 있게 해야 한다는 것을 의미합니다. 예를 들어, 특정 컨테이너에게 루트 사용자 권한으로 실행되도록 설정한다면 실제로 필요한 것보다 더 많은 특권을 가지게 됩니다. 전자 상거래 프로그램 내에 상품을 검색하는 마이크로 서비스가 있다고 할 때, 최소 권한 원칙에 따르면 제품과 관련된 데이터베이스에 읽기 전용으로만 접근할 수 있는 권한만 부여되어야 합니다. 이 때엔 사용자 정보 혹은 결제 정보 등 이와 직접적인 관련이 없는 것에 대한 권한은 필요하지 않습니다.

  • 심층 방어

심층 방어 원칙이란 보호를 여러 층으로 적용해야 함을 의미합니다. 공격자가 방어층 하나를 뚫는다고 해도 또 다른 방어층이 있으면 그 목적을 쉽게 달성하지 못하도록 방어할 수 있으며, 이중 로그인을 예로 들 수 있습니다.

  • 공격 표면 축소

일반적으로 시스템이 복잡할수록 이를 공격하는 방법이 다양해지기 때문에, 시스템의 복잡도를 줄여 보안력을 높이는 것을 의미합니다. 서버에 접근 가능한 사용자들과 구성 요소를 제한하거나, 인터페이스를 최대한 작고 단순하게 구성하는 등의 방법이 있습니다.

  • 폭발 반경 제한

보안 통제 항복들을 더 작은 부분으로 나누어 혹시 모를 사건 발생에 대한 상호영향도를 최소화 하는 것을 의미합니다.

  • 직무 분리 (Segregation of duties, SoD)

서로 다른 구성 요소나 사용자들에게 전체 시스템 중 꼭 필요한 가장 작은 부분집합에 대한 권한만을 부여하는 것을 의미합니다. 앞에서 설명한 최소 권한 및 폭발 반경 제한 원칙과 관련이 있습니다.

cgroups로 제한하기

리눅스의 모든 프로세스는 일반적으로 하나의 부모 프로세스의 하위 프로세스입니다 (부모-자식 관계 라고도 이해할 수 있습니다).

따라서 한 프로세스가 처음 생성될 때 (init), 생성된 프로세스는 다음의 특징을 가지게 됩니다.

  • 계층적이다
  • 자식 cgroups은 부모 cgroups의 속성을 상속한다

일반적으로 컨테이너는 리눅스 프로세스로 실행되므로, cgroups를 통해 각 칸테이너가 사용할 수 있는 자원들을 제한할 수 있습니다. cgroupscontrol groups를 줄인 말로, 주어진 그룹에 속한 프로세스들이 사용할 수 있는 자원(메모리나 CPU, 네트워크 입출력)을 제한하는 수단입니다.

제어 그룹을 관리한다는 것은, 결국 이 위계구조들에 있는 파일들과 디렉터리를 읽고 쓰는 것을 의미합니다.

namespace로 제한하기

cgroups가 프로세스가 사용할 수 있는 자원을 제한한다면, namespace는 프로세스가 볼 수 있는 것들을 제한합니다.

리눅스에서 지원하는 namespace의 종류는 다음과 같습니다.

  • 유닉스 시분할 시스템 (Unix Timesharing System, UTS) : 프로세스가 인식하는 시스템의 호스트 이름과 도메인 이름들에 관한 namespace
  • 프로세스 ID
  • 마운트 지점 (Mount point)
  • 네트워크
  • 사용자 ID와 그룹 ID
  • IPC (Inter-process communication, 프로세스간 통신)
  • 제어 그룹

하나의 프로세스는 namespace 종류 당 하나의 namespace에 속하게 됩니다. 프로세스를 어떤 namespace에 넣으면, 프로세스는 해당 namespace이 허용하는 것들만 볼 수 있게 됩니다.

lsns: 현존하는 namespace를 확인하기 위해 사용하는 명령어

도커 컨테이너 안에서 ps 명령어를 실행하면 현재 컨테이너 안에서 실행되는 프로세스만 나오고 호스트에서 실행되는 프로세스들은 나오지 않습니다. 이처럼 컨테이너가 볼 수 있는 프로세스 ID들이 제한된 것은 컨테이너마다 개별적인 프로세스 ID namespace를 적용했기 때문입니다.

또한 컨테이너 실행 시 루트 디렉터리가 변경되어, 호스트의 파일 시스템 전체를 볼 수 없습니다. 만약 루트 디렉터리를 변경하려면 chroot 명령어를 통해 변경할 수 있습니다.

> mkdir new_root
> sudo chroot new_root

이 외에도 각각의 namespace별로 제한하는 방법을 사례와 함께 제시해주고 있어, 실무에서 도커를 활용하여 직접 컨테이너 환경을 구축하고 이를 감싸는 보안 경계를 강화하고자 할 때 참고할 수 있습니다.

컨테이너 이미지와 보안

이미지는 컨테이너 실행에 필요한 파일과 설정 값 등을 포함하고 있는 것으로, 상태값을 가지지 않고 변하지 않습니다. (Immutable) 하나의 컨테이너 이미지는 크게 두 부분으로 구성됩니다. 하나는 루트 파일 시스템이고, 다른 하나는 이미지 설정 정보입니다. 아래는 실제 컨테이너 이미지의 config.json 파일로, 여기에 컨테이너 프로세스의 접근을 제한할 자원들과 프로세스별마다 적용되는 namespace를 비롯한 컨테이너와 관련된 설정을 포함하고 있습니다.

    ...
    "linux": {
        "resource": {
            "memory": {
                "limit": 1000000
            },
            "devices": [
                {
                    "allow": false,
                    "access": "rwm"
                }
            ],
            "namespaces": [
                {
                    "type": "pid"
                }
                {
                    "type": "network"
                }
                {
                    "type": "ipc"
                }
                {
                    "type": "uts"
                }
                {
                    "type": "mount"
                }
            ]
        }  
    }

컨테이너 이미지에 접근할 수 있는 사용자는 해당 이미지 안에 있는 어떤 파일에도 접근할 수 있게 됩니다. 때문에, 이미지에 패스워드나 인증 토큰과 같은 민감한 정보를 포함하는 것은 보안 취약점에 해당되므로 지양해야 합니다. 또한, 꼭 필요하지 않은 패키지나 라이브러리, 실행 파일은 이미지에 추가하지 않도록 주의해야 합니다.

추가적으로 이미지를 보관하는 레지스트리와 관련된 내용과, 이미지 서명, 배치(deployment) 시점의 보안 점검 주의 사항들을 책을 통해 확인할 수 있습니다.

컨테이너 방화벽

모든 외부 공격은 네트워크를 통해 도달되므로, 네트워크 보안을 위해 컨테이너 방화벽을 설정할 수 있습니다. 컨테이너 방화벽은 컨테이너 간의 트래픽을 제한하는데, 이에 따라 승인된 객체 사이에서만 정보가 오갈 수 있게 됩니다. 또한, 규칙에서 벗어난 네트워크 연결 시도를 기록하고 보고하는 기능도 제공하는데 이러한 기록을 통해 혹시 모를 공격의 전조를 감지할 수 있기도 합니다.


이 책은 아래 범주에 속하는 독자를 대상으로 하고 있습니다.

  • psgrep, cat, chown, chmod 등의 기본적인 리눅스 명령어에 익숙한 사람
  • dockerkubectl 등의 도구를 통해 컨테이너 응용 프로그램을 실행하고 관리해본 경험이 있는 사람

다만 특정 컨테이너에 종속되어 설명된다기보단, 여러 컨테이너 구현들이 공통의 특징들을 많이 공유한다는 점을 강조하고자 다양한 컨테이너 도구들을 사용하며 설명해주고 있습니다. 또한 그 근간이 되는 기술에 대한 설명과 다양한 예시를 함께 제공해주어, 초보자라도 차근차근 따라갈 수 있는 난이도의 책이라 생각됩니다. 책의 마지막 부분인 부록의 보안 점검 목록 항목들을 통해, 먼저 운영 중인 컨테이너의 보안을 점검하기 위한 현황표를 작성하고 역으로 필요한 부분 순으로 읽는 것도 이 책을 활용하는 좋은 방법일 것 같습니다.

개인적으로는 전자의 경우는 해당되나, 도커, 쿠버네티스 등의 컨테이너 관련 실무 경험이 부족하여 읽는 동안 다소 모호하게 이해되는 내용이 많았습니다. 아직까진 UbuntuCentOS 혹은 Windows 서버 관리 경험만 있고, 도커와 쿠버네티스 등의 컨테이너 도구를 활용한 경험이 없어 추후 실무에 활용하게 된다면 다시 참고할만한 책이라고 생각합니다.


🔗 참조

📌 Docker security

📌 한빛출판사: Container Security