서비스 전용 계정으로 실행하는 이유와 방법
root 실행은 버그 하나로 서버 전체 탈취로 이어진다. useradd -r -M -s /sbin/nologin으로 서비스별 계정을 만들고 systemd User=로 피해를 제한하는 구체적 설정을 확인하세요.
왜 모든 것을 root로 실행하면 안 되는가
root는 시스템에서 모든 것을 할 수 있다. 모든 파일을 읽고, 어떤 설정이든 변경하며, 시스템 전체를 삭제할 수 있다. 서비스를 root로 실행하면 그 모든 권한을 해당 서비스에 넘기는 셈이다. 서비스에 공격자가 악용할 수 있는 버그가 있다면, 공격자는 단순히 서비스만 장악하는 것이 아니라 root를 얻고, root는 곧 서버 전체를 의미한다. 권한이 없는 사용자로 실행하면 피해를 억제할 수 있다. 제한된 계정으로 실행되는 서비스의 버그는 공격자에게 그 계정이 접근할 수 있는 것만 허용하며, 이는 거의 아무것도 아니어야 한다.
이것이 최소 권한의 원칙이다. 시스템의 각 부분에 작업 수행에 꼭 필요한 접근 권한만 부여하고 그 이상은 주지 않는 것이다. 이는 침해 시 피해 범위를 제한하는 가장 효과적인 습관이며, 현대 서버에서는 적용하는 데 드는 비용이 거의 없다.
서비스별 전용 계정
전통적인 접근 방식은 각 서비스마다 별도의 시스템 사용자를 생성하는 것이다. 이 사용자는 해당 서비스의 파일만 소유하고 로그인할 수 없다. 웹 앱을 위한 시스템 계정은 다음과 같을 수 있다.
sudo useradd --system --no-create-home --shell /usr/sbin/nologin appsvc각 플래그가 중요하다. --system는 이 계정을 사람이 로그인하는 계정이 아닌 서비스 계정으로 만든다. --no-create-home는 필요 없는 홈 디렉터리 생성을 건너뛴다. --shell /usr/sbin/nologin은 공격자가 어떻게든 계정을 탈취하더라도 셸을 열 수 없음을 의미한다. 이 계정은 오직 프로세스와 그 파일을 소유하기 위해 존재한다.
그런 다음 해당 사용자에게 필요한 파일만 부여하고 그 이상은 주지 않는다.
sudo chown -R appsvc:appsvc /opt/myapp이제 서비스는 자신의 디렉터리를 읽고 쓰며, 디스크의 다른 어느 곳에도 관여하지 않는다. 만약 악용되더라도 공격자가 변경할 수 있는 파일은 /opt/myapp로 제한된다. 이 계정은 누구나 읽을 수 있는 파일은 여전히 읽을 수 있지만, 시스템의 나머지 부분은 수정할 수 없다.
systemd가 해당 사용자로 실행하게 하기
계정이 생성되면 systemd에 해당 사용자로 서비스를 실행하라고 지시한다. 유닛 파일에서 한 줄이면 충분하다.
[Service]
ExecStart=/opt/myapp/bin/server
User=appsvc
Group=appsvcUser=appsvc는 프로세스가 root의 권한 대신 해당 계정의 제한된 권한으로 시작됨을 의미한다. 이는 systemd에서 애플리케이션을 실행하는 일반적이고 잘 검증된 방법이며, 유닛을 작성하는 모든 서비스에 적용할 가치가 있다.
또는 DynamicUser로 계정을 완전히 건너뛰기
systemd는 한 걸음 더 나아가 임시 사용자를 생성할 수 있다. 이 사용자는 서비스가 실행되는 동안에만 존재한다. DynamicUser=yes을 설정하면 계정을 전혀 관리하지 않아도 된다.
[Service]
ExecStart=/opt/myapp/bin/server
DynamicUser=yes
StateDirectory=myapp시작 시 systemd는 사용되지 않는 사용자 ID를 할당하고, 중지 시 이를 해제한다. 또한 서비스는 사설 /tmp, 대부분의 파일 시스템에 대한 읽기 전용 뷰, 그리고 StateDirectory=가 설정하여 제공하는 /var/lib/myapp 아래의 쓰기 가능한 상태 디렉터리를 얻는다. 자체 상태 디렉터리만 필요한 독립형 서비스의 경우, DynamicUser=yes는 강력한 격리를 얻는 가장 적은 노력의 방법이다. 공격자가 표적으로 삼을 장기 계정이 전혀 없기 때문이다.
유닛을 직접 작성하는 것은 까다롭고, 강화 지시문을 올바르게 설정하는 것이 가치의 대부분이다. systemd 서비스 및 타이머 가이드의 생성기가 이러한 옵션을 채워 주므로 유닛이 처음부터 올바르게 작성된다.
이것이 나머지와 어떻게 맞물리는가
최소 권한은 하나의 계층이며, 다른 계층을 대체하기보다 함께 작동한다. 기본 거부 방화벽은 서비스에 도달할 수 있는 대상을 제어하고, 권한 없는 사용자로 실행하면 서비스가 침해되었을 때 할 수 있는 일을 제어하며, 강화된 SSH는 애초에 공격자가 서버에 접근하지 못하게 막는다. 이 중 어느 하나만으로는 충분하지 않으며, 함께 사용하면 한 서비스의 버그가 서버 전체의 침해로 이어지지 않는다.
다음으로 넘어가기 전에, 서버 전체에 대한 강화 체크리스트를 실행하고 작업할 개인화된 사본을 생성한다.
FAQ
왜 서비스를 root로 실행하면 안 되는가?
root는 시스템에서 모든 것을 할 수 있으므로, root로 실행되는 서비스가 악용되면 공격자에게 서비스뿐만 아니라 서버 전체를 넘겨주게 된다. 서비스를 제한된 권한 없는 계정으로 실행하면 피해를 해당 계정이 접근할 수 있는 범위로 억제한다. root는 관리를 위해 예약하고, 오래 실행되는 모든 서비스는 제한된 사용자로 실행한다.
로그인할 수 없는 사용자를 어떻게 생성하는가?
sudo useradd --system --no-create-home --shell /usr/sbin/nologin NAME를 실행한다. nologin 셸은 자격 증명이 도난당하더라도 계정이 대화형 세션을 열 수 없음을 의미하며, --system은 이를 서비스 계정으로 표시하고, --no-create-home은 필요 없는 홈 디렉터리 생성을 건너뛴다. chown로 자신의 파일에만 소유권을 부여한다.
systemd DynamicUser란 무엇인가?
DynamicUser=yes은 systemd에 서비스가 실행되는 동안에만 존재하는 임시 사용자를 생성하도록 지시하므로, 장기 계정을 전혀 관리하지 않는다. 또한 서비스에 사설 /tmp, 대부분 읽기 전용인 파일 시스템 뷰, 그리고 관리되는 상태 디렉터리를 제공한다. 이는 일회성의 낮은 권한 ID로 독립형 서비스를 실행하는 가장 적은 노력의 방법이다.
비-root 사용자로 실행하면 방화벽을 대체하는가?
아니다. 이들은 서로 다른 것을 보호한다. 권한 없는 사용자로 실행하면 서비스가 침해되었을 때 할 수 있는 일을 제한하는 반면, 방화벽은 서비스에 도달할 수 있는 대상 자체를 제한한다. 강화된 SSH와 함께 둘 다 사용하여, 각 계층이 다른 계층이 커버하지 못하는 부분을 담당하게 한다.
서비스 사용자가 어떤 파일을 소유해야 하는가?
서비스가 실제로 필요로 하는 파일만 소유하고 그 이상은 소유하지 않는다. 계정에 자신의 작업 디렉터리와 데이터에 대한 소유권을 부여하고, 나머지는 모두 root가 소유하도록 둔다. 좋은 패턴은 애플리케이션 디렉터리에 sudo chown -R svc-app:svc-app /opt/svc-app를 사용하는 반면, /etc 아래의 구성 파일은 root 소유로 유지하고 서비스가 읽기만 가능하게 하는 것이다. 목표는 프로세스가 침해되더라도 변경할 수 있는 파일이 시스템의 나머지 부분이 아닌 자신의 데이터로 제한되도록 하는 것이다.