TT Lab
블로그 시작하기
배우기 러닝패스 코스

서버를 처음부터 잠근다

코드가 뚫려도 — 여러 겹의 방어

TT Lab 에서 이어서 보기

한 줄 요약

AppArmor 는 프로세스가 접근할 수 있는 경로와 쓸 수 있는 기능을 프로필로 못박습니다. 애플리케이션 코드가 뚫려도, 그 프로세스가 할 수 있는 일 자체가 좁으면 피해가 거기서 멈춥니다.

왜 이게 필요했나

입력 검증은 애플리케이션의 몫이지만, 그 코드에 버그가 없다고 장담할 수 없습니다. 경로 우회 하나, 역직렬화 버그 하나면 공격자가 그 프로세스의 권한으로 파일을 읽거나 명령을 실행합니다. 방어를 한 겹만 두면 그 한 겹이 뚫리는 날 전부가 뚫립니다. AppArmor 는 "이 서비스는 /srv/files 밖을 읽을 일이 없다" 는 사실을 커널에 등록해, 코드가 무엇을 하려 하든 그 밖은 막습니다.

어떻게 동작하나

AppArmor 프로필은 경로 기반 접근 제어입니다. 프로필에 적힌 것만 허용되고, 나머지는 enforce 모드에서 자동으로 거부됩니다.

profile labhub-filesvc flags=(attach_disconnected) {
  include <abstractions/base>
  include <abstractions/python>
  network inet stream,
  /usr/bin/python3* ix,
  /srv/files/** r,
}

두 가지를 반드시 구별해야 합니다.

첫째, complain 과 enforce입니다. complain 모드는 위반을 로그에 남기기만 하고 막지 않습니다. 프로필을 올렸다고 안심하지만 실제로는 아무것도 막지 않는 상태가 되기 쉽습니다. 막으려면 enforce 여야 합니다. aa-status 로 어느 모드인지 확인합니다.

둘째, 프로필을 올리는 것과 서비스에 붙이는 것이 다른 일입니다. apparmor_parser 로 커널에 프로필을 올려도, systemd 가 그 서비스를 그 프로필로 띄우지 않으면 프로세스는 여전히 unconfined 입니다. systemd 유닛에 AppArmorProfile= 을 넣고 서비스를 다시 띄워야 적용됩니다. 돌고 있는 프로세스가 실제로 가둬졌는지는 /proc/<pid>/attr/current 가 <프로필> (enforce) 인지로 확인합니다.

거부된 접근은 apparmor="DENIED" 로 커널 로그에 남습니다. 이 로그는 프로필을 좁히다 정상 동작을 막았을 때 무엇을 더 허용해야 하는지 알려 주기도 합니다 — 너무 좁혀 서비스가 자기 파일도 못 읽으면, 그 로그를 보고 필요한 경로를 더합니다.

현장에서 만나는 모습

흔한 실수 셋입니다. complain 으로 올려 놓고 막힌다고 믿는 것, 프로필만 올리고 AppArmorProfile= 로 붙이지 않는 것, 그리고 너무 좁혀 서비스가 뜨지 않는 것입니다. 마지막은 "방어가 업무를 막은" 경우라, 운영이 그 프로필을 통째로 꺼 버리는 결과로 이어집니다. 그래서 가둔 뒤에는 반드시 정상 동작이 유지되는지 함께 확인합니다.

English corner

다음 실습에서 할 것

경로 우회 취약점이 있는 파일 서비스를 AppArmor 프로필로 가둡니다. 우회가 막히고, 정상 파일은 여전히 열리고, 위반이 로그에 남는지를 확인합니다.

한 걸음 더 — SELinux 와의 선택, 그리고 프로필 만들기

강제 접근 제어에는 AppArmor 말고 SELinux 도 있습니다. SELinux 는 레이블 기반이라 더 세밀하지만 배우기 가파르고, AppArmor 는 경로 기반이라 읽고 쓰기 쉽습니다. 우분투·데비안은 AppArmor 를, RHEL 계열은 SELinux 를 기본으로 씁니다. 어느 쪽이든 '허용 목록에 없는 것은 막는다' 는 원리는 같습니다.

프로필을 처음부터 손으로 쓰기는 어렵습니다. 그래서 aa-genprof·aa-logprof 로 서비스를 한 번 돌려 보며 필요한 접근을 학습해 초안을 만들고, 그 초안을 사람이 검토해 좁히는 흐름을 씁니다. 다만 학습으로 만든 프로필은 그 실행에서 우연히 쓰인 경로까지 넣으므로, 넣은 규칙마다 '이게 정말 필요한가' 를 되물어야 합니다.

점검 순서

  1. 프로필이 complain 이 아니라 enforce 인가(aa-status).
  2. systemd 유닛에 AppArmorProfile= 이 붙고 서비스가 다시 떴는가(/proc/<pid>/attr/current).
  3. 공격 경로가 막히고 거부가 로그에 남는가.
  4. 정상 동작이 유지되는가. 프로필이 /etc/apparmor.d 에 있어 부팅을 넘기는가.