사내에서 사용하는 EKS Worker EC2 AMI 를 AL2 에서 AL2023 으로 마이그레이션 하면서 발생한 일련의 부팅 실패와 빌드 실패를 디버깅한 기록이다. AMI 빌드는 Packer + Ansible 로 진행했고, base AMI 가 CIS hardening 적용본이라는 점이 모든 문제의 출발점이었다.
빌드/부팅 흐름:
[CIS-hardened AL2023 base AMI]
↓ Packer + Ansible provisioners
[install-worker.sh, configure-selinux.sh, cleanup.sh, ...]
↓ AMI snapshot
[EC2 launch with user-data (NodeConfig YAML)]
↓ first boot
nodeadm-boot-hook → nodeadm-config → nodeadm-run → kubelet → join EKS
vanilla EKS AMI 빌드 스크립트는 표준 AL2023 의 권한과 umask 를 가정하지만 CIS hardening 이 이 가정을 곳곳에서 깨뜨린다.
CIS Hardening 부작용 정리
이번 마이그레이션에서 마주친 CIS hardening 효과들과 실제로 부딪힌 증상:
umask 022 → 027
표준 AL2023 의 umask 는 022 라서 새 파일은 0644, 새 디렉토리는 0755 로 생성된다. CIS 는 umask 를 027 로 바꿔 새 파일이 0640, 새 디렉토리가 0750 으로 생성되도록 한다.
→ install-worker.sh 가 다운받아 /usr/bin/ 으로 옮긴 kubelet 바이너리가 0640 이라 ec2-user 가 실행 불가했다. cleanup.sh 가 touch /etc/machine-id 로 빈 파일을 만들 때도 0640 으로 생성되어 dbus-broker 가 못 읽었다.
/etc/machine-id 권한
표준 AL2023 은 0644. CIS 환경에서는 cleanup.sh 의 rm + touch 조합 + umask 027 효과로 0640 이 된다.
→ dbus-broker.service 가 부팅 시 EACCES 로 실패. 부팅 체인 전체가 무너졌다 (이 문서의 가장 큰 디버깅 케이스).
/etc/eks/ 디렉토리 권한
표준 AL2023 EKS AMI 는 0755 root:root. CIS 환경에서는 install-worker.sh 가 mkdir 할 때 umask 027 이 적용되어 0750 으로 생성된다.
→ configure-selinux.sh 가 ec2-user 로 실행되면서 matchpathcon -V /etc/eks/* 의 glob 확장이 실패 (“Permission denied”). ec2-user 는 root:root 0750 디렉토리의 항목을 읽을 수 없다.
containerd socket 접근 권한
표준 AL2023 EKS AMI 에서는 ec2-user 가 /run/containerd/containerd.sock 에 접근 가능하다 (특정 그룹 멤버십 또는 socket 권한). CIS 환경에서는 root 만 접근 가능.
→ cache-pause-container.sh 가 cache-pause-container -i ... 를 sudo 없이 호출하는데 ec2-user 권한으로는 containerd 와 통신 불가.
audit framework immutable mode
표준 환경에서는 auditctl 로 런타임에 룰 추가/삭제 가능. CIS 는 -e 2 플래그로 immutable mode 를 강제한다.
→ dbus-broker EPERM 의 syscall 을 audit 으로 잡으려 했으나 새 룰을 추가할 수 없어 실패. 결국 strace 로 우회.
문제 1: dbus-broker.service 시작 실패
증상
SSH 와 SSM 이 안 떠서 인스턴스에 접근할 수 없었다. EC2 콘솔의 System Log 부터 확인.
EC2 System Log
부팅 도중 dbus-broker.service 시작 실패가 보였다.
Booting 'Amazon Linux (6.12.79-101.147.amzn2023.x86_64) 2023'
RETBleed: WARNING: Spectre v2 mitigation leaves CPU vulnerable to RETBleed attacks, data leaks possible!
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
...
ena 0000:00:05.0: Elastic Network Adapter (ENA) v2.16.1g
ena 0000:00:05.0: ENA controller version: 0.0.1 implementation version 1
ena 0000:00:05.0: Elastic Network Adapter (ENA) found at mem c0400000, mac addr 06:b4:29:11:48:c5
ena 0000:00:05.0 ens5: renamed from eth0
nvme nvme0: using unchecked data buffer
[FAILED] Failed to start dbus-broker.service - D-Bus System Message Bus.
...
ENA 인식까지는 정상인데 그 다음 dbus-broker 가 실패하면서 부팅 체인이 무너진다.
EC2 Serial Console 로 진입
SSH 가 없으니 EC2 Serial Console 로 root 로그인 (packer 빌드 시 임시로 chpasswd 로 password 박아둠). 거기서 journalctl -u dbus-broker --no-pager 실행:
systemd[1]: Starting dbus-broker.service - D-Bus System Message Bus...
dbus-broker-launch[1970]: ERROR launcher_run_child @ ../src/launch/launcher.c +325: Permission denied
systemd[1]: Started dbus-broker.service - D-Bus System Message Bus.
dbus-broker-launch[1946]: ERROR launcher_run @ ../src/launch/launcher.c +1411: Broken pipe
dbus-broker-launch[1946]: run @ ../src/launch/main.c +152
dbus-broker-launch[1946]: main @ ../src/launch/main.c +178
dbus-broker-launch[1946]: Exiting due to fatal error: -32
systemd[1]: dbus-broker.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: dbus-broker.service: Failed with result 'exit-code'.
systemd[1]: Starting dbus-broker.service - D-Bus System Message Bus...
dbus-broker-launch[1978]: ERROR launcher_run_child @ ../src/launch/launcher.c +325: Permission denied
dbus-broker-launch[1973]: ERROR service_add @ ../src/launch/service.c +989: Transport endpoint is not connected
dbus-broker-launch[1973]: launcher_add_services @ ../src/launch/launcher.c +804
dbus-broker-launch[1973]: launcher_run @ ../src/launch/launcher.c +1415
dbus-broker-launch[1973]: Exiting due to fatal error: -107
...반복...
systemd[1]: dbus-broker.service: Start request repeated too quickly.
systemd[1]: dbus-broker.service: Failed with result 'exit-code'.
systemd[1]: Failed to start dbus-broker.service - D-Bus System Message Bus.
첫 부팅에 systemd-machine-id-setup 이 빈 파일에 새 ID 를 씀. 이때 O_CREAT|O_RDWR 로 여는데 파일이 이미 존재하면 mode 인자가 무시되어 기존 모드 0640 보존
dbus-broker 는 dbus 유저로 실행되어 root:root 0640 파일을 못 읽음
해결: cleanup.sh 끝에 chmod 0644 추가
문제가 만들어지는 위치에서 즉시 보정.
sudo touch /etc/machine-id+# CIS-hardened base AMI sets umask 027, so the touch above creates the file+# with mode 0640. systemd-machine-id-setup writes the new ID via O_RDWR on+# the existing file at first boot, preserving this mode -- which makes the+# file unreadable to the dbus user and breaks dbus-broker. Force 0644.+sudo chmod 0644 /etc/machine-id
systemd-machine-id-setup 이 첫 부팅에 빈 파일에 ID 를 쓸 때 O_RDWR 로 기존 파일을 여는 거라 모드는 보존된다 (POSIX open(O_CREAT) 동작 — 파일이 이미 있으면 mode 인자 무시).
이 chmod 는 cleanup.sh 의 rm -rf /etc/machine-id + touch /etc/machine-id 에 의해 무효화된다. 파일이 삭제되고 다시 만들어지면서 umask 027 이 적용되어 0640 으로 돌아간다. cleanup.sh 의 touch 이후에 chmod 가 와야 한다.
매 부팅 sysinit.target 단계에서 권한을 강제하므로, 누군가 권한을 바꿔도 자동 복구된다는 장점이 있다. 다만 이번 케이스는 한 번 정해지면 바뀌지 않으므로 cleanup.sh 의 단순 chmod 가 더 직접적이다.
문제 2: kubelet TLS cipher suite
증상
dbus 가 살아나고 nodeadm 까지 통과한 뒤 kubelet 이 무한 재시작.
kubelet[]: run.go:72] "command failed" err="failed to construct kubelet dependencies:
Cipher suite TLS_RSA_WITH_AES_128_GCM_SHA256 not supported or doesn't exist"
원인
custom ansible provisioner 가 /etc/eks/nodeadm.d/kubelet-config.yaml 로 떨어뜨리는 tlsCipherSuites 목록에 deprecated cipher 포함:
sudo bash -c 으로 root 가 glob 을 확장하도록 변경. 권한이 없는 디렉토리에서 발생하는 stderr 는 2>/dev/null 로 무시하고, set -o pipefail 환경에서 grep 이 실패해도 빌드가 깨지지 않도록 || true 추가.
cache-pause-container.sh
두 가지 문제. /usr/bin/cache-pause-container 가 umask 027 로 0640 으로 설치되어 실행 권한이 없고, ec2-user 가 containerd socket 에 접근할 수 없어 sudo 없이는 호출도 안 된다.
set -o nounset set -o errexit set -o pipefail+sudo chmod 0755 "/usr/bin/cache-pause-container"+ sudo systemctl start containerd-cache-pause-container -i ${PAUSE_CONTAINER_IMAGE}+sudo cache-pause-container -i ${PAUSE_CONTAINER_IMAGE} sudo systemctl stop containerd
install-worker.sh
세 가지 문제.
/usr/bin/imds 가 umask 027 로 0640 으로 설치되어 사용 직전에 chmod 0755 필요.
### isolated regions can't communicate to awscli.amazonaws.com so installing awscli through dnf-+sudo chmod 0755 "/usr/bin/imds" PARTITION=$(imds /latest/meta-data/services/partition)
AWS CLI 설치 시 vanilla 가 --bin-dir /bin/ 옵션을 쓰는데, hardened 환경에서는 생성된 심볼릭 링크와 설치 경로의 권한이 깨져 aws --version 도 동작하지 않았다. default 경로로 설치 후 수동 chmod + 심볼릭 링크 재구성으로 해결.
sudo touch /etc/machine-id+# CIS-hardened base AMI sets umask 027, so the touch above creates the file+# with mode 0640. systemd-machine-id-setup writes the new ID via O_RDWR on+# the existing file at first boot, preserving this mode -- which makes the+# file unreadable to the dbus user and breaks dbus-broker. Force 0644.+sudo chmod 0644 /etc/machine-id
디버깅에서 배운 것
EPERM vs EACCES 구분
EACCES (Permission denied): 파일 모드/ACL 에 의한 거부 (stat 의 mode bits)
EPERM (Operation not permitted): 보안 layer 에 의한 거부 (SELinux, seccomp, capability, IMA, lockdown 등)
man errno 기준 다른 의미인데 application 에서 strerror 로 출력하면 비슷하게 보일 수 있다. 반드시 strace 등으로 raw errno 를 확인해야 한다.
Serial Console 디버깅 패턴
SSH 와 SSM 안 뜬 상태에서 EC2 Serial Console 만 가능한 상황:
launch template 의 자동 종료 (instance failed health check) 때문에 시간 제한이 있다 → EKS managed node group 으로 띄우지 말고 단독 EC2 launch template 으로 띄워서 시간 확보
root 로그인이 가능해야 하므로 packer 에 임시로:
"echo 'root:temppass123' | sudo chpasswd"
네트워크가 안 떠서 dnf install 불가 → strace 등 디버깅 도구는 빌드 시점에 미리 설치:
"sudo dnf install -y strace"
dbus 가 죽었을 때 망가지는 것들
systemd-networkd (hotplug 알림)
systemd-logind
systemctl, networkctl, loginctl 모두 hang 또는 timeout
IMDS 접근 불가 (네트워크 미구성)
tmpfiles.d 의 적합한 사용처
매 부팅 보정이 필요한 경우 (외부 코드가 설정을 바꿀 수 있는 경우)
bake-time 변경이 cleanup.sh 등 후속 단계에서 무효화되는 경우
단순 일회성 설정이면 그냥 packer/provisioner 안에서 처리하는 게 깔끔하다
Kubernetes TLS Cipher Suite Deprecation
Kubernetes 1.30+ 의 kubelet 은 forward secrecy 가 없는 TLS cipher 들을 더 이상 지원하지 않는다.
거부되는 것: TLS_RSA_*
안전한 것: TLS_ECDHE_*
OS 튜닝 (sysctl·limits)
AMI 빌드 시 Ansible 로 박은 OS 한계 노브 3 종. EKS Worker node 가 컨테이너·multi-conn·고대역 트래픽을 안정적으로 처리하도록 디폴트보다 큰 값으로 명시.
소켓 receive buffer 의 절대 상한. 디폴트 ~208 KB → 약 125 배 증가.
적용 배경 = 노드 DaemonSet 으로 뜬 Datadog Agent (DogStatsD) 가 high-throughput burst 메트릭을 UDP 로 받을 때 receive buffer 부족 → silent drop → metric 손실. Datadog 공식 권장 따라 rmem_max + Agent 의 dogstatsd_so_rcvbuf 같이 키움. 자세히 = 01. DogStatsD UDP 처리.