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

1. 들어가며

16편에서 ss -p로 포트와 프로세스를 연결하는 방법을 배웠다. 관제 점검에서는 여기서 한 걸음 더 나아가야 한다. "22번 포트를 sshd가 쓰고 있다"는 사실만으로는 판단이 안 된다. 이 서버에서 그 포트가 승인된 것인지, 그 프로세스가 정상 경로의 프로그램인지 를 함께 봐야 한다.

이번 글에서는 대기 포트를 소유 프로세스 · 실행 계정 · 실행 파일 경로 · 허용 목록 과 결합해 한 줄씩 판단하는 점검 스크립트를 만들고, 허용 목록에 없는 테스트 웹 서버를 찾아낸다. 외부 연결 요약까지 포함해 네트워크 관점의 점검 절차를 정리한다.


2. 핵심 개념

2-1. 점검 항목

항목질문도구
대기 포트이 서버가 받는 접속은 무엇인가ss -tlnp, ss -ulnp
바인딩 주소외부에 노출되는가 (0.0.0.0, [::]) 로컬 전용인가 (127.0.0.1)ss -tln
소유 프로세스누가, 어떤 실행 파일로 열었나/proc/PID/exe, ps -o user
허용 여부서버 역할상 승인된 포트인가허용 목록 (자산 관리)
연결누구와 통신 중인가ss -tnp state established

2-2. 허용 목록 방식

방식장점단점
허용 목록(allowlist)목록 밖은 모두 표시 → 새로운 것에 강함서버별 목록 관리 필요
차단 목록(denylist)관리가 쉬움알려진 것만 잡음

서버마다 역할이 정해져 있으므로(웹 서버: 80·443, DB: 3306 …) 대기 포트는 허용 목록으로 관리하기 좋다. 42편의 포트 기준선이 허용 목록의 출발점이 된다.


3. 동작 원리

네트워크 점검표 = 포트 × 프로세스 × 실행 경로 × 허용 목록

ss -tlnpH 한 줄씩
  → LOCAL 에서 포트 추출, users:(("...",pid=N,...)) 에서 PID 추출
  → readlink /proc/N/exe , ps -o user= -p N
  → 포트 ∉ ALLOW → NOT_ALLOWED
  → exe ∈ /tmp · /dev/shm · /var/tmp → TMP_EXE
  → 표 한 줄 출력

두 플래그가 동시에 붙으면 41편 기준으로 우선 확인 대상이다. 반대로 허용 포트라도 실행 파일 경로가 바뀌었다면(예: sshd가 /usr/sbin이 아닌 경로) 확인해야 하므로, 실행 경로 열은 허용 포트에도 항상 표시한다.


4. 명령어 실습

# 1) (analyst) 점검 대상: 임시 경로의 python 복사본으로 연 테스트 웹 서버 (로컬 전용)
cp /usr/bin/python3 /tmp/py-lab && cd /tmp && nohup /tmp/py-lab -m http.server 8443 --bind 127.0.0.1 > /dev/null 2>&1 &

# 2) (root) 점검 스크립트 /root/net-audit.sh
#    ALLOW="22 8082"  ← 이 서버에서 승인된 대기 포트
#    ss 결과 + exe + user + 플래그(NOT_ALLOWED, TMP_EXE)
/root/net-audit.sh

# 3) 연결 요약 (SSH 외 연결 수)
ss -tnpH state established | awk '{print $3, "<->", $4}' | head -5
ss -tnpH state established '( not sport = :22 )' | wc -l

5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 · analyst@rocky9-lab — 점검 대상 준비 (허용 목록에 없는 대기 포트)

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 대기 포트 · 소유 프로세스 · 실행 경로를 한 표로

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 외부로 나가는 연결 요약

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

[analyst@rocky9-lab ~]$ cp /usr/bin/python3 /tmp/py-lab && cd /tmp && nohup /tmp/py-lab -m http.server 8443 --bind 127.0.0.1 > /dev/null 2>&1 &
[analyst@rocky9-lab ~]$ sleep 1; ss -tlnH 'sport = :8443' | wc -l
1
[root@rocky9-lab ~]# cat > /root/net-audit.sh <<'EOF'
> #!/bin/bash
> ALLOW="22 8082"   # 이 서버에서 승인된 대기 포트
> printf '%-22s %-7s %-8s %-26s %s\n' LOCAL PID USER EXE FLAG
> ss -tlnpH | while read -r st rq sq local peer proc; do
>   port=${local##*:}; pid=$(grep -oP 'pid=\K[0-9]+' <<<"$proc" | head -1)
>   exe=$(readlink /proc/$pid/exe); user=$(ps -o user= -p $pid); f=""
>   [[ " $ALLOW " == *" $port "* ]] || f="$f NOT_ALLOWED"
>   case "$exe" in /tmp/*|/dev/shm/*|/var/tmp/*) f="$f TMP_EXE";; esac
>   printf '%-22s %-7s %-8s %-26s %s\n' "$local" "$pid" "$user" "${exe:0:26}" "${f:- ok}"
> done
> EOF
[root@rocky9-lab ~]# chmod 700 /root/net-audit.sh; /root/net-audit.sh
LOCAL                  PID     USER     EXE                        FLAG
0.0.0.0:22             28      root     /usr/sbin/sshd              ok
127.0.0.1:8082         31      analyst  /usr/bin/python3.9          ok
127.0.0.1:8443         1462    analyst  /tmp/py-lab                 NOT_ALLOWED TMP_EXE
[root@rocky9-lab ~]# ss -tnpH state established | awk '{print $3, "<->", $4}' | head -5
172.17.0.2:22 <-> 172.17.0.1:52510
[root@rocky9-lab ~]# ss -tnpH state established '( not sport = :22 )' | wc -l
0
[root@rocky9-lab ~]# pkill -u analyst -f py-lab; rm -f /tmp/py-lab; true

6. 결과 해석

관찰의미
0.0.0.0:22 ... /usr/sbin/sshd ok승인된 포트, 정상 경로. 다만 0.0.0.0이라 외부 노출 되어 있음을 인지한다
127.0.0.1:8082 ... /usr/bin/python3.9 oklabapp. 허용 목록에 있고 로컬 전용이다
127.0.0.1:8443 1462 analyst /tmp/py-lab NOT_ALLOWED TMP_EXE승인되지 않은 포트 + 임시 경로 실행 파일. 확인 대상 1순위
연결 요약: 172.17.0.2:22 <-> 172.17.0.1:52510현재 연결은 실습용 SSH 하나뿐이다
SSH 외 연결 0건이 서버는 지금 다른 곳과 통신하지 않는다. 이것도 기록할 사실이다

실행 파일이 /usr/bin/python3.9인 labapp과 /tmp/py-lab인 테스트 서버는 같은 python 프로그램 이다. 프로그램 이름이 아니라 실행 경로 를 봐야 둘을 구분할 수 있다.


7. 보안 관점

주제내용
노출 범위내부용 관리 도구·DB가 0.0.0.0에 바인딩되어 있으면 방화벽 한 줄 실수로 외부에 노출된다. 바인딩 주소 자체를 점검한다
소켓 활성화포트 주인이 systemd(PID 1)로 보이면 37편 방식으로 socket unit을 확인한다
권한전체 결과를 보려면 root로 실행해야 한다. 일반 계정 결과의 빈 Process 열은 "없음"이 아니다 (16편)
도구 변조ss 결과는 /proc/net/tcp 직접 확인, 외부에서의 포트 스캔 결과와 교차 검증한다
나가는 연결대기 포트 점검만으로는 서버가 먼저 연결을 여는 통신을 볼 수 없다. established 연결과 방화벽·프록시 로그를 함께 본다

8. 보안관제 관점

[정기 점검]  /root/net-audit.sh > net_$(hostname)_$(date -u +%FT%H%MZ).txt
     ↓
[판단]       NOT_ALLOWED 또는 TMP_EXE 행 → 41편 기준 확인
             허용 포트인데 EXE 경로가 평소와 다름 → 확인
     ↓
[교차 검증]  방화벽·NetFlow 로그: 해당 포트로 외부 접속이 있었는가
             외부 포트 스캔 결과와 서버 내부 결과 비교
     ↓
[조치 전]    43편 증거 수집 (소켓 정보 포함)
     ↓
[개선]       승인된 포트면 허용 목록·기준선 갱신 / 아니면 원인 조사
서버 역할허용 목록 예
웹 서버22(관리망), 80, 443
DB 서버22(관리망), 3306 (앱 서버 대역만)
관제 수집 서버22, 514(syslog), 5044(beats)

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

실수결과예방
포트 번호만 보고 정상 판단다른 프로그램이 같은 포트를 쓰는 경우 누락실행 경로 병기
TCP만 점검UDP 대기(DNS, syslog 등) 누락ss -ulnp 병행
허용 목록 없이 매번 판단사람마다 결과가 다름서버 역할별 목록 문서화
IPv6 대기 누락[::]:포트 노출 간과IPv6 결과 포함
ss -tnp state established의 열 위치 착각잘못된 필드 출력state 필터 사용 시 State 열이 빠짐을 기억

10. 실습 체크리스트

[ ] 대기 포트·바인딩 주소·소유 프로세스·허용 여부를 점검 항목으로 정리했다
[ ] ss 결과에 exe·user·플래그를 붙인 점검 스크립트를 작성했다
[ ] 허용 목록 밖 + 임시 경로 테스트 서버를 NOT_ALLOWED TMP_EXE 로 식별했다
[ ] 같은 python 이라도 실행 경로로 정상/확인 대상을 구분했다
[ ] SSH 외 연결 수를 확인하고 기록했다
[ ] 서버 역할별 허용 목록 예를 작성할 수 있다

11. 핵심 정리

  • 네트워크 점검은 포트 × 소유 프로세스 × 실행 경로 × 허용 목록 을 한 줄로 결합해 판단한다.
  • 바인딩 주소로 외부 노출 여부를 확인한다 (0.0.0.0 vs 127.0.0.1).
  • 같은 프로그램 이름이라도 실행 경로가 다르면 다른 프로세스로 취급한다.
  • 대기 포트와 함께 established 연결, UDP, IPv6를 점검한다.
  • 허용 목록은 서버 역할별로 문서화하고 기준선(42편)과 연결해 관리한다.

12. 다음 편 예고

다음 글 「45. 예약 작업(cron·at) 무결성 점검」 에서는 cron 파일의 기준 해시 를 만들고, 패키지 파일 검증(rpm -V)과 수정 시각으로 변경을 탐지한 뒤 원래 상태로 복구하는 절차를 실습한다.


참고 자료


시리즈 이동

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

0개의 댓글