옛 박스는 그 포트가 0.0.0.0/0이었다. 새로 만들면서 "전 세계 개방은 아니지"라며 통신사 대역 세 개로 좁혔는데, 근거가 3분짜리 패킷 캡처 5건이었다.
전환하고 열흘 뒤 점포 아홉 곳의 전화 수신이 끊겼다. 5xx도 타임아웃도 실패 로그도 없었다. 요청이 박스에 도달조차 못 했기 때문이다.
보안 그룹에서 막힌 트래픽은 아무 데도 안 남는다. 그래서 고객 신고로만 알 수 있었다.
사건 — 전화가 안 들어오는데 아무 알람도 없었다
어떻게 알았나
고객사에서 전화가 와서 알았다. 오전 11시 20분경.
전환 직후부터 끊겨 있었는데 그때까지 아무도 몰랐다. 요청이 네트워크 계층에서 끊기면 애플리케이션 로그에 아무것도 안 남는다.
먼저 세운 가설이 틀렸다
같은 시기에 이중화를 붙였으니 그쪽을 의심했다. 두 대로 늘렸는데 한 대에만 연결 상태가 있어서 홀수 요청이 엉뚱한 데로 갔을 것이다 — 그럴듯했다.
로그로 배제했다. 그 계열 경로는 리스너 규칙에서 특정 포트에 고정돼 있었고, 다른 쪽으로 간 요청이 0건이었다.
로그 대조로 좁혔다
발신지에서 오는 요청을 날짜별·점포별로 세는 것으로 판정했다.
8/28 유입이 잡히는 점포 32개
8/31 유입이 잡히는 점포 23개 ← 9개 사라짐
끊긴 아홉 곳은 새 박스 로그에 흔적조차 없었다. 도달하지 못했다는 뜻이고, 그러면 남는 건 네트워크 계층이다. 보안 그룹을 보니 그 3분 캡처로 정한 대역 세 개였다.
복구는 1분 40초
11:44 0.0.0.0/0 추가 (코드로)
11:46:04 첫 점포 유입 확인
그리고 좁힌 대역으로 돌아가지 않기로 했다
이게 이 사건의 결론이다.
통신사가 노드를 바꾸면 통보 없이 같은 일이 재현된다.
실패가 조용해서 고객 신고로만 알 수 있다.
→ 남의 IP 를 우리 가용성의 전제로 삼지 않는다.
"보안을 위해 좁힌다"가 항상 옳지 않다. 좁힌 대상이 우리가 통제하지 못하는 값이면, 그건 보안 강화가 아니라 남에게 가용성을 맡기는 것이다.
감시는 총량이 아니라 "유입 점포 수"로
이 사고를 총 건수로는 못 잡는다. 나머지 23곳이 멀쩡해서 총량은 거의 안 줄었다.
그래서 액세스 로그에서 최근 1시간의 유입이 잡히는 점포 수를 15분마다 세어 지표로 올리고 알람을 걸었다.
부분 장애는 합계에 묻힌다. 세는 단위를 합계가 아니라 구성원 수로 바꿔야 잡힌다.
그래서 임계값은 실측으로 정했다
같은 시기에 WAF rate-based 규칙을 검토했는데, 이번엔 숫자부터 쟀다.
실사용자 IP 의 5분 창 최대 868건
조사한 (IP × 창) 조합 중 1,000건 초과 0개
8/28 스크래퍼 5분에 21,000 ~ 40,000건
→ 임계 2,000건 / 5분
실사용자 최대의 2.3배 여유, 공격은 10~20배로 넘긴다
정상과 공격 사이가 20배 이상 벌어져 있어서 임계를 어디에 두든 되는 구간이었다. 재 보기 전에는 이걸 몰랐고, 안 쟀으면 "1,000이 적당한가" 같은 감으로 정했을 것이다.
곁가지로 나온 것들
전환이 이걸 싸게 만들었다. 옛 구성은 로드밸런서가 여러 개였는데 지금은 하나가 전체의 99%를 받는다.
24시간 1위가 2위의 200배
→ Web ACL 하나면 된다
비용은 요청 수가 지배한다.
Web ACL $5 + 규칙 $1 + 요청 $0.60/백만
주 리전 $63/월
보조 $6.83/월 (요청 수가 약 70배 차이)
요청의 62.6%가 정적 자산 → CDN 으로 옮기면 같은 비율로 준다
로그는 S3 직송 + ALLOW 제외 필터 (로그 수집으로 보내면 월 95GB, WAF 본체보다 비싸다)
봇 차단은 안 넣었다. 액세스 로그 95분치에서 봇성 UA가 1.66%인데 전부 정상 크롤러였다. 월 $10짜리 기능을 "있으니까" 켜지 않았다.
의존성이 하나 는다는 점은 짚어 뒀다. 지금 전 로드밸런서가 fail_open = false라 WAF 쪽 장애가 곧 500이다. 붙일 대상을 함께 정해야 한다. 그리고 Count로 걸고 1주 관찰한 뒤 Block으로 바꾼다 — 비용은 같고 되돌리기는 명령 하나다.
"0건"은 대조군 없이 못 믿는다
옛 박스 4대의 커스텀 크론을 조사했다. 전환 잔재가 남았는지 보려는 것이었다.
Admin 5건
서비스 B 6건
CI/CD·API 0건 ← 이걸 믿어도 되나?
믿어도 됐다. 같은 명령이 다른 두 대에서 5~6건을 잡아냈기 때문이다. 0건이 "없다"인지 "명령이 틀렸다"인지는 같은 명령이 다른 데서 뭔가를 잡아낼 때만 갈린다.
판정은 crontab 파일이 아니라 /var/log/cron*의 CMD 집계로 했다. 실제로 돈 것만 남으므로, 파일을 옮겨 두고 못 찾은 경우까지 잡힌다.
그 조사가 엉뚱한 걸 찾아냈다
한 대에만 있던 로그 초기화 스크립트(5GB 초과 시 비움, 매시 실행)를 단서로 새 박스를 대조했더니, 앱 박스 5대 중 한 대만 logrotate 설정 파일이 없었다.
catalina.out 이 기동 이래 한 번도 안 잘림 → 205MB × 2
디스크가 100GB 라 증상이 없었다
증상이 없어서 안 보였을 뿐이다. 옛 것을 정리하려던 조사가 새 것의 구멍을 찾아냈다.
문서에 적힌 "후속"이 이미 끝나 있었다
모니터링 박스에 수집 정지 감지를 넣으려는데, 런북에 "SNS publish 권한이 역할에 없다 — 후속"이라고 적혀 있었다.
실제로는 인라인 정책이 이미 붙어 있었다. 문서만 안 고쳤던 것이다.
후속을 착수할 때는 문서가 아니라 실물을 먼저 조회한다. 문서를 믿고 없는 일을 다시 하려 들면 시간이 그쪽으로 샌다.
검증은 일부러 실패시켜서 했다. 데이터 경로를 없는 곳으로 바꿔 돌리고 메시지 ID 반환과 메일 수신까지 확인했다. 잘 도는 걸 확인하는 것보다, 실패했을 때 알림이 오는지를 확인하는 게 감시에서는 본질이다.
알림 본문을 만들려다 막힌 것
사무실 밖에서 서버에 접근하면 메일이 오게 했다. 이벤트 규칙의 입력 변환기로 본문을 만들려 했는데 셋이 한꺼번에 막혔다.
1) 템플릿이 유효한 JSON 이어야 해서 raw 개행이 거부된다
그런데 그 JSON 문자열이 디코드되지 않고 그대로 메시지가 된다
→ 따옴표와 \n 이 글자로 나온다
2) 이벤트 시각이 UTC 고정인데 변환 수단이 없다
3) 인스턴스 이름은 이벤트에 없는 값이라 붙일 수 없다
입력 변환기는 값을 꺼내기만 하고 가공은 못 한다. 규칙에서 변환기를 떼고 원본 이벤트를 람다로 넘겨 거기서 본문을 만들어 셋을 한 번에 풀었다.
실행 역할은 읽기 전용 역할과 분리했다. 알림 발행은 쓰기다.
세션 하나가 40.8KB, 절반이 중복
박스마다 로컬 캐시를 쓰고 있어서 박스를 2대로 늘리는 순간 세션이 갈린다. 공유 캐시로 모으려고 크기를 재 봤다.
세션 하나 40.8KB
그중 절반이 같은 데이터 두 벌
노드 크기가 여기서 갈린다.
세션 다이어트를 먼저 하면 작은 노드 2대 $55/월
안 하면 한 단계 위 2대 $110/월
순서가 돈을 가른다. 나중에 줄이려면 노드 타입 변경이라 페일오버가 따른다. 그리고 지금은 로컬이라 안 보이던 비용이다 — 같은 AZ 왕복 약 1ms가 새로 생긴다(현재 중앙값의 4% 수준이라 걸림돌은 아니지만, 0이던 것이 생기는 건 맞다).
지금 다시 한다면
- 좁히는 값이 우리 통제 밖이면 좁히지 않는다. 남의 IP 대역은 통보 없이 바뀐다. 보안을 얻는 대신 가용성을 남에게 맡기는 거래인지 먼저 본다.
- 3분 캡처로 영구 설정을 정하지 않는다. 표본이 짧으면 "관측된 전부"가 "존재하는 전부"로 둔갑한다.
- 막힌 트래픽은 아무 데도 안 남는다. 네트워크 계층에서 끊기면 로그가 없으므로, "도달했는가"를 별도로 세는 감시가 필요하다.
- 부분 장애는 합계에 묻힌다. 세는 단위를 합계가 아니라 구성원 수로 바꾼다.
- 임계는 재고 정한다. 정상과 공격이 20배 벌어져 있는 걸 알면 임계 논쟁이 필요 없어진다.
- 0건은 대조군과 같이 본다. 같은 명령이 다른 데서 뭔가를 잡아낼 때만 0이 0이다.
- 문서의 "후속"은 실물로 확인하고 시작한다.
- 감시는 잘 도는 걸 확인하는 게 아니라 실패했을 때 알림이 오는지로 확인한다.
한 줄
값을 정할 때 무엇을 보고 정했는지가, 그 값이 틀렸을 때 얼마나 빨리 아는지를 결정한다.
두 달 반에 걸친 이 시리즈는 여기서 끝난다. 첫 편은 콘솔로 만든 계정을 코드로 끌어오는 이야기였고, 마지막은 그 코드에 적은 값을 무엇으로 정했느냐였다.
'AWS' 카테고리의 다른 글
| 전환 뒤에 켜진 알람들 — 새 문제가 아니라 원래 있던 것이 보인 것 (0) | 2026.09.21 |
|---|---|
| CloudFront 캐시 무효화 비용 — 파일 수가 아니라 경로 수로 센다 (0) | 2026.09.18 |
| 계정 이관 마지막 열흘: 인증 앞단화, 알람, 그리고 문서 (0) | 2026.09.15 |
| 장기 액세스 키 대신 브리지 프로필로 Terraform 돌리기 (0) | 2026.09.11 |
| Aurora 메이저 버전 업그레이드는 성공한 다음에 터진다 (0) | 2026.09.09 |
댓글