
서비스 · 프로세스 관리 46 / 50 · Part 5. 보안과 SOC
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정analyst)
45편에서 예약 작업 파일의 변경을 세 가지 근거로 찾았다. systemd unit은 한 단계 더 복잡하다. 서비스 하나의 최종 설정은 원본 unit + drop-in + enable 링크 가 합쳐진 결과이고(22·28편), 원본은 그대로인데 drop-in 하나만 추가되어도 서비스 동작이 바뀐다.
이번 글에서는 unit 변경을 파일 층 · 병합 층 · 적용 층 · 일정 층 으로 나누어 점검한다. 무해한 drop-in(sshd 시작 전 logger 한 줄)과 timer를 추가한 뒤, 각 층의 점검 명령이 이를 어떻게 보여 주는지 확인하고 원래 상태로 되돌린다.
| 층 | 질문 | 명령 |
|---|---|---|
| 파일 | 어떤 파일이 새로 생기거나 바뀌었나 | 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 |
| 표시 | 의미 |
|---|---|
[EXTENDED] | drop-in으로 원본을 확장 |
[OVERRIDDEN] | 같은 이름의 unit이 상위 경로(/etc)에 있어 원본을 대체 |
[MASKED] | /dev/null 링크로 차단 |
[EQUIVALENT] | 내용이 같은 사본 |
[REDIRECTED] | 링크로 다른 파일을 가리킴 |
| 변경 | 이유 |
|---|---|
핵심 서비스(sshd 등)의 ExecStartPre=/ExecStartPost= 추가 | 서비스는 정상 동작하면서 추가 명령이 root로 실행된다 |
ExecStart= 교체 | 이름은 같지만 다른 프로그램이 실행된다 (28편) |
| 보안 서비스의 mask | 부팅 후에도 시작되지 않는다 (23편) |
| 샌드박스 옵션 완화 | 38편의 보호가 사라진다 |
| 패키지 밖 timer·socket 추가 | 새 자동 실행 경로 |

(가정) /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는 계속 "변경 없음"이다. 병합 층을 보지 않으면 이 변경을 놓친다.
# 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




텍스트 원본(실제 출력):
[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 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-id | machine-id는 설치 후 생성되어 권한이 달라지는 정상 차이 다. unit 파일 변경은 없다 |
delta [EXTENDED] /usr/lib/.../sshd.service → /etc/.../sshd.service.d/zz-lab.conf | sshd 원본은 그대로인데 동작이 확장되었다. 병합 층에서만 보이는 변경 |
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 → 정리 후 4 | sshd drop-in 하나가 사라졌다. (변경 전의 6은 --type 없이 모든 종류를 센 값이라 직접 비교하지 않는다 — 같은 명령끼리 비교 해야 한다) |
| 주제 | 내용 |
|---|---|
| 원본 검증의 한계 | rpm -V는 원본 파일만 본다. drop-in·/etc의 대체 unit은 systemd-delta 로 봐야 한다 |
| 핵심 서비스 우선 | sshd, 로그 수집기, 보안 에이전트, 웹 서버처럼 권한이 크거나 외부에 노출된 서비스의 drop-in을 우선 점검한다 |
| 적용 시점 | 파일이 바뀌어도 daemon-reload·재시작 전에는 실행 중인 프로세스에 반영되지 않는다. "파일 변경 시각"과 "적용 시각"을 구분해 기록한다 |
| 감사 연계 | 48편의 -w /etc/systemd/system -p wa 규칙으로 누가 파일을 만들었는지 확인한다 |
[기준] 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 → 재검증
| 실수 | 결과 | 예방 |
|---|---|---|
| unit 원본만 해시 점검 | drop-in 변경 누락 | systemd-delta 병행 |
/etc/systemd/system만 확인 | /run, 사용자 unit 누락 | 경로 전체 + 사용자 홈 |
| 파일만 지우고 daemon-reload 누락 | 메모리의 설정이 그대로 남음 | 정리 후 daemon-reload |
| machine-id 차이를 변조로 오판 | 오탐 | 정상 차이 목록 관리 |
| timer 파일만 지우고 enable 링크 방치 | 깨진 링크 잔존 | disable 후 삭제 |
[ ] 파일·원본·병합·적용·일정 층을 구분해 점검했다
[ ] 원본 변경 없이 drop-in 만으로 sshd 설정이 확장된 것을 systemd-delta 로 찾았다
[ ] systemctl show 로 실제 적용될 ExecStartPre 와 출처를 확인했다
[ ] find -newermt 로 새 unit·링크를 찾았다
[ ] daemon-reload 시각을 journal 에서 확인했다
[ ] 정리 후 daemon-reload 와 재확인까지 완료했다
systemd-delta와 systemctl show로만 보인다.ExecStartPre/Post, ExecStart 교체, mask, 샌드박스 완화가 고위험 변경이다.다음 글 「47. 자원 사용 이상 탐지 (CPU·메모리)」 에서는 load·%CPU·메모리 지표에서 원인 프로세스와 소속(cgroup)까지 좁히는 절차를 정리하고, 두 번 측정해 지속적인 고부하만 보고 하는 점검 스크립트를 만든다.