문제가 발생한 프로젝트 구조는 단순하다.
클라이언트 → API 서버 → AI 서버
클라이언트는 절대 AI 서버에 직접 접근 못 하고, 무조건 API 서버를 통해서만 요청이 전달된다. 이게 기본 설계다.
근데 어느 날 로그를 보니, AI 서버로 외부에서 마구잡이로 요청이 들어오고 있었다.
찾아 보니 AI 서버가 완전히 오픈된 상태였다...
[Client 브라우저]
↓ HTTPS (443포트)
[ALB]
→ 보안그룹: default
↓ 내부 트래픽 (HTTP 80포트)
[ECS Fargate Task - API 서버]
→ 포트: 80
→ 보안그룹: default
↓ HTTP 요청 (8000포트)
[EC2 - AI 서버]
→ 포트: 8000
→ 보안그룹: 모든 TCP 허용 (0.0.0.0/0)
AI 서버 인스턴스의 보안그룹을 보니까, 모든 TCP 포트를 0.0.0.0/0에 대해 허용하고 있었다. 이건 무슨 말이냐면
"누구든지, 어떤 포트든, 아무거나 막 던져도 받아줄게요~"
…라는 뜻이다.
이건 거의 "해커야 놀자" 오픈런 상태다.
API 서버는 외부 요청 받는 역할이니까 어느 정도 열려 있는 게 맞다. 근데 AI 서버는 절대 아니지....
AI 서버는 오직 API 서버에서만 호출해야 하는 폐쇄형 서비스다. 그런데 WAF도 없고, 인증도 없고, 그냥 퍼블릭 IP로 노출돼 있었다.
이 상태로 EC2 띄워놓으면? 누가 뭐라도 한방 치면 바로 뚫린다.
보안그룹을 수정했다는 공지가 떴는데, 갑자기 클라이언트에서 API 호출하면 500에러를 뱉는다고 문의가 들어왔다.
로그를 보니 AI 서버와 통신 자체가 안 되고 있는 상황이였다...
알고보니 AI 보안그룹 인바운드 규칙에 default 보안 그룹을 80포트의 자기자신을 소스로 설정한 인바운드 규칙만 추가하여 추가한 상태였다.

| 구분 | 프로토콜 | 포트 범위 | 소스 | 설명 |
|---|---|---|---|---|
| 인바운드 | HTTP (TCP) | 80 | sg-0e0701be0ed88f096 (자기 자신) | SG 내부 통신 허용 |
| 인바운드 | TCP | 80 | 0.0.0.0/0 | 전 세계 어디서든 접근 허용 |
| 인바운드 | TCP | 443 | 0.0.0.0/0 | |
| 아웃바운드 | 모든 트래픽 | 전체 | 0.0.0.0/0 | 외부 어디든 요청 가능 |
default 보안그룹은 API 서버에도 적용되어 있어 연결은 가능하지만, → 같은 SG를 사용하는 모든 인스턴스 접근도 허용되는 위험한 설정default SG는 많은 리소스에 공유될 가능성이 있어 보안 통제가 불가능함 → 보안 통제를 위해선 반드시 분리된 전용 SG가 필요여러가지 문제가 있지만, 핵심은 AI 서버는 8000포트로 요청 받아야 하는데, 포트가 안 열려 있으니 통신 자체가 막혀 있었던 것이다.
이건 그냥 전형적인 SG 누락 → connection timeout 패턴이다.

[Client 브라우저]
↓ HTTPS (443포트)
[ALB]
→ 보안그룹: defaultc
↓ 내부 트래픽 (HTTP 80포트)
[ECS Fargate Task - API 서버]
→ 포트: 80
→ 보안그룹: default + api-to-ai-traffic
↓ HTTP 요청 (8000포트)
[EC2 - AI 서버]
→ 포트: 8000
→ 보안그룹: api-to-ai-traffic
AI 서버는 오직 API 서버를 통해서만 요청을 받아야 하며, 그 외 트래픽은 전부 차단돼야 한다.
위의 조건을 보장하기 위해 아래 조건이 반드시 필요하다.
api-to-ai-traffic → API 서버에서 나가는 단방향 요청만 허용api-to-ai-traffic SG 등록api-to-ai-traffic SG 등록
default보안그룹을 그대로 사용하면 안 되는 이유
defaultSG는 VPC 생성 시 자동으로 붙는 공용 보안그룹- 여러 인스턴스가 공유 → 어떤 인스턴스가 뭘 열었는지 추적 불가
- 실수로
0.0.0.0/0이나 포트 열면 → 모든 인스턴스 뚫림- AI 서버에 붙어 있을 경우 → 인터넷 아무 IP나 8000포트로 두들겨도 받아버림
- 최소 권한 원칙 완전 무시됨
| 보안그룹 | 방향 | 프로토콜 | 포트 | 대상/소스 | 설명 |
|---|---|---|---|---|---|
| api-to-ai-traffic | 인바운드 | 사용자 지정 TCP | 8000 | api-to-ai-traffic | AI 서버에서 API 서버 요청받기 |
| 아웃바운드 | 사용자 지정 TCP | 8000 | api-to-ai-traffic | API 서버 → AI 서버 요청 |

api-to-ai-traffic 보안그룹 등록api-to-ai-traffic 보안그룹에서 오는 8000포트의 요청을 허용하겠다는 의미api-to-ai-traffic SG 보안그룹을 추가하는 것도 잊지 말자api-to-ai-traffic으로 보내는 것을 허용하겠다는 의미Fargate 자체가 HTTP 요청을 실제로 날리는 주체기 때문에, Fargate Task에 api-to-ai-traffic 보안그룹을 반드시 직접 붙여줘야 한다.(ALB에 추가하면 안됨)
AI 서버 보안그룹
api-to-ai-traffic의 인바운드 규칙 의미→ “이 보안그룹(
api-to-ai-traffic)에 속한 인스턴스/태스크가 보내는 8000포트 요청만 받겠다”는 뜻
만약 Fargate 태스크에 api-to-ai-traffic 보안그룹이 안 붙어 있다면 AI 서버 입장에선 “너 SG도 인증 안 된 놈이네” → 요청 거부 → 연결 timeout
참고 - 같은 보안 그룹 안에 붙은 인스턴스끼리는, SG 룰이 없어도 통신된다
AWS에서 보안 그룹(Security Group)은 stateful한 구조로 작동한다.
즉, 한쪽에서 트래픽이 허용되면 응답은 별도의 규칙 없이도 자동으로 허용된다. 그리고 더 중요한 점은, 같은 보안 그룹을 공유하는 인스턴스끼리는 인바운드/아웃바운드 규칙 없이도 서로 통신이 가능하다.
예를 들어
api-to-ai-traffic보안 그룹을 API 서버와 AI 서버 양쪽에 모두 할당했다면, 이 두 인스턴스는 별도의 규칙 없이도 TCP 통신이 이루어진다.이유는 AWS는 동일한 보안 그룹이 할당된 리소스들끼리, 내부적으로 프라이빗 IP 기반의 트래픽을 암묵적으로 허용하는 특수 규칙을 가지고 있기 때문이다. 이건 AWS 내부에서 SG 참조 없이도 작동하는 자동 허용(implicit allow) 로직이다.
(실제로 AWS 공식 문서에도 “same SG assigned = mutual communication allowed” 라고 명시돼 있음)하지만 이걸 정식 보안 정책으로 믿고 가면 안 된다. 이건 "우연히 되는 것처럼 보이는 예외 케이스"일 뿐이고 SG 콘솔이나 IaC 코드 상에서 명시적 룰 없이 통신이 되면 보안 감사나 협업 시 오해를 일으킬 수 있고, SG를 재사용하거나 다른 인프라에 이식할 때 통신이 갑자기 막히는 원인이 되기 쉽다.
따라서 같은 SG 간 통신이라도, 인바운드와 아웃바운드 규칙에 명확하게 해당 SG를 소스/대상으로 등록하는 것이 보안 설계의 정석이다.
이 글에서 소개한 보안 설정은 딱 "기본은 지켰다" 수준이다.
누구나 알고 있어야 할 최소한의 기준이고, 특히 AI 서버처럼 호출할 때마다 비용이 발생하는 구조에서는 반드시 챙겨야 한다.
쓸데없는 외부 요청 = 그대로 비용 낭비니까, 보안 설정은 선택이 아니라 필수다.
“귀찮다”, “나중에 하지 뭐” 하고 넘겼다가 결국 이렇게 소 잃고 외양간 고치는 꼴 나는 건 진짜 한순간이다.
이번 사례는 그걸 직접 겪어보며 배운 경험이었고, 같은 실수만 반복하지 않으면 충분히 의미 있는 피드백이라고 생각한다.
누굴 탓하려는 게 아니다.
나도 보안 전문가가 아닌 입장에서 말하는 거지만, 보안은 결국 습관에서 시작된다고 밎는다.
조금 귀찮더라도, 기본부터 잘 지키는 분위기를 팀 전체가 함께 만들어 나가는 것, 그게 진짜 중요한 포인트라고 생각한다.