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 취약점은 검증 단계를 압축하더라도 최소 개발계 → 운영계 순서는 지킵니다.
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
- uname -r 결과가 최신 커널 버전인지 확인 — 커널 업데이트 후 재부팅이 필요
- apt-get -s upgrade 결과에 보안 업데이트 패키지가 있으면 패치 대상
- 패치 이력이 없다면 이 서버는 장기간 패치 미적용 상태 — 보안 감사 대상
커널 패치 절차
커널 패치는 재부팅이 필요하다
커널 패치를 적용했는데 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
- uname -r과 ls /boot/vmlinuz-* 결과가 다르면 새 커널 설치 후 재부팅 미완료
- /var/run/reboot-required 파일이 있으면 Ubuntu에서 재부팅이 필요한 상태
- needs-restarting 명령어가 'Reboot is required'를 출력하면 RHEL 계열에서 재부팅 필요
미들웨어 버전 업 절차
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
실습
- yum check-update --security 결과에 패키지가 없으면 보안 패치가 최신 상태입니다
- dry-run 결과에서 kernel 패키지가 포함된 경우 재부팅 계획을 별도로 수립해야 합니다
- yum history list에 방금 실행한 트랜잭션 ID가 나타나는지 확인합니다
- 패치 후 주요 서비스(nginx, tomcat 등) 상태를 systemctl status로 확인합니다
- 재부팅이 필요한 패치가 있다면 needs-restarting 명령으로 확인합니다: sudo needs-restarting -r
- yum history list 출력에서 트랜잭션 ID(첫 번째 열)가 보이면 해당 ID로 undo가 가능합니다
- yum history info <ID>에서 Updated 항목이 실제로 변경된 패키지입니다
- yum list --showduplicates에서 @base 또는 @updates가 붙은 것이 현재 설치된 버전입니다
- 다운그레이드 dry-run에 'Nothing to do'가 나오면 이미 최저 가능 버전이거나 이전 버전이 없는 것입니다
트러블슈팅
실제 업무에서 이 지식이 쓰이는 상황:
금융권/공공기관의 분기별 패치 관리 프로세스:
금융권에서는 보안 취약점 패치를 임의로 적용할 수 없습니다. 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와 변경 관리 티켓을 항상 함께 남깁니다.
다음 모듈에서는 WEB/WAS/DB 망 분리와 이중화 구조 설계를 다룹니다.