서비스 · 프로세스 관리 48 / 50 · Part 5. 보안과 SOC
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정 analyst)

1. 들어가며

지금까지의 증거는 대부분 현재 상태 (/proc, 파일, 설정)였다. 그런데 사고 조사에서 가장 중요한 질문은 과거에 관한 것이다. "누가, 언제, 무엇을 실행했는가?", "cron 파일은 누가 바꿨는가?" 셸 히스토리는 사용자가 지우거나 남기지 않을 수 있고, journal에는 프로그램이 스스로 남긴 로그만 있다.

auditd 는 이 질문에 커널 수준에서 답한다. 시스템 콜이 일어나는 순간 커널이 이벤트를 만들기 때문에, 프로그램이 로그를 남기지 않아도 기록된다. 이번 글에서는 관제에 필요한 최소한의 감사 규칙을 설계·적재하고, 로그인 사용자의 실행 기록과 예약 작업 디렉터리 변경을 ausearch·aureport로 조회한다.


2. 핵심 개념

2-1. 감사 규칙 종류

종류형식의미
시스템 콜 규칙-a always,exit -F arch=b64 -S execve ...조건에 맞는 시스템 콜 종료 시 기록
파일 감시-w 경로 -p rwxa -k 키경로에 대한 읽기(r)·쓰기(w)·실행(x)·속성 변경(a) 기록
제어-D(전체 삭제), -b(버퍼), -e 2(규칙 잠금)운영 설정

2-2. 주요 필드

필드의미관제 활용
auid로그인 사용자 ID — 로그인 시 설정, sudo·su 후에도 유지실제 행위자 추적
uid / euid실제 / 유효 사용자권한 상승 여부
ses로그인 세션 번호19·41편의 sessionid와 연결
pid / ppid프로세스 / 부모실행 체인 복원
comm / exe이름 / 실행 파일 경로exe 기준 판단
EXECVE a0 a1 ...실행 인자무엇을 실행했나
key규칙에 붙인 이름검색 기준

2-3. 규칙 설계 원칙

원칙이유
목적별 key검색·보고를 key 단위로
범위 제한 (auid>=1000, auid!=unset)모든 execve를 기록하면 데몬 활동으로 로그가 폭증한다
변경 감시는 쓰기·속성(-p wa)읽기까지 감시하면 정상 조회로 잡음이 많다
규칙 파일로 관리/etc/audit/rules.d/*.rules → augenrules --load (재부팅 후 유지)

3. 동작 원리

auditd — 커널이 남기는 "누가 · 언제 · 무엇을"

analyst 로그인 → pam_loginuid 가 auid=1000 · ses=64 설정
  → sudo systemctl ...
       sudo(euid=root) → systemctl(uid=root) 로 권한 변경
       auid 는 1000 그대로 → 규칙 "auid>=1000" 에 계속 일치
  → execve 마다 커널이 SYSCALL · EXECVE · CWD · PATH 레코드 생성 → auditd 가 파일에 기록

실습 환경 안내 — 커널 감사 기능은 초기 PID 네임스페이스에서만 제어할 수 있어, 격리 컨테이너 안에서는 auditd가 동작하지 않는다. 실습에서는 호스트 PID 네임스페이스를 공유하는 별도의 수집 노드(audit-collector) 에서 auditd를 실행했다. 시스템 콜 규칙은 그대로 적용되고, 파일 감시 규칙(-w)만 대상 서버(rocky9-lab)의 경로를 /proc/<PID>/root/...로 바꿔 적재했다. 일반 서버에서는 규칙 파일을 그대로 augenrules --load하면 된다.


4. 명령어 실습

# 1) (수집 노드) 규칙 파일 작성 · 적재 · 확인
cat > /etc/audit/rules.d/50-soc-process.rules <<'EOF'
## 사용자 로그인 세션(auid>=1000)에서 실행된 모든 프로그램
-a always,exit -F arch=b64 -S execve -F auid>=1000 -F auid!=unset -k user_exec
## 예약 작업 · 서비스 설정 변경
-w /etc/cron.d/ -p wa -k cron_change
-w /var/spool/cron/ -p wa -k cron_change
-w /etc/systemd/system/ -p wa -k unit_change
EOF
auditctl -R <규칙 파일> ; auditctl -l          # 일반 서버: augenrules --load

# 2) (rocky9-lab) 감사 대상 행위
id -un; ls /etc/cron.d | head -2; sudo -n systemctl is-active crond         # analyst
echo '# lab audit test' > /etc/cron.d/lab-audit; rm -f /etc/cron.d/lab-audit # root

# 3) (수집 노드) 조회와 요약
ausearch -k user_exec -i --start recent | grep 'type=EXECVE' | tail -4
ausearch -k user_exec -i --start recent | grep -oE 'auid=... ses=[0-9]+ comm=... exe=...' | tail -3
ausearch -k cron_change -i --start recent | ...        # 변경된 파일 · 시스템 콜 · 주체
aureport -x --summary -i --start recent | head -8
aureport -k --summary -i --start recent | head -6

5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 (auditd 수집 노드) · root@audit-collector — 감사 규칙 설계와 적재

실제 실행 결과 — Rocky Linux 9.8 · analyst@rocky9-lab — 감사 대상 행위 (일반 작업)

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 감사 대상 행위 (설정 변경)

실제 실행 결과 — Rocky Linux 9.8 (auditd 수집 노드) · root@audit-collector — 감사 로그 조회와 요약

텍스트 원본(실제 출력):

[root@audit-collector ~]# cat > /etc/audit/rules.d/50-soc-process.rules <<'EOF'
> ## 사용자 로그인 세션(auid>=1000)에서 실행된 모든 프로그램
> -a always,exit -F arch=b64 -S execve -F auid>=1000 -F auid!=unset -k user_exec
> ## 예약 작업 · 서비스 설정 변경
> -w /etc/cron.d/ -p wa -k cron_change
> -w /var/spool/cron/ -p wa -k cron_change
> -w /etc/systemd/system/ -p wa -k unit_change
> EOF
[root@audit-collector ~]# RP=$(for p in $(pgrep -x systemd); do grep -qs rocky9-lab /proc/$p/root/etc/hostname && echo $p; done | head -1); echo "감시 대상 서버(rocky9-lab)의 init PID(호스트 기준)=$RP"
감시 대상 서버(rocky9-lab)의 init PID(호스트 기준)=606
[root@audit-collector ~]# auditctl -D >/dev/null; sed "s#^-w /#-w /proc/$RP/root/#" /etc/audit/rules.d/50-soc-process.rules > /tmp/lab-mapped.rules; auditctl -R /tmp/lab-mapped.rules 2>/dev/null >/dev/null; auditctl -l
-a always,exit -F arch=b64 -S execve -F auid>=1000 -F auid!=-1 -F key=user_exec
-w /proc/606/root/etc/cron.d -p wa -k cron_change
-w /proc/606/root/var/spool/cron -p wa -k cron_change
-w /proc/606/root/etc/systemd/system -p wa -k unit_change
[analyst@rocky9-lab ~]$ id -un; ls /etc/cron.d | head -2; sudo -n systemctl is-active crond
analyst
0hourly
active
[root@rocky9-lab ~]# echo '# lab audit test' > /etc/cron.d/lab-audit; rm -f /etc/cron.d/lab-audit
[root@audit-collector ~]# ausearch -k user_exec -i --start recent 2>/dev/null | grep -E 'type=EXECVE' | tail -4
type=EXECVE msg=audit(09/24/26 13:44:17.167:1125) : argc=2 a0=head a1=-2
type=EXECVE msg=audit(09/24/26 13:44:17.167:1126) : argc=5 a0=sudo a1=-n a2=systemctl a3=is-active a4=crond
type=EXECVE msg=audit(09/24/26 13:44:17.175:1127) : argc=3 a0=/usr/sbin/unix_chkpwd a1=analyst a2=chkexpiry
type=EXECVE msg=audit(09/24/26 13:44:17.179:1132) : argc=3 a0=systemctl a1=is-active a2=crond
[root@audit-collector ~]# ausearch -k user_exec -i --start recent 2>/dev/null | grep -oE 'auid=[a-z]+ uid=[a-z]+ .* ses=[0-9]+ comm=[a-z]+ exe=[^ ]+' | tail -3
auid=analyst uid=analyst gid=analyst euid=analyst suid=analyst fsuid=analyst egid=analyst sgid=analyst fsgid=analyst tty=pts0 ses=64 comm=head exe=/usr/bin/head
auid=analyst uid=analyst gid=analyst euid=root suid=root fsuid=root egid=analyst sgid=analyst fsgid=analyst tty=pts0 ses=64 comm=sudo exe=/usr/bin/sudo
auid=analyst uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts1 ses=64 comm=systemctl exe=/usr/bin/systemctl
[root@audit-collector ~]# ausearch -k cron_change -i --start recent 2>/dev/null | grep -oE 'type=PATH .* item=1 name=[^ ]+|type=SYSCALL .*syscall=[a-z]+|auid=[a-z]+ uid=[a-z]+|comm=[a-z]+ exe=[^ ]+' | grep -A0 -E 'name=/etc/cron.d/lab-audit|syscall=|auid=|comm=' | tail -8 | cut -c1-110
type=PATH msg=audit(09/24/26 13:44:17.539:1172) : item=1 name=/etc/cron.d/lab-audit
type=SYSCALL msg=audit(09/24/26 13:44:17.539:1172) : arch=x86_64 syscall=openat
auid=root uid=root
comm=bash exe=/usr/bin/bash
type=PATH msg=audit(09/24/26 13:44:17.539:1173) : item=1 name=/etc/cron.d/lab-audit
type=SYSCALL msg=audit(09/24/26 13:44:17.539:1173) : arch=x86_64 syscall=unlinkat
auid=root uid=root
comm=rm exe=/usr/bin/rm
[root@audit-collector ~]# aureport -x --summary -i --start recent 2>/dev/null | head -8

Executable Summary Report
=================================
total  file
=================================
796  /usr/libexec/openssh/sshd-session
106  /usr/lib/systemd/systemd
55  /usr/sbin/auditctl
[root@audit-collector ~]# aureport -k --summary -i --start recent 2>/dev/null | head -6

Key Summary Report
===========================
total  key
===========================
161  user_exec
[root@audit-collector ~]# auditctl -D >/dev/null

6. 결과 해석

관찰의미
감시 대상 서버 init PID 606, 규칙 4개 적재시스템 콜 규칙 1개, 파일 감시 3개. auid!=unset은 auid!=-1로 정규화되어 표시된다
EXECVE a0=head a1=-2, a0=sudo a1=-n a2=systemctl ...실행된 명령과 인자 전체 가 남는다. 셸 히스토리가 없어도 재구성할 수 있다
a0=/usr/sbin/unix_chkpwdsudo가 내부적으로 실행한 보조 프로그램까지 기록된다
head: auid=analyst uid=analyst euid=analyst일반 권한 실행
sudo: auid=analyst uid=analyst euid=root권한 상승 순간 — 유효 사용자가 root로 바뀌었다
systemctl: auid=analyst uid=root ... ses=64root로 실행됐지만 실제 행위자는 analyst 이고 64번 로그인 세션에서 나왔다
cron_change: name=/etc/cron.d/lab-audit, syscall=openat → unlinkat파일 생성(openat)과 삭제(unlinkat)가 각각 기록되었다. 이미 지워진 파일도 기록은 남는다
cron_change 주체 auid=root, comm=bash / comm=rmroot 로그인 세션의 bash가 만들고 rm이 지웠다
aureport -x: sshd-session 796, systemd 106, auditctl 55실행 파일별 집계. 실습 중 반복된 SSH 접속이 가장 많다 — 자동화 접속도 이벤트를 만든다
aureport -k: user_exec 161key별 이벤트 수. 규칙 범위를 조정할 때 근거가 된다

7. 보안 관점

주제내용
행위자 추적auid·ses 덕분에 공유 root 계정·sudo 사용에서도 실제 사람 을 특정할 수 있다
지워진 흔적파일을 만들고 바로 지워도 감시 규칙이 있으면 생성·삭제 기록이 남는다
로그 보호감사 로그도 root가 지울 수 있다. 원격 전송(audisp-remote, SIEM 에이전트)과 -e 2(재부팅 전 규칙 변경 금지)를 검토한다
성능·용량execve 전체 감사는 이벤트가 많다. 범위를 제한하고 aureport -k로 key별 양을 주기적으로 점검한다
탐지 규칙 연계SIEM에서 key=cron_change, key=unit_change 이벤트를 경보로, user_exec는 조사용 이력으로 쓴다

8. 보안관제 관점

[질문] "3시 10분에 /etc/cron.d 에 새 파일이 생겼다. 누가?"
     ↓
ausearch -k cron_change -i --start 03:05 --end 03:15
     → name=/etc/cron.d/X · syscall=openat · auid=<사용자> · ses=<N> · exe=/usr/bin/...
     ↓
ausearch --session <N> -i | grep -E 'type=EXECVE|type=USER_LOGIN'
     → 그 로그인 세션에서 실행한 명령 전체 · 로그인 출발지
     ↓
journalctl _COMM=sshd-session -g "session opened|Accepted" --since 03:00   (31편)
     ↓
타임라인: 로그인 → 명령 실행 → 파일 생성 → (다음 실행 시각) → 보고 (50편)
유용한 조회목적
ausearch -k KEY -i --start today오늘의 해당 key 이벤트
ausearch -ua analyst -i특정 로그인 사용자의 이벤트
ausearch --session N -i특정 로그인 세션의 모든 이벤트
aureport -au / aureport -l인증 / 로그인 요약

9. 실무에서 자주 발생하는 실수

실수결과예방
execve 전체를 조건 없이 감사로그 폭증, 성능 저하auid 조건, 대상 제한
auditctl로만 규칙 추가재부팅 시 사라짐rules.d 파일 + augenrules
-i 없이 조회uid·syscall이 숫자로만 보임-i로 이름 해석
규칙 쉘 명령에서 >= 따옴표 누락리다이렉션으로 해석되어 규칙 오류규칙 파일 사용 또는 따옴표
컨테이너 안에서 auditd 운영 시도동작하지 않음 (실측)호스트·VM 수준에서 운영

10. 실습 체크리스트

[ ] 시스템 콜 규칙과 파일 감시 규칙의 차이를 설명할 수 있다
[ ] auid·ses·euid 필드의 의미를 설명할 수 있다
[ ] 사용자 실행(user_exec)과 cron·unit 변경 감시 규칙을 적재했다
[ ] sudo 후 root 로 실행된 명령에서도 auid=analyst 가 유지되는 것을 확인했다
[ ] 생성 후 삭제된 cron 파일의 기록을 ausearch 로 찾았다
[ ] aureport 로 실행 파일·key 별 요약을 확인했다

11. 핵심 정리

  • auditd는 시스템 콜 시점에 커널이 기록하므로, 프로그램이 로그를 남기지 않아도 행위가 남는다.
  • auid(로그인 사용자)와 ses(세션) 는 sudo 이후에도 유지되어 실제 행위자를 특정한다.
  • 규칙은 목적별 key, 범위 제한, 파일 감시는 -p wa로 설계하고 rules.d로 관리한다.
  • 생성 후 바로 삭제된 파일도 감시 규칙이 있으면 기록이 남는다.
  • 감사 로그도 원격 전송과 규칙 잠금으로 보호한다.

12. 다음 편 예고

다음 글 「49. 프로세스·서비스 보안 점검 스크립트」 에서는 41~48편의 점검을 하나의 읽기 전용 스크립트 로 묶는다. 임시 경로 실행, 허용 목록 밖 포트, 패키지 밖 unit, 예약 작업, 실패 unit, CPU 과다를 섹션별로 점검하고 결과를 저장·비교한다.


참고 자료


시리즈 이동

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글