EC2 운영 — security group, key pair, SSM
EC2 운영의 일상 도구들. Security Group 규칙 설계, NACL과의 차이, key pair의 한계와 SSM Session Manager, IMDSv2, 그리고 AMI로 인스턴스 골격을 굳히는 법까지 정리합니다.
8장 EC2와 VPC 기초에서 EC2 한 대를 띄우는 그림을 잡았습니다. 본 챕터는 그 EC2를 다루는 방법입니다. 보안 규칙은 어떻게 설계하며, 접속은 어떻게 하고, 같은 인스턴스를 여러 번 띄우려면 무엇을 굳혀야 하는지를 다룹니다.
운영 EC2의 일은 대체로 셋으로 요약됩니다. 첫째, Security Group으로 누가 들어올 수 있는지 제어합니다. 둘째, 접속은 옛날에는 SSH와 key pair, 요즘은 SSM Session Manager로 합니다. 셋째, AMI로 골격을 굳혀 같은 인스턴스를 빠르게 재생성합니다. 이 셋을 한 줄에 꿰면 EC2 운영의 일상이 단순해집니다.
여기서 정리하는 SG 패턴은 13장 ALB / NLB와 ACM의 로드 밸런서 보안 규칙으로, AMI와 ASG는 15장 ECS와 Fargate의 컨테이너 운영으로 이어집니다.
Security Group의 구조 #
**Security Group (SG)**은 인스턴스(정확히는 ENI)에 붙는 stateful 방화벽입니다. 인스턴스 한 대에 SG를 여러 개 붙일 수 있고, SG 한 개를 여러 인스턴스가 공유할 수 있습니다.
Inbound vs Outbound #
SG의 규칙은 두 방향입니다.
| Inbound | Outbound | |
|---|---|---|
| 무엇을 제어 | 들어오는 트래픽 | 나가는 트래픽 |
| 기본값 | 모두 막음 | 모두 허용 |
| 자주 만짐 | 자주 | 거의 안 |
기본값을 기억합니다. Inbound는 기본 차단, Outbound는 기본 허용입니다. 그래서 SG 작업의 대부분은 Inbound 규칙 추가입니다.
규칙의 구조 #
Protocol Port Source Description
TCP 80 0.0.0.0/0 HTTP from anywhere
TCP 443 0.0.0.0/0 HTTPS from anywhere
TCP 22 198.51.100.10/32 SSH from my home IP각 규칙은 프로토콜, 포트, 소스의 조합입니다. 소스(Source) 항목에는 두 가지가 들어갈 수 있습니다.
- CIDR 블록 —
0.0.0.0/0(모든 IP),10.0.0.0/16(VPC 내부),198.51.100.10/32(단일 IP) - 다른 SG의 ID —
sg-0abc...로, 이것이 진짜 강력합니다.
SG를 SG로 가리키기 #
운영에서 핵심 패턴은 SG가 다른 SG를 가리키는 것입니다.
ALB SG (sg-alb)
Inbound: TCP 443 from 0.0.0.0/0
App SG (sg-app)
Inbound: TCP 8080 from sg-alb ← IP 가 아니라 SG 자체ALB의 IP가 바뀌어도(실제로 ALB는 여러 IP를 동적으로 할당합니다) SG 가리키기는 자동으로 따라갑니다. 규칙 유지보수가 훨씬 단순해집니다.
자주 쓰는 SG 패턴 #
ALB SG (sg-alb)
in: 443 ← 0.0.0.0/0
out: all
App SG (sg-app)
in: 8080 ← sg-alb ← ALB 만 들어올 수 있음
22 ← sg-bastion ← Bastion 에서 SSH (옛날 방식)
out: all
DB SG (sg-db)
in: 5432 ← sg-app ← 앱 서버만 DB 접근
out: all (또는 닫기)
Bastion SG (sg-bastion)
in: 22 ← 198.51.100.10/32 ← 본인 IP 만
out: all규칙이 SG에서 SG로 흐르는 것이 핵심입니다. IP가 아닙니다.
Outbound를 막는 경우 #
기본은 outbound 모두 허용이지만, 공격에 노출됐을 때 데이터 유출을 막기 위해 outbound도 좁히는 패턴이 있습니다. 보통 운영 환경의 DB나 내부 리소스부터 적용합니다.
App SG outbound:
TCP 5432 → sg-db ← DB 만
TCP 443 → 0.0.0.0/0 ← 외부 API 호출용
TCP 53 → 0.0.0.0/0 ← DNS
UDP 53 → 0.0.0.0/0 ← DNSNACL — 또 다른 층 #
VPC의 두 번째 방화벽은 **NACL (Network Access Control List)**입니다. 서브넷 단위로 동작합니다.
| Security Group | NACL | |
|---|---|---|
| 적용 단위 | 인스턴스 (ENI) | 서브넷 |
| Stateful | 그렇다 | 아님 (응답도 명시 허용 필요) |
| 규칙 종류 | Allow only | Allow + Deny |
| 평가 순서 | 모든 규칙 | 번호순 (낮은 번호 먼저) |
| 일상에서 | 매일 만짐 | 거의 안 만짐 |
NACL은 자주 쓰지 않습니다. 기본 NACL이 모든 트래픽을 허용하고, SG가 충분히 세밀하기 때문입니다. NACL을 만지는 경우는 다음과 같습니다.
- 특정 IP 대역을 차단할 때(Deny가 필요할 때 — SG에는 Deny가 없습니다).
- 공격을 받을 때 임시로 차단할 때.
- 컴플라이언스 요구로 서브넷 단위 명시 차단이 필요할 때.
NACL의 stateless 함정 #
NACL은 stateless 라 응답 트래픽도 명시적으로 허용해야 합니다.
Inbound Allow TCP 1024-65535 0.0.0.0/0 ← ephemeral port 응답
Outbound Allow TCP 80 0.0.0.0/01024-65535가 ephemeral port 영역입니다. 이를 빠뜨리면 응답이 돌아오지 않습니다. SG에서는 stateful이라 자동인데, NACL은 명시가 필요합니다.
Key pair의 한계 #
옛날부터 EC2 SSH 접속은 key pair로 했습니다.
# 키 페어 만들기
aws ec2 create-key-pair --key-name my-key --query 'KeyMaterial' --output text > my-key.pem
chmod 400 my-key.pem
# 인스턴스 띄울 때 key 지정
aws ec2 run-instances --key-name my-key ...
# 접속
ssh -i my-key.pem ec2-user@<public-ip>EC2가 띄워질 때 인스턴스의 ~/.ssh/authorized_keys에 자동으로 키가 추가되어 SSH가 가능해집니다.
key pair 모델의 한계 #
key pair 모델은 운영 규모가 커지면 깨집니다.
- 키 분실 — 잃어버리면 다시 만들 수 없습니다. 인스턴스를 재생성하거나 EBS를 마운트한 뒤 수동으로 추가해야 합니다.
- 키 공유의 위험 — 팀원에게 줘야 하는데 한 번 새면 회수할 수 없습니다.
- 감사 어려움 — 누가 언제 들어왔는지 별도 로깅이 필요합니다.
- 인터넷에 22 포트 노출 — 공격 면적이 됩니다.
- MFA 불가 — 키만 있으면 통과합니다.
EC2 Instance Connect #
콘솔이 만든 임시 SSH 키를 한 번만 쓰는 방식입니다. SG에 22 포트 허용은 여전히 필요합니다. 콘솔의 “Connect” 버튼이 이것을 씁니다.
SSM Session Manager — 키 없는 접속 #
**SSM (AWS Systems Manager)**의 Session Manager는 EC2 접속의 새 표준입니다. 22 포트도 열지 않고, 키도 없이 EC2 안의 셸로 들어갑니다.
[내 컴퓨터] ──HTTPS──▶ [SSM Endpoint] ◀──HTTPS──[EC2 안의 SSM Agent]
│
▼
IAM 권한 확인EC2 안에서 도는 SSM Agent가 AWS API로 outbound 연결을 만들고, 그 채널을 통해 콘솔의 셸 입력이 흐릅니다. 방향이 반대(EC2가 outbound)라서 SG inbound 22 포트가 필요 없습니다.
Session Manager 셋업 #
- SSM Agent가 설치된 AMI — Amazon Linux 2023 / Ubuntu 최신은 기본 포함입니다.
- EC2의 IAM Role에
AmazonSSMManagedInstanceCore정책을 붙입니다. - outbound 인터넷 또는 VPC Endpoint — Private 서브넷의 EC2도 SSM이 가능합니다.
aws ssm start-session --target i-0abc1234def567890
# 포트 포워딩도 가능
aws ssm start-session --target i-0abc... \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["80"],"localPortNumber":["8080"]}'key pair vs Session Manager #
| key pair (SSH) | Session Manager | |
|---|---|---|
| 22 포트 | 열어야 함 | 안 열어도 됨 |
| 키 관리 | 직접 | 없음 |
| 인증 | SSH 키 | IAM (MFA 가능) |
| 감사 로그 | 별도 | CloudTrail / S3 자동 |
| Private 서브넷 | Bastion 필요 | VPC Endpoint로 직접 |
| 포트 포워딩 | ssh -L | start-session으로 가능 |
운영에서는 Session Manager가 거의 항상 정답입니다. 자세한 IAM 설정은 2장 IAM, 보안은 6장 보안 기본을 참조합니다.
CloudShell과 헷갈리지 않습니다. 5장 CloudShell과 IAM Identity Center의 CloudShell은 AWS 콘솔 안의 브라우저 터미널로, 내 IAM 자격으로
aws cli를 쓰는 방식입니다. Session Manager는 EC2 인스턴스 안의 셸입니다.
EC2의 메타데이터 서비스 (IMDS) #
EC2 안에서 자기 인스턴스의 정보(인스턴스 ID, 리전, IAM 역할 자격증명 등)를 받는 서비스가 **IMDS (Instance Metadata Service)**입니다.
TOKEN=$(curl -X PUT http://169.254.169.254/latest/api/token \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/instance-id169.254.169.254라는 link-local 주소가 EC2 안에서만 응답하는 메타데이터 엔드포인트입니다. IAM Role의 임시 자격증명도 여기서 받습니다. aws cli가 EC2 안에서 자동으로 그 자격을 쓰는 이유입니다.
IMDSv1 vs IMDSv2 #
옛날에는 토큰 없이 GET으로 받았습니다(IMDSv1). SSRF 공격으로 토큰을 빼가는 사고가 많아 IMDSv2로 바뀌었습니다. PUT으로 토큰을 받고 그것으로 GET 합니다. 새 인스턴스는 IMDSv2만 활성화하는 것이 권장됩니다.
aws ec2 modify-instance-metadata-options \
--instance-id i-0abc... \
--http-tokens required \
--http-endpoint enabledAMI 만들기 — 골격 굳히기 #
같은 셋업을 가진 인스턴스를 여러 번 빠르게 띄우려면 두 갈래가 있습니다. 하나는 AMI를 만드는 것이고, 다른 하나는 빈 AMI로 띄우고 셋업 스크립트를 자동 실행하는 User data 방식입니다.
AMI 만들기 #
콘솔에서 인스턴스 우클릭 후 “이미지 생성"을 하거나 CLI로 만듭니다.
aws ec2 create-image \
--instance-id i-0abc... \
--name "my-app-2026-05-24" \
--description "Node 20 + nginx + my-app v1.2.3" \
--no-reboot # 옵션 — 재부팅 없이 (디스크 일관성은 약간 떨어질 수 있음)만들어진 AMI는 인스턴스의 EBS 스냅샷과 메타데이터입니다. 그 AMI로 새 인스턴스를 띄우면 같은 디스크 상태로 시작합니다. AMI는 리전 단위라 다른 리전에서는 copy-image를 씁니다.
User data — 부팅 스크립트 #
AMI 대신 빈 OS 이미지로 띄우고 부팅 스크립트로 셋업하는 패턴입니다. AMI보다 유연하고, 변경 추적도 쉽습니다.
#!/bin/bash
yum update -y
yum install -y nginx
systemctl enable --now nginx
# 앱 코드 받기
aws s3 cp s3://my-bucket/app.tar.gz /tmp/
tar -xzf /tmp/app.tar.gz -C /opt/myappUser data는 인스턴스 첫 부팅 때 한 번 실행됩니다. 로그는 /var/log/cloud-init-output.log에 남습니다.
Golden AMI vs User data #
| Golden AMI | User data | |
|---|---|---|
| 부팅 속도 | 빠름 | 느림 (스크립트 실행 시간) |
| 변경 관리 | AMI 새로 빌드 | 스크립트 수정 |
| 재현성 | 매우 높음 | 외부 의존성(yum repo, S3)이 변할 수 있음 |
| 적합 | ASG 빠른 스케일 / 안정적 | 개발 / 빠른 변경 |
운영에서는 둘 다 같이 씁니다. 골든 AMI로 OS와 의존성을 박고, User data로 앱 버전만 끼워 넣는 식입니다.
Auto Scaling Group — 자동 복구 #
인스턴스가 죽으면 새로 띄우고 ALB에 자동 연결해 주는 기능이 **ASG (Auto Scaling Group)**입니다.
Launch Template (인스턴스 템플릿: AMI, 타입, SG, key, user data)
│
▼
┌─────────┐
│ ASG │ desired=2 min=2 max=10
└─────────┘
│
├─── EC2 (AZ a) ← health check 실패 → terminate + 새로 띄움
├─── EC2 (AZ b)
└─── EC2 (AZ b)기본 구성은 다음과 같습니다.
- Launch Template — 어떤 EC2를 띄울지 정의합니다(AMI, 타입, SG, IAM, user data).
- Desired / Min / Max — 항상 유지할 개수, 최소, 최대입니다.
- Health Check — EC2 자체(
EC2) 또는 ALB target group(ELB) 기준입니다.
컨테이너 워크로드라면 15장 ECS와 Fargate가 더 매끄러운 대안입니다. ECS가 컨테이너 단위의 ASG를 흡수합니다.
자주 만나는 함정 #
- “왜 ALB가 EC2에 도달 못 합니까?” — 위에서 아래로 확인합니다. (1) ALB SG outbound가 EC2 SG inbound와 일치하는가, (2) EC2 SG inbound에 ALB SG가 source로 들어 있는가, (3) EC2의 OS 안 방화벽(
firewalld,ufw)도 그 포트를 허용하는가, (4) ALB target group의 health check 경로가 200을 응답하는가, (5) EC2가 그 포트에서 listen 중인가(ss -tlnp). 대부분 (1) 또는 (2)이며, SG 항목을 IP로 적었다면 SG 자체로 바꾸는 것이 운영 정답입니다. - 키가 없는데 EC2 안에 들어가야 함 — Session Manager가 켜져 있으면
aws ssm start-session을 씁니다. 안 켜져 있으면 인스턴스를 정지하고 EBS를 분리해 다른 EC2에 마운트한 뒤~/.ssh/authorized_keys를 수정하고 재연결하거나, EBS 스냅샷을 떠서 새 키로 새 인스턴스를 띄웁니다. - Outbound all 그대로 둬서 데이터 유출 — EC2가 침해됐을 때 outbound가 다 열려 있으면 공격자가 임의 IP로 데이터를 보냅니다. DB 서버나 내부 시스템은 outbound도 좁히는 것이 가장 안전합니다.
- NACL 임의 차단으로 응답이 안 옴 — NACL의 stateless 성질을 잊고 outbound만 허용하면 inbound 응답이 막힙니다. 거의 항상 NACL은 기본값으로 두고 SG만 만지는 것이 안전합니다.
- IMDSv1 그대로 운영 — 오래된 AMI나 옛 셋업이 IMDSv1 인 채로 운영 중이면 SSRF 공격 면이 됩니다. 모든 인스턴스에
--http-tokens required를 적용합니다. - AMI가 너무 커서 부팅이 느려짐 — 오래 운영한 인스턴스를 그대로 AMI로 떠서 5GB 이상이 되면 부팅 시간이 늘어납니다. AMI 만들기 전에 로그 / 캐시 / 임시 파일을 정리하고(
yum clean all등),cloud-init clean으로 다음 부팅에 다시 init이 돌게 하며, swap과 journal을 비웁니다.
연습문제 #
- §“3-tier 웹 앱 SG 설계"의 네 SG (alb · app · db · bastion)를 보지 않고 다시 그리되, 각 inbound 규칙의 source가 IP인지 SG 인지를 표시하세요. 그런 다음 ALB의 IP가 바뀌어도 규칙을 고치지 않아도 되는 이유를 §“SG를 SG로 가리키기"를 근거로 한 줄로 설명해 보세요.
- key pair와 Session Manager의 비교표를 보고, §“key pair 모델의 한계"의 다섯 항목 중 Session Manager가 각각 어떻게 해결하는지 연결해 보세요. 6장 보안 기본의 최소 권한과 어떻게 맞물리는지도 한 줄 적어 두세요.
- 같은 셋업의 인스턴스를 자동 복구하려는 상황에서 Golden AMI와 User data를 함께 쓴다면, 각각에 무엇을 넣을지(OS · 의존성 · 앱 버전) 나눠 적어 보세요. 이 분담은 15장 ECS와 Fargate에서 컨테이너 이미지와 태스크 정의로 다시 나타납니다.
한 줄 요약: SG는 인스턴스 단위 stateful 방화벽으로 SG가 SG를 가리키는 패턴이 IP보다 강력하고, NACL은 서브넷 단위 stateless 라 거의 안 만진다. 접속은 22 포트도 키도 안 쓰는 SSM Session Manager가 새 표준이며, IMDSv2 강제로 SSRF를 막는다. 골격은 Golden AMI와 User data를 함께 써서 굳히고, ASG가 자동 복구를 맡는다.
다음 챕터 #
EC2의 기본은 정리됐습니다. 다음 10장 S3에서는 EC2와 함께 자주 다루는 객체 스토리지로 넘어갑니다. 버킷의 모양, 정책과 Public Access Block, 정적 사이트 호스팅, presigned URL 같은 일상 패턴을 정리합니다.