대상 OS: Debian 12 (bookworm)

운영 서버 보안에서 ‘방화벽/업데이트’는 시작일 뿐이다.
진짜 사고는 종종 “정상 프로세스가 정상 권한으로 할 수 있는 일”의 틈에서 벌어진다.
예를 들어 웹 프로세스가 실수로 `/etc`를 읽고, 취약점으로 쉘을 얻은 공격자가 홈 디렉터리를 뒤지고, 크론을 건드리는 식이다.
Debian 12는 AppArmor가 기본적으로 잘 붙어 있고, 서비스를 “필요한 만큼만” 움직이게 만들기 좋다.
이 글은 AppArmor를 **끄지 않고**, complain(학습)에서 enforce(강제)로 안전하게 옮기는 실전 절차를 정리한다.

---

왜 지금 AppArmor인가: ‘취약점이 있어도 피해를 좁히는’ 방어선

AppArmor는 커널 레벨에서 프로세스가 접근할 수 있는 파일/네트워크/권한을 제한한다.
즉, CVE 하나를 완전히 막지 못하더라도 “침투 후 확장”을 크게 어렵게 만든다.

- 서비스 A가 `/var/lib/A`만 읽고 쓰게 만들면, A가 뚫려도 `/etc/shadow`는 원칙적으로 못 만진다.
- 특정 바이너리의 실행(exec)을 막으면, 공격자가 시스템 도구를 체인으로 엮는 걸 줄인다.

---

1) 현재 AppArmor 상태 지도 만들기

1) AppArmor가 켜져 있는지 확인한다.

sudo aa-status

2) 커널 부팅 파라미터로 비활성화되어 있지 않은지 확인한다.

cat /proc/cmdline

3) 어떤 서비스가 이미 프로파일 적용을 받고 있는지 확인한다.

sudo aa-status | sed -n '1,160p'

운영 팁: 처음부터 “모든 걸 강제”하려 하면 장애가 난다. 이미 프로파일이 존재하는 서비스부터 다루고, 커스텀 서비스는 뒤로 미루자.

---

2) Debian 12에서 프로파일 파일 위치와 관리 흐름

- 프로파일 디렉터리: `/etc/apparmor.d/`
- 프로파일 on/off: `aa-enforce`, `aa-complain`, `aa-disable`
- 로드/리로드: `systemctl reload apparmor` 또는 `apparmor_parser`

패키지가 없다면 설치한다.

sudo apt-get update
sudo apt-get install -y apparmor apparmor-utils

---

3) complain 모드로 ‘학습’부터 시작하기(장애 없이 로그 수집)

예시로 nginx를 든다(다른 데몬도 방법은 동일).

1) nginx 프로파일이 있는지 확인.

ls -1 /etc/apparmor.d/ | grep -E '^usr\.sbin\.nginx$' || true

2) 프로파일을 complain 모드로 전환한다(차단은 하지 않고 기록만 남김).

sudo aa-complain /etc/apparmor.d/usr.sbin.nginx
sudo aa-status | grep -i nginx || true

3) 서비스 동작을 실제로 재현한다(트래픽/헬스체크/배포 등).

sudo systemctl restart nginx
sudo systemctl status nginx --no-pager

4) AppArmor 관련 거부/학습 로그를 확인한다.

sudo journalctl -k -g apparmor --since "-30 min" --no-pager | tail -n 80

중요: complain 단계는 “문제점을 드러내는 과정”이다. 로그가 많이 나오는 건 자연스럽다.

---

4) aa-logprof로 ‘최소 권한’만 허용하도록 프로파일 다듬기

aa-logprof는 로그를 기반으로 프로파일을 조정하는 대화형 도구다.

1) 학습된 이벤트를 기반으로 제안된 규칙을 검토한다.

sudo aa-logprof

2) 선택 기준(운영에서 실수 줄이는 룰)

1.

“경로가 너무 넓게 허용되는 제안”은 의심한다. 예: `/var/** rw,` 같은 패턴이 나오면 정말 필요한지 되묻는다.

2.

“임시 파일”은 구체적으로 제한한다. 예: 업로드 디렉터리만 rw를 주고, `/tmp/**` 전체 rw는 피한다.

3.

실행(exec) 허용은 특히 보수적으로. 서비스가 쉘이나 패키지 매니저를 실행할 이유는 거의 없다.

3) 수정 후 즉시 리로드한다.

sudo systemctl reload apparmor
sudo systemctl restart nginx

---

5) enforce로 전환(강제 적용) + 즉시 검증

1) enforce로 전환한다.

sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx

2) 서비스 상태와 최근 커널 로그를 동시에 확인한다.

sudo systemctl status nginx --no-pager
sudo journalctl -k -g DENIED --since "-10 min" --no-pager | tail -n 80

3) 만약 실제 장애가 발생하면 “원인 파악용”으로만 잠깐 complain로 되돌려 범위를 좁힌다.

sudo aa-complain /etc/apparmor.d/usr.sbin.nginx
# 원인 파악 후 규칙 수정 → 다시 enforce

운영 팁: ‘enforce 전환’은 배포/변경 창에 맞춰 진행하고, 롤백 플랜(complain 복귀/프로파일 원복)을 문서화하자.

---

6) systemd 서비스와 AppArmor를 같이 보는 법(원인 추적 포인트)

문제가 생겼을 때는 시스템 콜 레벨 메시지(커널)와 서비스 로그(systemd)를 같이 봐야 한다.

1) 해당 유닛의 최근 실패 원인을 확인.

sudo systemctl -u nginx -n 200 --no-pager

2) 커널(AppArmor) 로그에서 같은 시간대의 DENIED를 매칭.

sudo journalctl -k --since "-15 min" --no-pager | grep -Ei 'apparmor|DENIED' | tail -n 120

3) 프로파일이 실제로 적용 중인지 재확인.

sudo aa-status | sed -n '1,120p'

---

7) 커스텀 바이너리/에이전트에 프로파일을 붙이는 현실적인 방법

사내 에이전트(예: `/opt/agent/bin/agentd`)처럼 기본 프로파일이 없을 때가 많다.
이때 “처음부터 완벽한 프로파일”을 만들기보다, 최소한의 틀을 만들고 학습으로 좁혀가는 방식이 안정적이다.

1) 프로파일 파일을 생성한다(초기에는 complain 추천).

sudo aa-autodep /opt/agent/bin/agentd || true
ls -1 /etc/apparmor.d/ | grep -i agentd || true

2) 생성된 프로파일을 complain으로 두고 실제 업무 플로우를 전부 태운다.

sudo aa-complain /etc/apparmor.d/opt.agent.bin.agentd 2>/dev/null || true
sudo systemctl restart agentd 2>/dev/null || true

3) 로그를 기반으로 aa-logprof로 규칙을 정리한 뒤 enforce로 전환한다.

sudo aa-logprof
sudo aa-enforce /etc/apparmor.d/opt.agent.bin.agentd 2>/dev/null || true

주의: `aa-autodep`가 항상 원하는 이름으로 파일을 만들지 않을 수 있다. 실제 생성 파일명을 `/etc/apparmor.d/`에서 확인하고 적용하자.

---

8) 운영에서 자주 터지는 함정 5가지(미리 피하기)

1.

배포 시 심볼릭 링크가 바뀌는 구조(예: `/srv/app/current -> releases/2026...`)에서 경로 규칙이 너무 구체적이면 다음 배포에 바로 터진다. “안정적인 경로(예: /srv/app/shared)” 기준으로 룰을 잡자.

2.

로그 디렉터리 권한이 바뀌거나 logrotate 정책이 바뀌면, 쓰기 거부가 생긴다. AppArmor만 보지 말고 파일 소유/권한/유닛의 `User=`도 함께 점검.

3.

TLS 인증서 경로가 `/etc/ssl/private`처럼 민감한 곳이면, 읽기 권한을 줄 때 더 신중해야 한다. 서비스가 정말 그 키를 읽어야 하는지부터 확인.

4.

`/tmp`를 광범위하게 허용하면 편하지만, 공격자가 임시 파일을 악용할 여지가 커진다. 가능하면 애플리케이션 전용 temp 경로를 만들고 거기에만 권한을 준다.

5.

“DENIED가 없으니 안전”은 착각이다. 로그가 없을 뿐 정책이 느슨할 수도 있다. 정기적으로 complain→logprof→enforce를 반복하며 프로파일을 다듬는 루틴이 필요하다.

---

마무리: AppArmor는 ‘귀찮은 기능’이 아니라, 사고 비용을 줄이는 보험이다

Debian 12에서 AppArmor를 제대로 쓰면, 취약점이 있어도 피해 반경이 줄고, 감사/조사에 드는 시간이 줄어든다.
핵심은 세 가지다.

- complain으로 학습하고
- aa-logprof로 최소 권한만 남기며
- enforce로 강제한 뒤, 배포/운영 변화에 맞춰 계속 조정한다

서버 보안은 ‘한 번의 큰 설정’보다 ‘작은 제약을 꾸준히 붙이는 것’이 결국 승률이 높다.