2021년 12월 10일 저녁, Log4Shell(CVE-2021-44228) 취약점 뉴스가 터졌습니다. 서버에 Java와 Log4j가 깔려있다면 원격에서 임의 코드를 실행할 수 있는 CVSS 10.0 — 역대 최악의 취약점 중 하나였습니다.
그날 밤, 전국 운영 팀에서 긴급 패치가 시작됐습니다. 6개월째 "다음 분기에 패치하자"고 미뤄왔던 팀들은 그날 밤 비상 대응을 해야 했습니다. 미리 패치 파이프라인을 갖추고 있던 팀은 두 시간 안에 끝냈고, 그렇지 않은 팀은 이틀을 꼬박 밤을 새웠습니다.
패치는 귀찮은 일이 아닙니다. 미리 해두면 두 시간, 나중에 하면 이틀입니다.
- 1패치 유형(보안/버그/기능)을 분류하고 우선순위에 따라 패치 계획을 수립할 수 있다
- 2yum update --security로 보안 패치만 선택 적용하고 이력을 확인할 수 있다
- 3커널 패치 후 uname -r과 grubby로 새 커널 부팅 여부를 검증할 수 있다
- 4미들웨어(Nginx, Tomcat, JDK) 버전 업 5단계 절차를 실무에 적용할 수 있다
- 5패치 실패 시 yum history undo와 GRUB 이전 커널 선택으로 롤백할 수 있다
which yum dnf apt-get 2>/dev/null | head -3cat /etc/os-release | grep -E 'NAME|VERSION'rpm -qa | grep '^kernel-[0-9]' | sort 2>/dev/null || dpkg -l linux-image-* 2>/dev/null | grep '^ii'rpm -qa --last 2>/dev/null | head -10 || grep ' install\| upgrade' /var/log/dpkg.log 2>/dev/null | tail -10패치 관리 전략
왜 패치를 미루면 안 되는가
패치를 미루는 이유는 항상 "서비스가 안정적으로 돌고 있어서"입니다. 그 이유가 패치를 못하는 이유가 됩니다. 그런데 취약점 공격은 서비스가 잘 돌아가는 서버를 노립니다.
패치 유형 분류:
| 유형 | 설명 | 적용 시기 |
|---|---|---|
| 보안 패치 (Security) | CVE 번호 부여된 취약점 수정 | Critical·High: 72시간 이내 / Medium: 1개월 이내 |
| 버그 패치 (Bugfix) | 기능 오동작 수정 | 월별 정기 패치 주기 |
| 기능 업데이트 (Enhancement) | 새 기능 추가 | 분기별 검증 후 적용 |
보안 패치 우선순위 (CVSS 기준):
| CVSS 점수 | 등급 | 목표 적용 기한 |
|---|---|---|
| 9.0 ~ 10.0 | Critical | 48~72시간 이내 긴급 대응 |
| 7.0 ~ 8.9 | High | 7일 이내 |
| 4.0 ~ 6.9 | Medium | 30일 이내 정기 패치 |
| 0.1 ~ 3.9 | Low | 분기 정기 패치 |
패치 적용 순서 원칙:
개발계에서 먼저 적용하고 검증한 뒤 운영계로 올립니다. "개발계도 없이 운영에 바로 적용"은 패치로 인한 장애를 만드는 지름길입니다.
개발계(Dev) → 검증계(Staging) → 운영계(Prod)
24h 관찰 24h 관찰
Critical 취약점은 검증 단계를 압축하더라도 최소 개발계 → 운영계 순서는 지킵니다.
패치 한 번은 실제로 어떤 단계를 거치나 — 확인부터 검증·롤백 대비까지 6단계
"패치했다"는 한 마디 뒤에는 여러 단계가 숨어 있습니다. 취약점을 확인하고, 테스트 환경에서 검증하고, 변경 승인을 받고, 패키지를 적용하고, 재부팅이 필요한지 판단하고, 마지막으로 검증하며 롤백을 대비하는 순서입니다. 이 흐름을 건너뛰면 "적용은 했는데 서비스가 죽었다", "패치했다고 생각했는데 여전히 취약하다" 같은 사고가 납니다. 어느 단계를 생략했는지가 곧 사고의 원인이므로, 각 단계가 하는 일과 막히면 나오는 증상을 나눠 봅니다.
[CVE 공지 / 정기 업데이트]
│
① 취약점·업데이트 확인 (yum check-update --security · CVSS 등급)
│
② 테스트 환경 검증 (개발계 먼저 적용 → 서비스 정상 확인)
│
③ 변경 승인 + 백업/스냅샷 (점검 창 합의 · 롤백 지점 확보)
│
④ 패키지 적용 (yum update --security → 디스크 파일 교체)
│
⑤ 재부팅 필요 판단 (커널·glibc=재부팅 / 라이브러리=서비스 재시작)
│
⑥ 검증 + 롤백 대비 (서비스 헬스·버전 확인 → 이상 시 yum history undo)
▼
[패치 '적용' 완료 = 새 코드를 실제로 실행 중]
각 단계에서 하는 일과, 막히면 나오는 증상:
| 단계 | 하는 일 | 여기서 막히면 |
|---|---|---|
| ① 확인 | check-update --security로 대상만 선별, CVSS로 우선순위 결정 | 전체 update -y로 뭉뚱그리면 기능 변경까지 딸려와 의존성 충돌 위험 |
| ② 검증 | 개발계·통시계에 먼저 적용해 서비스가 정상 기동하는지 확인 | 검증 없이 운영 직행 → 패치 후 서비스 기동 실패를 운영에서 처음 마주침 |
| ③ 승인·백업 | 점검 창을 잡고 스냅샷·설정 백업으로 롤백 지점을 확보 | 백업 없이 진행 → 문제 발생 시 되돌릴 대상이 없음 |
| ④ 적용 | 패키지 매니저가 디스크의 파일을 새 버전으로 교체 | 공유 라이브러리 버전 충돌 → 서비스가 필요한 .so를 못 찾아 기동 실패 |
| ⑤ 재부팅 판단 | needs-restarting -r/-s로 재부팅 vs 서비스 재시작을 구분 | 커널 패치인데 재부팅 안 함 → 디스크만 새 버전, 실행 중은 옛 커널(취약 지속) |
| ⑥ 검증·롤백 | uname -r·서비스 헬스로 반영을 확인, 이상 시 즉시 롤백 | 검증 생략 → 재부팅 유발·서비스 실패를 사용자가 먼저 발견 |
즉 "패치 완료"는 rpm -q가 새 버전을 보여주는 것이 아니라, 그 코드를 실행하는 프로세스가 새 버전을 물고 있고 서비스가 정상이라는 뜻입니다. 파일 교체(④)까지만 하고 재부팅·재시작(⑤)과 헬스 검증(⑥)을 생략하면 "설치는 됐으나 적용은 안 된" 상태로 남습니다. 패치 실패는 대개 ②(검증 생략)·④(의존성 충돌)·⑤(재부팅 누락)에서 갈리므로, 적용 후 needs-restarting -s(서비스)와 -r(재부팅), 그리고 systemctl status로 서비스 상태까지 확인해야 ⑥이 끝납니다.
yum/apt 패치 실무
보안 패치 확인과 선택 적용
패치는 적용하는 것만큼 "무엇을 적용하는가"를 아는 것이 중요합니다. yum update -y로 전체를 한 번에 올리면 보안 취약점뿐 아니라 기능 변경까지 따라오고, 의존 라이브러리 버전이 바뀌면서 서비스가 중단될 수 있습니다. 보안 패치만 선별해 적용하고 변경 이력을 남기는 것이 운영 안정성과 감사 대응을 동시에 만족하는 방법입니다. 패치 전 --assumeno로 변경 범위를 먼저 확인하는 습관이 예상치 못한 사고를 막습니다.
확대
yum update -y로 전체 업데이트를 걸었다가 PHP 버전이 올라가면서 레거시 애플리케이션이 기동 실패한 경우가 있습니다. 보안 패치만 선택해서 적용하고, 변경 범위를 먼저 확인한 뒤 진행해야 이 사고를 막을 수 있습니다. 패치 이력이 남아있어야 "이 장애가 패치 때문인지"를 나중에 추적할 수 있습니다.
패치를 적용하기 전에 무엇이 바뀌는지 먼저 확인합니다. 확인 없이 업데이트하면 예상치 못한 패키지가 함께 올라가는 경우가 있습니다.
# 업데이트 가능한 패키지 전체 목록
sudo yum check-update
# 보안 패치만 확인 (Security Advisory 분류된 것만)
sudo yum check-update --security
# Ubuntu/Debian 계열
sudo apt update
apt list --upgradable 2>/dev/null | grep -i security
# 특정 패키지의 현재 설치 버전 확인
rpm -qa | grep openssl
# openssl-1.1.1k-9.el8.x86_64
# openssl-libs-1.1.1k-9.el8.x86_64
# 설치 가능한 버전 목록 (yum)
sudo yum list --showduplicates openssl | tail -10
보안 패치 적용:
# dry-run 먼저 — 실제 설치 없이 변경 목록만 확인
sudo yum update --security --assumeno
# 실제 보안 패치 적용
sudo yum update --security -y
# 특정 패키지만 업데이트 (영향 범위 최소화)
sudo yum update openssl openssl-libs -y
# Ubuntu/Debian 계열 보안 패치만
sudo apt-get upgrade -y --with-new-pkgs $(apt-get -s upgrade 2>/dev/null | grep "^Inst" | grep -i security | awk '{print $2}' | tr '\n' ' ')
패치 이력 확인:
# yum 패치 이력 (날짜별 트랜잭션 목록)
sudo yum history list | head -20
# ID | Command | Date | Action | Altered
# 15 | update --security -y | 2024-12-10 23:14 | Update | 8
# 특정 트랜잭션 상세 내용
sudo yum history info 15
# 로그 파일로 확인
sudo grep -i "updated\|installed" /var/log/yum.log | tail -30
# apt 패치 이력 (Debian 계열)
grep "upgrade\|install" /var/log/apt/history.log | tail -30
1. 현재 OS 버전 및 패치 이력 확인
cat /etc/os-release | grep -E '^NAME|VERSION_ID' && uname -r2. 적용 가능한 보안 업데이트 목록 확인 (dry-run)
sudo apt-get -s upgrade 2>/dev/null | grep -E '^Inst|upgraded' | head -15 || sudo yum check-update --security 2>/dev/null | head -15 || echo '패키지 매니저 환경 확인 필요'3. 패치 이력 확인
grep 'upgrade\|install' /var/log/apt/history.log 2>/dev/null | tail -10 || rpm -qa --last 2>/dev/null | head -10 || echo '패치 이력 없음'- uname -r 결과를 먼저 확인 — 실행 중인 커널 버전. apt-get -s upgrade | grep linux-image 패키지가 있으면 새 커널이 설치됐지만 재부팅 안 된 상태
- apt-get -s upgrade 결과에서 "security" 포함 패키지 수가 10개 이상이면 장기 패치 미적용 서버 — 보안 감사 대상이며 CVSS 9.0 이상 취약점이 포함될 가능성 높음
- 패치 이력 파일(/var/log/apt/history.log)이 6개월 이상 비어있고 uname -r 버전이 2년 이상 된 것이면 — 커널 패치와 패키지 패치 모두 미적용 상태로 즉시 패치 계획 수립 필요
커널 패치 절차
커널 패치는 재부팅이 필요하다
커널 패치를 적용했는데 uname -r로 확인하니 여전히 이전 버전이었습니다. 패치는 설치됐지만 재부팅을 안 했기 때문입니다. 재부팅 계획을 세우지 않고 패치를 진행하면 패치가 됐다고 생각하는데 실제로는 취약한 상태가 지속됩니다. 반대로 재부팅 후 새 커널에서 드라이버 호환성 문제로 서버가 부팅 불가 상태가 되면, 콘솔 접근 수단이 없으면 복구 방법이 없어집니다. 재부팅 없이 커널 패치를 적용하는 kpatch(라이브 패치)도 있지만, 모든 취약점에 적용할 수 있는 것은 아닙니다.
확대
커널 패치 절차:
# 현재 실행 중인 커널 버전
uname -r
# 5.14.0-362.8.1.el9_3.x86_64
# 설치된 커널 전체 목록
rpm -qa | grep '^kernel-[0-9]' | sort
# kernel-5.14.0-362.8.1.el9_3.x86_64
# kernel-5.14.0-427.13.1.el9_4.x86_64 ← 이미 설치된 최신 버전
# 커널 업데이트 (재부팅 필요)
sudo yum update kernel -y
# 다음 부팅에서 사용할 기본 커널 확인
sudo grubby --default-kernel
# /boot/vmlinuz-5.14.0-427.13.1.el9_4.x86_64
# 사용 가능한 커널 목록
sudo grubby --info=ALL | grep "^kernel"
재부팅 후 새 커널 확인:
# 재부팅
sudo reboot
# 재접속 후 새 커널 버전 확인
uname -r
# 5.14.0-427.13.1.el9_4.x86_64 ← 새 커널로 올라왔으면 성공
# 기본 커널 경로와 실행 중인 커널 일치 여부 확인
sudo grubby --default-kernel
kpatch — 재부팅 없는 커널 패치:
# kpatch 설치 (RHEL/CentOS 계열)
sudo yum install kpatch kpatch-dnf -y
# 적용 가능한 라이브 패치 목록 확인
sudo kpatch list
# kpatch는 일부 취약점에만 제공되며, 커널 패치의 완전한 대체재가 아닙니다.
# Critical 커널 취약점은 결국 재부팅 패치가 필요합니다.
이전 커널로 롤백 (GRUB 사용):
재부팅 후 새 커널에서 문제가 생기면 GRUB 메뉴에서 이전 커널을 선택합니다.
# 물리 서버/KVM: 재부팅 시 GRUB 메뉴에서 방향키로 이전 커널 선택
# 클라우드 VM: 콘솔/직렬 포트 접근 후 GRUB 메뉴 선택
# CLI로 기본 커널 변경 (이전 커널로 롤백)
sudo grubby --set-default /boot/vmlinuz-5.14.0-362.8.1.el9_3.x86_64
# 변경 확인
sudo grubby --default-kernel
# /boot/vmlinuz-5.14.0-362.8.1.el9_3.x86_64
sudo reboot
1. 현재 실행 중인 커널 확인
uname -r && echo '---' && ls /boot/vmlinuz-* 2>/dev/null | sort -V | tail -32. 재부팅 없이 적용된 보안 패치 확인 (가능 여부)
rpm -qa | grep kpatch 2>/dev/null || apt list --installed 2>/dev/null | grep livepatch || echo '라이브 패치 미설치'3. 재부팅 필요 여부 확인
needs-restarting -r 2>/dev/null || (ls /var/run/reboot-required 2>/dev/null && cat /var/run/reboot-required.pkgs 2>/dev/null) || echo '재부팅 불필요 또는 확인 도구 없음'- uname -r(실행 중 커널)과 ls /boot/vmlinuz-*의 최신 파일명을 비교 — 숫자가 다르면 새 커널이 설치됐지만 재부팅 안 된 상태. 패치 효과가 아직 미적용
- /var/run/reboot-required 파일이 있으면 Ubuntu에서 재부팅 필요 — 이 파일이 생성된 날짜(stat 명령)와 패치 날짜가 일치하면 해당 패치로 인한 재부팅 대기
- needs-restarting -r가 "Reboot is required"를 출력하면 RHEL 계열 재부팅 필요 상태 — needs-restarting -s로 서비스별 재시작 필요 목록도 함께 확인해야 전체 적용 완료
미들웨어 버전 업 절차
확대
Nginx / Tomcat / JDK 버전 업 5단계
JDK 8에서 JDK 11로 올렸더니 javax 패키지를 찾을 수 없다는 오류와 함께 Tomcat이 기동 실패했습니다. Release Notes를 읽지 않고 업그레이드했기 때문입니다. Nginx 버전 업 후 일부 ssl_ 지시어가 인식되지 않아 설정 검증에서 실패한 경우도 마찬가지입니다. 버전 업은 테스트 환경에서 충분히 검증하고 롤백 계획을 준비한 뒤에 운영에 적용해야 합니다. 미들웨어 버전 업은 OS 패키지 패치보다 영향 범위가 큽니다. 설정 파일 문법이 바뀌거나, 애플리케이션이 의존하는 API가 제거되면 서비스가 기동 자체를 못 합니다.
5단계 버전 업 절차:
1단계 — Release Notes 확인 (가장 중요)
# 현재 버전 확인
nginx -v
# nginx version: nginx/1.18.0
java -version
# openjdk version "1.8.0_392"
# Release Notes에서 반드시 확인할 항목:
# - Deprecated → Removed 항목 (기존 설정이 오류가 되는 것)
# - 기본값 변경 사항
# - 의존 라이브러리 최소 버전 변경
2단계 — 현재 설정 파일 백업
# Nginx 설정 전체 백업
sudo tar czf /opt/backup/nginx-conf-$(date +%Y%m%d).tar.gz /etc/nginx/
# Tomcat 설정 백업
sudo tar czf /opt/backup/tomcat-conf-$(date +%Y%m%d).tar.gz /opt/tomcat/conf/
# JDK 환경변수 기록
echo "JAVA_HOME=$JAVA_HOME" > /opt/backup/java-env-$(date +%Y%m%d).txt
java -version >> /opt/backup/java-env-$(date +%Y%m%d).txt 2>&1
# 현재 패키지 목록 스냅샷 (RPM 환경)
rpm -qa | sort > /opt/backup/rpm-list-$(date +%Y%m%d).txt
3단계 — 개발계에서 먼저 버전 업 및 검증
# Nginx 버전 업 (yum 환경)
sudo yum update nginx -y
# 설정 파일 문법 검사
sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# 문법 오류 시 즉시 확인
sudo nginx -T 2>&1 | grep -i error
4단계 — 서비스 재기동 및 검증 체크리스트
# 서비스 재기동
sudo systemctl restart nginx
sudo systemctl status nginx
# 응답 확인
curl -I http://localhost/health
# HTTP/1.1 200 OK
# 오류 로그 확인 (재기동 후 1분간)
sudo tail -f /var/log/nginx/error.log
5단계 — 이전 버전 바이너리 보관 (롤백 준비)
# 이전 버전 바이너리 보존 (yum은 자동 보존)
sudo yum list installed nginx
sudo yum list --showduplicates nginx | grep nginx
# 이전 버전으로 다운그레이드 (필요 시)
sudo yum downgrade nginx-1.18.0 -y
sudo systemctl restart nginx
JDK 버전 업 시 특별 주의사항:
# JDK 8 → 11 전환 시 javax → jakarta 패키지 변경으로 기동 실패 사례
# 애플리케이션 pom.xml 또는 build.gradle의 의존성 버전도 함께 확인 필요
# 현재 사용 중인 JDK 확인
alternatives --list | grep java
# java auto /usr/lib/jvm/java-11-openjdk-11.0.21.0.9-2.el9.x86_64/bin/java
# JDK 여러 버전 공존 설치 후 전환
sudo alternatives --config java
실습
1. 현재 설치된 패키지 버전 확인
rpm -qa --last 2>/dev/null | head -20 || dpkg -l | grep '^ii' | tail -202. 보안 업데이트 목록 확인
sudo yum check-update --security 2>/dev/null || sudo apt-get -s upgrade 2>/dev/null | grep -i security | head -203. dry-run으로 변경 범위 미리 확인
sudo yum update --security --assumeno 2>/dev/null | tail -20 || sudo apt-get -s upgrade 2>/dev/null | grep '^Inst' | grep -i security | head -204. 보안 패치 실제 적용
sudo yum update --security -y 2>/dev/null | tail -20 || echo 'apt 환경: sudo apt-get upgrade -y 실행 필요'5. 패치 이력 확인
sudo yum history list 2>/dev/null | head -10 || grep 'upgrade\|install' /var/log/apt/history.log 2>/dev/null | tail -10- yum check-update --security 결과를 먼저 본다 — 출력이 없으면 보안 패치 최신 상태. kernel 패키지가 목록에 있으면 재부팅 계획을 별도 수립해야 패치 적용됨
- dry-run(--assumeno) 출력에서 업데이트 패키지 수가 50개 이상이면 패치 적용 시간 30분 이상 예상 — 점검 시간 창(maintenance window)이 충분한지 먼저 확인
- yum history list에 방금 트랜잭션 ID가 없으면 패치 미적용 — 패치 후 systemctl status nginx tomcat 으로 서비스 상태 확인. 서비스가 failed 상태면 즉시 yum history undo <ID>로 롤백
1. 패치 이력 목록 조회
sudo yum history list 2>/dev/null | head -15 || grep 'upgrade' /var/log/apt/history.log 2>/dev/null | tail -102. 특정 트랜잭션 상세 내용 확인
sudo yum history list 2>/dev/null | awk 'NR==2{print $1}' | xargs -I{} sudo yum history info {} 2>/dev/null | head -30 || echo 'yum history 미지원 환경'3. 설치된 이전 버전 목록 확인
sudo yum list --showduplicates openssl 2>/dev/null | grep openssl | head -10 || apt-cache showpkg openssl 2>/dev/null | grep 'Versions' -A 10 | head -104. 다운그레이드 dry-run 확인 (실제 실행 없음)
sudo yum downgrade --assumeno openssl 2>/dev/null | tail -15 || echo 'Ubuntu 환경에서는 apt-get install openssl=<버전> 으로 다운그레이드 가능'- yum history list 출력에서 트랜잭션 ID(첫 번째 열)를 먼저 확인 — ID가 있어야 undo 가능. ID가 없으면 yum이 아닌 다른 방법으로 설치된 것으로 rpm -e로 직접 제거해야 함
- yum history info <ID>의 Updated 항목 수가 예상과 다르면 의존성 패키지까지 함께 업데이트된 것 — 롤백 시 해당 패키지 수만큼 다운그레이드됨을 인지하고 진행
- yum list --showduplicates에서 @base나 @updates가 붙은 것이 현재 설치 버전 — 다운그레이드 dry-run에 "Nothing to do"가 나오면 이전 버전이 저장소에 없거나 이미 최저 버전으로 rpm 직접 다운로드 필요
트러블슈팅
심화 — 패치는 '설치'가 아니라 '적용'까지가 끝이다
심화: 파일 교체와 메모리 적용의 간극 — 왜 재시작해야 진짜 패치인가
앞에서 "커널 패치는 재부팅이 필요하다"까지 봤습니다. 한 단계 더 들어가면, 재부팅이 필요한 건 커널만이 아닙니다. 문제의 뿌리는 패키지 매니저가 하는 일이 '디스크의 파일 교체'까지라는 점입니다.
- 패키지 매니저는 디스크만 바꿉니다:
yum update openssl은 디스크의libssl.so를 새 버전으로 갈아끼웁니다. 하지만 이미 실행 중이던 프로세스는 기동할 때 옛.so를 메모리에 로드해 뒀고, 리눅스는 그 옛 파일을 (디스크에서 지워져도) 프로세스가 놓을 때까지 붙잡아 둡니다. 그 프로세스에겐 여전히 옛 코드가 돕니다 — 파일명 뒤에(deleted)가 붙은 매핑이 바로 그 증거입니다. - 그래서 경계가 셋입니다: ① 공유 라이브러리(openssl·glibc 일부) 패치 → 그 라이브러리를 쓰는 서비스만 재시작(
needs-restarting -s). ② 커널·glibc 핵심·systemd 패치 → 재부팅(needs-restarting -r). ③ 재부팅을 미뤄야 하는 커널 CVE → kpatch/livepatch. - 라이브 패치의 한계: livepatch는 실행 중 커널 함수의 진입점을 후킹해 새 함수로 우회하는 방식입니다. 코드 로직 결함은 고칠 수 있지만, 자료구조 레이아웃이나 초기화 경로가 바뀌는 패치는 우회로 표현할 수 없어 적용 대상이 아닙니다. 그래서 Critical 커널 취약점은 결국 점검 시간을 잡아 재부팅해야 합니다.
정리하면 패치의 완료 조건은 "rpm -q가 새 버전을 보여준다"가 아니라 "그 코드를 실행하는 모든 프로세스가 새 버전을 물고 있다"입니다. 그래서 성숙한 패치 SOP는 마지막에 needs-restarting -s(재시작 필요 서비스)와 -r(재부팅 필요)을 반드시 확인하는 단계를 둡니다.
상황: openssl 보안 패치를 정상 적용했습니다. rpm -q openssl은 새 버전이고, yum history에도 트랜잭션이 기록돼 있습니다. 그런데 다음 날 정기 취약점 스캔(Nessus 등)이 어제 막은 것과 같은 CVE를 여전히 리포트합니다. 담당자는 "패치가 안 됐나?" 싶어 다시 rpm -q를 찍어 보지만 버전은 분명 새것입니다.
원인: 디스크의 libssl.so는 새 버전으로 교체됐지만, 패치 전부터 떠 있던 nginx·sshd·httpd 프로세스가 기동 당시 로드한 옛 libssl.so를 메모리에서 계속 실행 중입니다. 그 옛 파일은 디스크에서 교체되며 삭제 표시가 붙었지만, 프로세스가 붙잡고 있어 사라지지 않습니다. 스캐너는 실행 중 프로세스가 실제로 물고 있는 라이브러리를 보므로, 디스크는 패치돼도 메모리가 옛것이면 취약점을 그대로 잡아냅니다. 파일은 패치됐고 메모리는 안 됐습니다.
진단: sudo needs-restarting -s로 재시작이 필요한 서비스 목록을 봅니다. 더 확실히는 sudo lsof +c0 2>/dev/null | grep -iE 'libssl|libcrypto' | grep -i deleted — 출력에 (deleted) 표시된 라이브러리를 물고 있는 PID가 곧 '옛 코드를 실행 중인 범인'입니다. 이 목록이 스캐너가 지목한 서비스와 일치하면 확정입니다.
해결: 해당 프로세스를 재시작합니다(sudo systemctl restart nginx sshd). glibc처럼 거의 모든 프로세스가 물고 있는 라이브러리가 바뀐 경우엔 서비스 하나씩 재시작하는 것보다 점검 시간을 잡아 재부팅이 현실적입니다. 재발 방지책은 패치 SOP 마지막에 needs-restarting -s(서비스)와 -r(재부팅) 확인을 못 박아, '설치'에서 멈추지 않고 '적용'까지 끝내는 것입니다(취약점 조치와 보안 심사 대응 실무).
실제 업무에서 이 지식이 쓰이는 상황:
금융권/공공기관의 분기별 패치 관리 프로세스:
금융권에서는 보안 취약점 패치를 임의로 적용할 수 없습니다. CVE 공시 → 영향도 분석 → 패치 계획 수립 → 정보보호팀 승인 → 적용 → 결과 보고서 작성의 단계를 거칩니다.
# 실무에서 CVE 대응 시 가장 먼저 하는 작업
# 1. 영향 받는 패키지가 설치됐는지 확인
rpm -qa | grep -i log4j
# log4j-2.14.1-2.el8.noarch ← 취약 버전 설치됨
# 2. 해당 패키지를 사용하는 서비스 식별
rpm -q --whatrequires log4j 2>/dev/null
# 또는
find /opt /app -name "log4j*.jar" 2>/dev/null
# 3. 패치 계획 문서화
# - 현재 버전: log4j-2.14.1 (CVE-2021-44228 영향 받음)
# - 패치 버전: log4j-2.17.1 이상
# - 적용 서버 목록
# - 점검 시간 (서비스 중단 여부)
# - 롤백 절차
패치 후 서비스 검증 체크리스트 (실무 표준):
# 패치 직후 확인 항목 (자동화 스크립트 예시)
echo "=== 패치 후 서비스 상태 점검 ==="
systemctl is-active nginx tomcat postgresql
echo ""
echo "=== 주요 포트 응답 확인 ==="
ss -tlnp | grep -E ':80|:443|:8080|:5432'
echo ""
echo "=== 최근 5분 에러 로그 ==="
journalctl -p err --since "5 min ago" --no-pager | tail -20
패치 관리는 "하는 것"과 "기록하는 것"이 세트입니다. 어떤 서버에 언제 무엇을 패치했는지 이력이 없으면 감사(Audit) 때 패널티를 받습니다. yum history와 변경 관리 티켓을 항상 함께 남깁니다.
명령어·단축키 빠른 참조
이 모듈에서 다룬 패치 확인·적용·검증·롤백 명령을 실전 옵션과 함께 모았습니다. "예" 열의 조합을 그대로 써도 됩니다.
| 명령어/단축키 | 용도 | 자주 쓰는 예 |
|---|---|---|
yum check-update --security | 보안 패치만 목록 확인(설치 안 함) | Debian: apt list --upgradable | grep -i security |
yum update --security | 보안 패치만 선택 적용 | dry-run --assumeno, 실제 적용 -y |
yum update kernel -y | 커널 업데이트(재부팅 필요) | 적용 후 reboot → uname -r 재확인 |
rpm -qa --last | head | 최근 설치·업데이트 이력 | 현재 버전: rpm -qa | grep openssl |
yum history list / info N | 패치 트랜잭션 이력 조회 | 롤백용 트랜잭션 ID 확인 |
yum history undo N | 트랜잭션 통째로 롤백 | 특정 패키지만: yum downgrade openssl |
uname -r | 실행 중 커널 버전 | 설치 커널과 다르면 재부팅 미반영 |
grubby --default-kernel | 다음 부팅 기본 커널 확인 | 롤백 grubby --set-default /boot/vmlinuz-… |
needs-restarting -r | 재부팅 필요 여부 | 서비스만 재시작 대상은 needs-restarting -s |
lsof +c0 | grep -i deleted | 옛 라이브러리 물고 있는 프로세스 | 패치 후 스캐너 재검출 원인 추적 |
systemctl restart nginx | 패치된 .so 반영 위해 서비스 재시작 | 라이브러리 패치의 실제 '적용' 단계 |
nginx -t | 미들웨어 버전 업 후 설정 문법 검사 | JDK 전환은 alternatives --config java |
관련 모듈로 더 깊이:
- 취약점 조치와 보안 심사 대응 실무 — 패치 이력과 조치 확인서가 보안 감사 대응에서 쓰이는 방식
- 파일/설정/DB/인증서 롤백 절차와 판단 기준 — 패치 후 문제 발생 시 안전하게 되돌리는 절차
- 작업계획서 작성과 변경관리 체크리스트 — 패치 작업을 변경 관리 작업계획서로 정리하는 법
다음 모듈에서는 WEB/WAS/DB 망 분리와 이중화 구조 설계를 다룹니다.