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

1. 들어가며

41~48편에서 판단 기준, 기준선, 증거 수집, 네트워크, 예약 작업, unit 변경, 자원, 감사 로그를 하나씩 다뤘다. 관제 현장에서 이를 매번 손으로 입력하면 사람마다 결과가 다르고, 빠뜨리는 항목이 생긴다. 반복 점검은 스크립트로 표준화 해야 한다.

이번 글에서는 지금까지의 점검을 하나의 읽기 전용 점검 스크립트 로 묶는다. 섹션별로 결과를 출력하고, 경고(WARN) 개수를 요약하고, UTC 시각이 붙은 파일로 저장해 이전 결과와 비교한다. 작성 중 실제로 겪은 셸 하위 셸 카운터 버그 와 수정 방법도 함께 정리한다.


2. 핵심 개념

2-1. 점검 스크립트 설계 원칙

원칙구현
읽기 전용조회 명령만 사용. 수정·삭제·종료 명령 금지
섹션 구분[번호] 항목 제목 → 비어 있는 섹션도 "이상 없음"이라는 결과
경고 형식 통일[WARN] 대상 설명 한 줄 → grep·SIEM 수집이 쉬움
요약마지막 줄 요약: WARN N건
저장/var/log/soc-audit/<host>_<UTC>.txt
매개변수서버별 차이(허용 포트)는 인자로
이식성배포판별 명령(rpm/dpkg)은 분기 처리

2-2. 섹션 구성

섹션점검근거 편
1실행 파일이 삭제됐거나 임시 경로41·43
2허용 목록 밖 대기 포트44
3패키지 밖 unit·drop-in, 사용자 unit46
4사용자 crontab, cron 패키지 변경, at 대기45
5failed·masked unit23·24
6CPU 80% 이상47

2-3. 셸 함정: 파이프 뒤 while

n=0
ss -tlnpH | while read l; do n=$((n+1)); done   # while 이 하위 셸에서 실행 → 바깥 n 은 0
while read l; do n=$((n+1)); done < <(ss -tlnpH) # 현재 셸에서 실행 → n 증가

첫 버전에서는 섹션 2·4·5·6이 파이프 뒤 while을 써서 경고는 출력되는데 요약 개수에 반영되지 않았다. 프로세스 치환(< <(...))으로 고쳤다.


3. 동작 원리

soc-proc-audit.sh 구조 — 41~48편을 한 번에

soc-proc-audit.sh "22 8082"
  → OUT=/var/log/soc-audit/<host>_<UTC>.txt
  → { 헤더(host·time·kernel) → [1]~[6] 점검 → 요약 } | tee $OUT
  → 다음 점검 후 diff <(grep WARN 이전) <(grep WARN 최신) → 새로 생긴 경고만

4. 명령어 실습

# 1) 스크립트 작성 (전체 내용은 실행 결과 화면) 후 실행 — 허용 포트 22, 8082
chmod 700 /root/soc-proc-audit.sh
/root/soc-proc-audit.sh "22 8082"

# 2) 결과 보관과 비교 — 허용 목록을 22 로 좁혀 다시 실행
ls -l /var/log/soc-audit/
/root/soc-proc-audit.sh "22" > /dev/null
diff <(grep WARN <이전 결과>) <(grep WARN <최신 결과>)

스크립트는 root 권한이 필요하지만 아무것도 바꾸지 않는다. 운영 서버에 배포하기 전에는 테스트 서버에서 실행 시간과 출력량을 확인한다.


5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 점검 스크립트 작성 (읽기 전용 · 섹션별 · 결과 저장)

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 점검 결과 보관과 비교

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

[root@rocky9-lab ~]# cat > /root/soc-proc-audit.sh <<'EOF'
> #!/bin/bash
> # 프로세스·서비스 보안 점검 v1.0 — 읽기 전용. 사용: soc-proc-audit.sh [허용포트 목록]
> ALLOW=${1:-"22"}; OUT=/var/log/soc-audit/$(hostname)_$(date -u +%Y%m%dT%H%M%SZ).txt; mkdir -p ${OUT%/*}
> n=0; warn(){ n=$((n+1)); printf '  [WARN] %s\n' "$*"; }
> sec(){ printf '\n[%s] %s\n' "$1" "$2"; }
> {
> echo "host=$(hostname) time=$(date -u +%FT%TZ) kernel=$(uname -r)"
> sec 1 "실행 파일이 삭제됐거나 임시 경로에서 실행 중인 프로세스"
> for d in /proc/[0-9]*; do e=$(readlink $d/exe 2>/dev/null) || continue
>   case "$e" in *" (deleted)"|/tmp/*|/dev/shm/*|/var/tmp/*) warn "pid=${d#/proc/} user=$(stat -c %U $d) exe=$e";; esac; done
> sec 2 "허용 목록(${ALLOW}) 밖의 대기 포트"
> while read -r s r q l p x; do port=${l##*:}; [[ " $ALLOW " == *" $port "* ]] || warn "$l $(grep -oP 'users:\(\("\K[^"]+' <<<"$x")"; done < <(ss -tlnpH)
> sec 3 "systemd: 패키지 밖 unit · drop-in · 사용자 unit"
> for f in $(find /etc/systemd/system -type f \( -name '*.service' -o -name '*.timer' -o -name '*.socket' -o -name '*.conf' \)); do rpm -qf "$f" >/dev/null 2>&1 || warn "미소유 $f"; done
> for f in /home/*/.config/systemd/user/*; do [ -e "$f" ] && warn "사용자 unit $f"; done
> sec 4 "예약 작업"
> for f in /var/spool/cron/*; do [ -f "$f" ] && warn "crontab $(basename $f): $(grep -vc '^#' $f) 줄"; done
> while read l; do warn "cron 패키지 파일 변경 $l"; done < <(rpm -V cronie crontabs 2>/dev/null | grep -v '^\.\.\.\.\.\.\.T')
> [ -n "$(atq)" ] && warn "at 대기 작업 $(atq | wc -l)건"
> sec 5 "실패 · 마스크된 unit"
> while read u rest; do warn "failed $u"; done < <(systemctl --failed --no-legend --plain)
> while read u rest; do warn "masked $u"; done < <(systemctl list-unit-files --state=masked --no-legend)
> sec 6 "CPU 80% 이상 프로세스"
> while read l; do warn "$l"; done < <(ps -eo pid=,user=,%cpu=,comm= | awk '$3>=80')
> printf '\n요약: WARN %d건\n' $n
> } | tee $OUT
> echo "저장: $OUT"
> EOF
[root@rocky9-lab ~]# chmod 700 /root/soc-proc-audit.sh; /root/soc-proc-audit.sh "22 8082"
host=rocky9-lab time=2026-09-24T13:44:54Z kernel=6.18.44-fc-v37

[1] 실행 파일이 삭제됐거나 임시 경로에서 실행 중인 프로세스

[2] 허용 목록(22 8082) 밖의 대기 포트

[3] systemd: 패키지 밖 unit · drop-in · 사용자 unit
  [WARN] 미소유 /etc/systemd/system/labapp.service
  [WARN] 미소유 /etc/systemd/system/labapp.service.d/50-sandbox.conf
  [WARN] 미소유 /etc/systemd/system/labapp.service.d/override.conf

[4] 예약 작업

[5] 실패 · 마스크된 unit

[6] CPU 80% 이상 프로세스

요약: WARN 3건
저장: /var/log/soc-audit/rocky9-lab_20260924T134454Z.txt
[root@rocky9-lab ~]# ls -l /var/log/soc-audit/
total 4
-rw-r--r-- 1 root root 560 Sep 24 13:44 rocky9-lab_20260924T134454Z.txt
[root@rocky9-lab ~]# sleep 1; /root/soc-proc-audit.sh "22" > /dev/null; ls /var/log/soc-audit/ | wc -l
2
[root@rocky9-lab ~]# diff <(grep WARN $(ls -t /var/log/soc-audit/* | sed -n 2p)) <(grep WARN $(ls -t /var/log/soc-audit/* | sed -n 1p))
0a1
>   [WARN] 127.0.0.1:8082 python3
4c5
< 요약: WARN 3건
---
> 요약: WARN 4건

6. 결과 해석

관찰의미
헤더 host=rocky9-lab time=...Z kernel=6.18.44-fc-v37결과 파일만 봐도 어느 서버를 언제 점검했는지 알 수 있다. 커널 버전은 실습 환경(호스트 커널 공유)을 그대로 보여 준다
[1]·[2]·[4]·[5]·[6] 비어 있음해당 항목 이상 없음 이라는 결과다. 섹션을 남겨 두어야 "점검했다"는 증거가 된다
[3] labapp.service와 drop-in 2개 미소유25·28·38편에서 관리자가 만든 unit이다. 승인된 항목 이므로 예외 목록으로 관리할 대상
요약: WARN 3건, 저장 경로 출력결과가 파일로 남았다
2차 실행(허용 포트 22만) → diff 4c5, WARN 3건 → 4건허용 목록을 좁히자 127.0.0.1:8082 python3가 새 경고로 추가되었다. diff 화면의 > 줄이 새로 생긴 경고 다

결과를 보면 이 서버에서 경고는 모두 설명 가능한 항목 이다. 점검 스크립트의 역할은 판단이 아니라 판단할 대상을 빠짐없이, 같은 형식으로 보여 주는 것 이다.


7. 보안 관점

주제내용
스크립트 보호점검 스크립트 자체가 변조되면 결과를 믿을 수 없다. root 소유·700 권한, 해시 관리, 가능하면 관제 서버에서 배포·실행
결과 보호결과 파일은 서버 밖으로 수집한다. 서버 안의 과거 결과는 조작될 수 있다
예외 관리승인된 항목(labapp 등)은 스크립트 안에 하드코딩하지 말고 근거·만료일이 있는 예외 파일 로 관리한다
한계스크립트는 알려진 점검 항목만 본다. 기준선 비교(42편)·감사 로그(48편)와 함께 운영한다

8. 보안관제 관점

[배포]    관제 서버 → 대상 서버에 스크립트 배포 (해시 확인)
[실행]    systemd timer (35편) 로 매일 1회 · 결과 파일 생성
[수집]    결과 파일 → SIEM / 중앙 저장소
[분석]    서버별 WARN 수 추이 · 전일 대비 신규 WARN (diff)
[대응]    신규 WARN → 41편 판단 → 43편 수집 → 50편 절차
운영 지표의미
서버별 WARN 수평소보다 늘면 우선 점검
신규 WARN 수전일 대비 새로 생긴 항목 — 실제 조사 대상
예외 목록 크기너무 커지면 점검이 무력화된다
실행 실패스크립트가 돌지 않은 서버 = 관제 공백

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

실수결과예방
파이프 뒤 while로 카운트요약 개수 오류 (실측)< <(명령)
이상 없는 섹션을 출력하지 않음점검 누락과 구분 불가섹션 제목은 항상 출력
점검 스크립트에 조치 명령 포함오탐 한 번에 서비스 장애점검과 조치 분리
결과를 로컬에만 저장비교·보존 불가중앙 수집
배포판 차이 무시Ubuntu에서 rpm 없음 → 섹션 오류command -v rpm \|\| dpkg 분기

10. 실습 체크리스트

[ ] 읽기 전용 · 섹션 · WARN 형식 · 요약 · 저장 원칙으로 스크립트를 작성했다
[ ] 6개 섹션이 각각 어느 편의 점검인지 연결할 수 있다
[ ] 파이프 뒤 while 의 하위 셸 카운터 문제를 이해하고 수정했다
[ ] 결과 파일을 UTC 시각 이름으로 저장했다
[ ] 허용 목록을 바꿔 두 결과를 diff 로 비교했다
[ ] 승인된 항목을 예외로 관리하는 방법을 설명할 수 있다

11. 핵심 정리

  • 반복 점검은 읽기 전용 스크립트 로 표준화해 사람마다 다른 결과와 누락을 없앤다.
  • 섹션별 출력, 통일된 WARN 형식, 요약, UTC 시각 파일 저장이 핵심 설계다.
  • 파이프 뒤 while은 하위 셸에서 실행되므로 카운터는 < <(명령)으로 입력한다.
  • 결과는 이전 결과와 diff해 새로 생긴 경고 에 집중한다.
  • 스크립트·결과·예외 목록 모두 보호·관리 대상이다.

12. 다음 편 예고

시리즈의 마지막 글 「50. 서비스·프로세스 보안관제 종합 실습」 에서는 훈련용 시나리오(무해한 항목)를 두고 탐지 → 증거 수집 → 타임라인 → 격리·제거 → 검증 → 보고 의 전 과정을 이 시리즈의 도구만으로 수행하고, 보고서 템플릿으로 정리한다.


참고 자료


시리즈 이동

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

0개의 댓글