소개

LXC 컨테이너는 두가지로 나눌 수 있습니다.

전자는 이전 형식의 컨테이너라고 할 수 있습니다. 안전하지 않으므로, 비특권 컨테이너가 가능하지 않는 환경이나 컨테이너의 사용자가 root에 접근해도 상관없을 때 사용하여야 합니다.

후자는 LXC 1.0 (2014년 2월)에서 도입되었으며, 최신 커널 (3.13보다 높은)이 요구됩니다. 이 컨테이너의 장점은 root 권한을 이용한 공격에 대해 안전하므로, 커널의 보안 문제가 없는 한 컨테이너는 안전하다는 것입니다.

특권 컨테이너는 안전하지 않은 것으로 가정하기 때문에, 일반적으로 새로운 컨테이너 탈출 취약점을 CVE와 당장의 빠른 수정을 가할 만큼으로 취급하진 않을 것입니다. 하지만 해당 문제들을 완화시켜 예기치 않은 손상으로부터 호스트를 보호할 것입니다.

특권 컨테이너

특권 컨테이너는 컨테이너의 uid 0가 호스트의 uid 0로 매핑되어 있는 컨테이너로 정의할 수 있습니다. 이러한 컨테이너에서는 호스트 보호와 탈출 방지가 강제적 접근 제어 (apparmor, selinux), seccomp 필터, 캐퍼빌리티 제거, 네임스페이스를 통해 수행됩니다.

위의 조합된 기술은 일반적으로 예기치 못한 손상으로부터 호스트를 보호하는 역할을 합니다. 여기서 말하는 손상은 호스트의 하드웨어/커널을 재설정하거나 호스트의 파일시스템을 접근하는 것을 말합니다.

LXC 개발진은 이러한 컨테이너는 root 권한을 이용한 공격에 안전하지 않으며, 안전하게 만들 수도 없다고 생각하고 있습니다.

하지만 신뢰할 수 있는 작업만 수행되거나 root로는 신뢰할 수 없는 작업이 실행되지 않는 환경에서는 지금도 유용합니다.

우리는 이러한 컨테이너들을 탈출하고 호스트의 모든 root 권한을 확득하는 등의 여러 취약점들을 알고 있습니다. 이러한 취약점들은 간단하게 차단될 수 있으므로, 취약점을 알아냈을 때 지금과는 다른 정책들로 업데이트합니다. 하지만 핵심 기능을 너무 많이 막게 되어 대부분의 컨테이너들이 사용할 수 없게 된다면, 해당 취약점을 차단하지 않습니다.

비특권 컨테이너

비특권 컨테이너는 안전하게 설계되었습니다. 컨테이너의 uid 0가 컨테이너 밖의 비특권 사용자로 매핑되어 있습니다. 그리고 자기 자신의 자원만 접근할 수 있는 권한만 존재합니다.

이러한 컨테이너에서는 SELinux, AppArmor, Seccomp, 캐퍼빌리티의 사용이 필수적이지 않습니다. LXC는 커널 보안 문제를 다룰 수 있는 하나의 추가 보안 레이어를 사용하고 있습니다. 하지만 해당 레이어는 별도의 보안 모델을 강요하지 않습니다.

비특권 컨테이너가 동작하기 위해서, LXC는 3개의 setuid 코드들과 상호작용하고 있습니다.

그외 모든 것들은 당신 소유의 사용자 또는 uid로 동작하게 됩니다.

그 결과, 이러한 컨테이너가 가지는 대부분의 보안 문제들 (컨테이너 탈출, 자원 오사용, ...)은 임의의 비특권 사용자를 대상으로 했을 때도 동일하게 적용됩니다. 따라서 보안 문제가 발생한다면, 이는 LXC의 문제라기 보다는 일반적인 커널의 문제가 될 것입니다.

LXC 개발진은 기꺼이 이러한 보안 문제를 추적하는 것을 도와줄 것입니다. 그리고 가능한한 빨리 해결하기 위해 리눅스 커널 커뮤니티에 연락을 취할 것입니다.

잠재적 DoS 공격

LXC는 기본적으로 DoS 공격을 막지는 않습니다. 여러개의 신뢰할 수 없는 컨테이너들을 실핼할 때, 또는 신뢰할 수 없는 사용자들에게 컨테이너들을 실행할 수 있게 허용할 때, 아래 사항들을 염두에 두고 설정 사항들을 업데이트해 나가야 합니다.

Cgroup 제한

LXC는 자신의 부모로부터 cgroup 제한들을 상속받습니다. Linux 배포판이라면 제한이 설정되어 있지 않을 것입니다. 그 결과, 컨테이너 내의 사용자는 fork 폭탄 방법으로 꽤 쉽게 host에게 DoS 공격을 가할 수 있습니다. 또한 모든 시스템의 메모리를 사용하거나 커널이 메모리를 모두 소진할 떄까지 네트워크 인터페이스를 생성할 수 있습니다.

이러한 문제들은 적절한 lxc.cgroup 항목(memory, cpu, pids) 설정을 통해 어느정도 해결할 수 있습니다. 또는 부모 사용자가 로그인 시간에 적절히 설정된 cgroup에 위치할 수 있도록 하여, 해결할 수도 있습니다.

사용자 제한 (ulimit)

cgroup 처럼, 부모의 제한은 비특권 컨테이너로 상속되기 때문에, 부모보다 높은 ulimit 값은 설정할 수 없습니다.

다만 염두에 두어야할 부분이 있는데, 네임스페이스가 제공하는 ulimit은 커널 관점의 uid로 묶여있다는 점입니다. 즉, 전역적인 커널 uid이며, 사용자 네임스페이스 안에서의 uid가 아닙니다.

만약 서로 같은 커널 uid를 사용하는 id 매핑을 가지고 있는 두개의 컨테이너가 있다면, 그들은 서로 제한을 공유하게 되는 것입니다. 이는 어떤 컨테이너 내의 사용자가 다른 컨테이너의 동일한 사용자에게 DoS 공격을 가할 수 있음을 의미합니다.

이를 막기 위해서, 신뢰할 수 없는 사용자나 컨테이너는 id 매핑을 완전히 분리할 필요가 있습니다. (이상적으로는 65536개의 uid와 gid 각각에 대하여)

네트워크 브리지 공유

LXC는 컨테이너를 위해 L2 연결을 설정합니다. 또한 편의를 위해 시스템에 1개의 기본 브리지를 제공합니다.

브리지에 연결된 컨테이너는 그들이 원하는 어떠한 L2 트래픽이라도 전송가능합니다. 따라서 브리지에 MAC/IP 스푸핑 공격을 하는 것이 가능합니다. 신뢰할 수 없는 컨테이너들을 실핼할 때, 또는 신뢰할 수 없는 사용자들에게 컨테이너들을 실행할 수 있게 허용할 때, 이상적으로는 사용자 하나당 또는 컨테이너 그룹 하나당 하나의 브리지를 사용해야 합니다. 그리고 /etc/lxc/lxc-usernet에 그들이 할당한 브리지만 사용할 수 있도록 설정하여야 합니다.

보안 문제 보고하기

보안 문제를 가능한 빨리 모든 리눅스 배포판에서 고치길 원한다면, 아래 방법을 통해 해당 문제를 보내주시면 됩니다.

우리는 보안 문제를 확인한 후, 모든 지원 중인 버전에 대해 수정을 준비할 것입니다. 그리고 테스트를 위해 당신에게 패치를 제공하고 CVE 번호를 할당할 것입니다. 뿐만 아니라 당신과 리눅스 배포판 커뮤니티에 다음 버전 공개 일정을 알릴 것입니다.