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

1. 들어가며

45편에서 예약 작업 파일의 변경을 세 가지 근거로 찾았다. systemd unit은 한 단계 더 복잡하다. 서비스 하나의 최종 설정은 원본 unit + drop-in + enable 링크 가 합쳐진 결과이고(22·28편), 원본은 그대로인데 drop-in 하나만 추가되어도 서비스 동작이 바뀐다.

이번 글에서는 unit 변경을 파일 층 · 병합 층 · 적용 층 · 일정 층 으로 나누어 점검한다. 무해한 drop-in(sshd 시작 전 logger 한 줄)과 timer를 추가한 뒤, 각 층의 점검 명령이 이를 어떻게 보여 주는지 확인하고 원래 상태로 되돌린다.


2. 핵심 개념

2-1. 점검 층

층질문명령
파일어떤 파일이 새로 생기거나 바뀌었나find /etc/systemd/system -newermt, rpm -qf
원본패키지가 설치한 unit 자체가 바뀌었나rpm -V systemd <패키지>
병합원본을 무엇이 확장·대체·차단하나systemd-delta
적용실제로 실행될 값은 무엇인가systemctl show -p DropInPaths,ExecStart,ExecStartPre,User
일정언제 실행되나systemctl list-timers, list-sockets
시점언제 적용되었나journalctl _PID=1 -g Reloading

2-2. systemd-delta 결과 종류

표시의미
[EXTENDED]drop-in으로 원본을 확장
[OVERRIDDEN]같은 이름의 unit이 상위 경로(/etc)에 있어 원본을 대체
[MASKED]/dev/null 링크로 차단
[EQUIVALENT]내용이 같은 사본
[REDIRECTED]링크로 다른 파일을 가리킴

2-3. 위험도가 높은 변경

변경이유
핵심 서비스(sshd 등)의 ExecStartPre=/ExecStartPost= 추가서비스는 정상 동작하면서 추가 명령이 root로 실행된다
ExecStart= 교체이름은 같지만 다른 프로그램이 실행된다 (28편)
보안 서비스의 mask부팅 후에도 시작되지 않는다 (23편)
샌드박스 옵션 완화38편의 보호가 사라진다
패키지 밖 timer·socket 추가새 자동 실행 경로

3. 동작 원리

systemd unit 변경 점검 — 파일 · 병합 결과 · 실제 적용값

(가정) /etc/systemd/system/sshd.service.d/zz-lab.conf  ← ExecStartPre 한 줄
       /etc/systemd/system/lab-refresh.{service,timer} + enable
       systemctl daemon-reload

파일 층   : find -newermt '-5 min'   → 새 파일 4개 (링크 포함)
병합 층   : systemd-delta            → [EXTENDED] sshd.service → zz-lab.conf
적용 층   : systemctl show sshd      → ExecStartPre=/usr/bin/logger ...
일정 층   : list-timers              → lab-refresh.timer 다음 실행 시각
시점      : journalctl _PID=1        → Reloading

주목할 점은 sshd 원본 파일은 바뀌지 않았다 는 것이다. rpm -V openssh-server는 계속 "변경 없음"이다. 병합 층을 보지 않으면 이 변경을 놓친다.


4. 명령어 실습

# 1) 현재 상태 기록
systemd-delta --no-pager | tail -1
find /etc/systemd/system -type f \( -name '*.service' -o -name '*.timer' -o -name '*.socket' -o -name '*.conf' \) -printf '%TY-%Tm-%Td %TH:%TM  %p\n' | sort
for f in $(find /etc/systemd/system -maxdepth 1 -type f -name '*.service'); do printf '%-45s %s\n' $f "$(rpm -qf $f ...)"; done
rpm -V systemd openssh-server && echo "vendor unit 변경 없음"

# 2) 변경 발생 (무해한 logger 항목)
mkdir -p /etc/systemd/system/sshd.service.d
printf '[Service]\nExecStartPre=/usr/bin/logger lab-sshd-prestart\n' > /etc/systemd/system/sshd.service.d/zz-lab.conf
printf '...Type=oneshot\nExecStart=/usr/bin/logger lab-timer-run\n' > /etc/systemd/system/lab-refresh.service
printf '...[Timer]\nOnCalendar=hourly\n[Install]\nWantedBy=timers.target\n' > /etc/systemd/system/lab-refresh.timer
systemctl daemon-reload; systemctl enable --now lab-refresh.timer

# 3) 층별 탐지
systemd-delta --type=extended --no-pager
systemctl show sshd -p DropInPaths,ExecStartPre --no-pager
find /etc/systemd/system -newermt '-5 min' \( -type f -o -type l \)
systemctl list-timers lab-refresh.timer --no-pager --no-legend
journalctl _PID=1 --since "-5min" --no-pager -o cat | grep -iE 'reload' | tail -3

# 4) 정리 후 재확인
systemctl disable --now lab-refresh.timer; rm -rf /etc/systemd/system/sshd.service.d /etc/systemd/system/lab-refresh.*
systemctl daemon-reload; systemd-delta --type=extended --no-pager | tail -1

5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — unit 영역의 현재 상태 기록

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 변경 발생 (무해한 drop-in 과 timer)

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 변경 탐지

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 정리

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

[root@rocky9-lab ~]# systemd-delta --no-pager | tail -1
6 overridden configuration files found.
[root@rocky9-lab ~]# find /etc/systemd/system -type f \( -name '*.service' -o -name '*.timer' -o -name '*.socket' -o -name '*.conf' \) -printf '%TY-%Tm-%Td %TH:%TM  %p\n' | sort
2026-09-24 12:11  /etc/systemd/system/labapp.service.d/override.conf
2026-09-24 12:32  /etc/systemd/system/labapp.service
2026-09-24 12:32  /etc/systemd/system/labapp.service.d/50-sandbox.conf
[root@rocky9-lab ~]# for f in $(find /etc/systemd/system -maxdepth 1 -type f -name '*.service'); do printf '%-45s %s\n' $f "$(rpm -qf $f 2>&1 | sed 's/file .* is not owned by any package/패키지 미소유/')"; done
/etc/systemd/system/labapp.service            패키지 미소유
[root@rocky9-lab ~]# rpm -V systemd openssh-server && echo "vendor unit 변경 없음"
.M.......  c /etc/machine-id
[root@rocky9-lab ~]# mkdir -p /etc/systemd/system/sshd.service.d; printf '[Service]\nExecStartPre=/usr/bin/logger lab-sshd-prestart\n' > /etc/systemd/system/sshd.service.d/zz-lab.conf
[root@rocky9-lab ~]# printf '[Unit]\nDescription=Lab cache refresh\n[Service]\nType=oneshot\nExecStart=/usr/bin/logger lab-timer-run\n' > /etc/systemd/system/lab-refresh.service
[root@rocky9-lab ~]# printf '[Unit]\nDescription=Lab cache refresh timer\n[Timer]\nOnCalendar=hourly\n[Install]\nWantedBy=timers.target\n' > /etc/systemd/system/lab-refresh.timer
[root@rocky9-lab ~]# systemctl daemon-reload; systemctl enable --now lab-refresh.timer -q
[root@rocky9-lab ~]# systemd-delta --type=extended --no-pager | grep -v -e hostnamed -e 'user@'
[EXTENDED]   /etc/systemd/system/labapp.service → /etc/systemd/system/labapp.service.d/50-sandbox.conf
[EXTENDED]   /etc/systemd/system/labapp.service → /etc/systemd/system/labapp.service.d/override.conf
[EXTENDED]   /usr/lib/systemd/system/sshd.service → /etc/systemd/system/sshd.service.d/zz-lab.conf

5 overridden configuration files found.
[root@rocky9-lab ~]# systemctl show sshd -p DropInPaths,ExecStartPre --no-pager | cut -c1-110
ExecStartPre={ path=/usr/bin/logger ; argv[]=/usr/bin/logger lab-sshd-prestart ; ignore_errors=no ; start_time
DropInPaths=/etc/systemd/system/sshd.service.d/zz-lab.conf
[root@rocky9-lab ~]# find /etc/systemd/system -newermt '-5 min' \( -type f -o -type l \) -printf '%p\n'
/etc/systemd/system/timers.target.wants/lab-refresh.timer
/etc/systemd/system/sshd.service.d/zz-lab.conf
/etc/systemd/system/lab-refresh.timer
/etc/systemd/system/lab-refresh.service
[root@rocky9-lab ~]# systemctl list-timers lab-refresh.timer --no-pager --no-legend | cut -c1-90
Thu 2026-09-24 14:00:00 UTC 17min left - - lab-refresh.timer lab-refresh.service
[root@rocky9-lab ~]# journalctl _PID=1 --since "-5min" --no-pager -o cat | grep -iE 'reload|created symlink' | tail -3
Queuing reload/restart jobs for marked units…
Reloading.
Reloading.
[root@rocky9-lab ~]# systemd-delta --type=extended --no-pager | tail -1
4 overridden configuration files found.

6. 결과 해석

관찰의미
변경 전 6 overridden configuration files found배포판 기본 drop-in과 28·38편의 labapp drop-in이 포함된 기준 상태
/etc/systemd/system의 실제 파일 3개 (labapp과 drop-in)관리자 영역에 있는 unit 파일 목록과 수정 시각
labapp.service 패키지 미소유관리자가 만든 서비스. 변경 관리 기록이 있어야 하는 대상
rpm -V → .M....... c /etc/machine-idmachine-id는 설치 후 생성되어 권한이 달라지는 정상 차이 다. unit 파일 변경은 없다
delta [EXTENDED] /usr/lib/.../sshd.service → /etc/.../sshd.service.d/zz-lab.confsshd 원본은 그대로인데 동작이 확장되었다. 병합 층에서만 보이는 변경
ExecStartPre={ path=/usr/bin/logger ; argv[]=... lab-sshd-prestart ...}다음 sshd 시작(재부팅·재시작) 때 root로 실행될 명령이 적용 값에 들어갔다
DropInPaths=/etc/systemd/system/sshd.service.d/zz-lab.conf적용 값의 출처 파일
find -newermt → timer 링크, drop-in, timer, service새로 생긴 파일 4개. enable 링크도 포함된다
list-timers → 14:00 실행 예정새 자동 실행 일정
journalctl _PID=1 → Reloading.daemon-reload 시각. 변경이 언제 적용되었는지 의 단서
extended 기준 변경 중 5 → 정리 후 4sshd drop-in 하나가 사라졌다. (변경 전의 6은 --type 없이 모든 종류를 센 값이라 직접 비교하지 않는다 — 같은 명령끼리 비교 해야 한다)

7. 보안 관점

주제내용
원본 검증의 한계rpm -V는 원본 파일만 본다. drop-in·/etc의 대체 unit은 systemd-delta 로 봐야 한다
핵심 서비스 우선sshd, 로그 수집기, 보안 에이전트, 웹 서버처럼 권한이 크거나 외부에 노출된 서비스의 drop-in을 우선 점검한다
적용 시점파일이 바뀌어도 daemon-reload·재시작 전에는 실행 중인 프로세스에 반영되지 않는다. "파일 변경 시각"과 "적용 시각"을 구분해 기록한다
감사 연계48편의 -w /etc/systemd/system -p wa 규칙으로 누가 파일을 만들었는지 확인한다

8. 보안관제 관점

[기준]    systemd-delta --no-pager > delta_baseline.txt
          find /etc/systemd /home/*/.config/systemd -type f -exec sha256sum {} + > unit.sha256
     ↓
[점검]    diff <(systemd-delta --no-pager) delta_baseline.txt
          sha256sum -c unit.sha256 --quiet ; find ... -newer unit.sha256
     ↓
[변경 발견] systemctl cat <unit> · systemctl show -p DropInPaths,ExecStart,ExecStartPre,ExecStartPost,User
            rpm -qf <파일> · 감사 로그(unit_change)
     ↓
[판단]    변경 관리 기록 대조 → 미승인이면 사본 보존 → 원복 → daemon-reload → 재검증

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

실수결과예방
unit 원본만 해시 점검drop-in 변경 누락systemd-delta 병행
/etc/systemd/system만 확인/run, 사용자 unit 누락경로 전체 + 사용자 홈
파일만 지우고 daemon-reload 누락메모리의 설정이 그대로 남음정리 후 daemon-reload
machine-id 차이를 변조로 오판오탐정상 차이 목록 관리
timer 파일만 지우고 enable 링크 방치깨진 링크 잔존disable 후 삭제

10. 실습 체크리스트

[ ] 파일·원본·병합·적용·일정 층을 구분해 점검했다
[ ] 원본 변경 없이 drop-in 만으로 sshd 설정이 확장된 것을 systemd-delta 로 찾았다
[ ] systemctl show 로 실제 적용될 ExecStartPre 와 출처를 확인했다
[ ] find -newermt 로 새 unit·링크를 찾았다
[ ] daemon-reload 시각을 journal 에서 확인했다
[ ] 정리 후 daemon-reload 와 재확인까지 완료했다

11. 핵심 정리

  • systemd 변경은 파일 · 원본 · 병합 · 적용 · 일정 층으로 나누어 점검한다.
  • 원본은 그대로인데 drop-in으로 동작이 바뀌는 변경은 systemd-delta와 systemctl show로만 보인다.
  • 핵심 서비스의 ExecStartPre/Post, ExecStart 교체, mask, 샌드박스 완화가 고위험 변경이다.
  • 파일 변경 시각과 daemon-reload(적용) 시각을 구분해 기록한다.
  • 정리할 때는 disable → 삭제 → daemon-reload → 재확인 순서를 지킨다.

12. 다음 편 예고

다음 글 「47. 자원 사용 이상 탐지 (CPU·메모리)」 에서는 load·%CPU·메모리 지표에서 원인 프로세스와 소속(cgroup)까지 좁히는 절차를 정리하고, 두 번 측정해 지속적인 고부하만 보고 하는 점검 스크립트를 만든다.


참고 자료


시리즈 이동

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

0개의 댓글