본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 19. · 7 Views
AWS 보안 그룹과 네트워크 완벽 가이드
초급 개발자를 위한 AWS 보안 그룹과 네트워크 설정 가이드입니다. 실무에서 반드시 알아야 할 보안 그룹 설정, 인바운드/아웃바운드 규칙, 포트 관리, Elastic IP까지 이북처럼 술술 읽히는 스토리로 배워봅시다.
목차
1. 보안 그룹이란
어느 날 신입 개발자 김개발 씨가 처음으로 AWS EC2 인스턴스를 생성했습니다. 설정을 마치고 웹 서버를 실행했는데, 브라우저에서 접속이 되지 않았습니다.
"분명히 서버는 실행 중인데 왜 안 되는 거지?" 선배 박클라우드 씨가 다가와 물었습니다. "보안 그룹 설정은 확인해 봤어?"
보안 그룹은 AWS에서 EC2 인스턴스를 보호하는 가상 방화벽입니다. 마치 아파트 경비실에서 방문자를 확인하고 출입을 통제하는 것처럼, 보안 그룹은 서버로 들어오고 나가는 트래픽을 제어합니다.
이것을 제대로 이해하면 안전하면서도 필요한 통신은 허용하는 서버 환경을 구축할 수 있습니다.
다음 코드를 살펴봅시다.
# boto3를 사용한 보안 그룹 조회 예제
import boto3
# EC2 클라이언트 생성
ec2 = boto3.client('ec2', region_name='ap-northeast-2')
# 보안 그룹 목록 조회
response = ec2.describe_security_groups()
# 각 보안 그룹의 정보 출력
for sg in response['SecurityGroups']:
print(f"보안 그룹 이름: {sg['GroupName']}")
print(f"보안 그룹 ID: {sg['GroupId']}")
print(f"설명: {sg['Description']}")
# 인바운드 규칙 확인
print(f"인바운드 규칙 개수: {len(sg['IpPermissions'])}")
print("-" * 50)
김개발 씨는 입사 일주일 차 신입 개발자입니다. 처음으로 AWS에 서버를 띄우라는 과제를 받았습니다.
유튜브 강의를 보며 EC2 인스턴스를 생성하고, Node.js 서버를 실행했습니다. 터미널에는 "Server is running on port 3000"이라는 메시지가 떴습니다.
"좋아, 이제 브라우저에서 확인해 보자." 김개발 씨는 EC2의 퍼블릭 IP 주소를 복사해서 브라우저 주소창에 입력했습니다. 하지만 페이지는 로딩만 계속되다가 결국 "연결할 수 없음"이라는 에러가 나타났습니다.
분명히 서버는 실행 중인데 왜 접속이 안 되는 걸까요? 김개발 씨는 코드를 다시 확인하고, 서버를 재시작해 보고, 심지어 EC2 인스턴스를 재부팅까지 해봤습니다.
하지만 결과는 같았습니다. 선배 개발자 박클라우드 씨가 김개발 씨의 모니터를 보며 물었습니다.
"보안 그룹 설정은 확인해 봤어?" 김개발 씨는 고개를 갸우뚱했습니다. "보안 그룹이요?" 그렇다면 보안 그룹이란 정확히 무엇일까요?
쉽게 비유하자면, 보안 그룹은 마치 아파트 경비실과 같습니다. 아파트에 들어가려면 경비실에서 방문 목적을 확인하고 출입증을 받아야 합니다.
택배 기사는 들어갈 수 있지만, 의심스러운 방문자는 차단됩니다. 마찬가지로 EC2 인스턴스도 외부에서 함부로 접근할 수 없도록 보호되어 있습니다.
보안 그룹이 없던 시절에는 어땠을까요? 초기 인터넷 서버들은 누구나 접근할 수 있었습니다.
IP 주소만 알면 서버의 모든 포트에 접속을 시도할 수 있었습니다. 해커들은 이런 취약점을 악용해 서버를 공격하고, 데이터를 훔치고, 심지어 서버를 마비시키기도 했습니다.
더 큰 문제는 관리자가 일일이 방화벽 규칙을 설정해야 했다는 점입니다. 바로 이런 문제를 해결하기 위해 AWS는 보안 그룹을 제공합니다.
보안 그룹을 사용하면 클릭 몇 번으로 필요한 트래픽만 허용할 수 있습니다. 또한 여러 EC2 인스턴스에 동일한 보안 그룹을 적용해 일관된 보안 정책을 유지할 수 있습니다.
무엇보다 AWS가 자동으로 관리해주기 때문에 관리자의 부담이 크게 줄어듭니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 boto3 라이브러리를 사용해 EC2 클라이언트를 생성합니다. 이 클라이언트를 통해 AWS EC2 서비스와 통신할 수 있습니다.
describe_security_groups 메서드를 호출하면 현재 계정의 모든 보안 그룹 정보를 조회할 수 있습니다. 각 보안 그룹의 이름, ID, 설명, 그리고 인바운드 규칙 개수를 출력합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 쇼핑몰 서비스를 운영한다고 가정해봅시다.
웹 서버는 고객들이 접속해야 하므로 HTTP(80번 포트)와 HTTPS(443번 포트)를 열어야 합니다. 하지만 데이터베이스 서버는 웹 서버에서만 접근할 수 있어야 합니다.
보안 그룹을 사용하면 이런 세밀한 접근 제어가 가능합니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 모든 포트를 전체 공개(0.0.0.0/0)로 설정하는 것입니다. "일단 되게 만들고 나중에 수정하지 뭐"라는 생각으로 모든 트래픽을 허용하면, 보안에 심각한 구멍이 생깁니다.
따라서 필요한 포트만 최소한으로 열고, 접근 가능한 IP 범위도 제한해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박클라우드 씨는 AWS 콘솔에서 보안 그룹 설정을 확인했습니다. "역시나.
인바운드 규칙에 3000번 포트가 없네요. 이러니까 접속이 안 되는 거죠." 보안 그룹을 제대로 이해하면 안전하면서도 필요한 통신은 원활히 허용하는 서버 환경을 구축할 수 있습니다.
여러분도 EC2 인스턴스를 생성할 때마다 보안 그룹 설정을 꼼꼼히 확인하는 습관을 들여보세요.
실전 팁
💡 - 보안 그룹은 상태 저장 방화벽입니다. 인바운드로 들어온 트래픽의 응답은 자동으로 허용됩니다.
- 하나의 EC2 인스턴스에 여러 보안 그룹을 적용할 수 있습니다. 규칙들은 OR 조건으로 합쳐집니다.
- 보안 그룹 변경사항은 즉시 적용되므로, 서버 재시작이 필요 없습니다.
2. 인바운드 아웃바운드 규칙
박클라우드 씨가 김개발 씨에게 설명을 시작했습니다. "보안 그룹에는 두 가지 규칙이 있어요.
인바운드와 아웃바운드." 김개발 씨는 고개를 갸우뚱했습니다. "그게 뭔가요?" 박클라우드 씨는 웃으며 쉬운 예시를 들어주기 시작했습니다.
인바운드 규칙은 외부에서 서버로 들어오는 트래픽을 제어하고, 아웃바운드 규칙은 서버에서 외부로 나가는 트래픽을 제어합니다. 마치 건물의 입구와 출구를 따로 관리하는 것처럼, 들어오는 것과 나가는 것을 별도로 설정할 수 있습니다.
이를 통해 세밀한 네트워크 보안 정책을 구현할 수 있습니다.
다음 코드를 살펴봅시다.
# boto3를 사용한 보안 그룹 규칙 생성 예제
import boto3
ec2 = boto3.client('ec2', region_name='ap-northeast-2')
# 보안 그룹 ID (실제 값으로 변경 필요)
security_group_id = 'sg-0123456789abcdef0'
# 인바운드 규칙 추가 - HTTP 허용
ec2.authorize_security_group_ingress(
GroupId=security_group_id,
IpPermissions=[
{
'IpProtocol': 'tcp', # TCP 프로토콜
'FromPort': 80, # HTTP 포트
'ToPort': 80,
'IpRanges': [{'CidrIp': '0.0.0.0/0', 'Description': 'Allow HTTP from anywhere'}]
}
]
)
print("인바운드 규칙이 추가되었습니다.")
박클라우드 씨는 메모장을 꺼내 그림을 그리기 시작했습니다. "자, 여기 EC2 서버가 있다고 생각해봐요.
외부 사용자가 웹사이트에 접속하려면 서버로 요청을 보내야겠죠? 이게 바로 인바운드 트래픽입니다." 김개발 씨가 고개를 끄덕이자, 박클라우드 씨는 계속 설명했습니다.
"반대로 서버가 외부 API를 호출하거나 데이터베이스에 접속할 때는 서버에서 밖으로 나가는 거예요. 이게 아웃바운드 트래픽이고요." 쉽게 비유하자면, 인바운드와 아웃바운드는 마치 회사의 출입 통제 시스템과 같습니다.
방문자가 회사에 들어올 때는 신분증을 확인하고 출입증을 발급합니다. 이것이 인바운드 규칙입니다.
반대로 직원이 외부로 나갈 때도 퇴근 시간과 목적을 확인할 수 있습니다. 이것이 아웃바운드 규칙입니다.
인바운드와 아웃바운드를 구분하지 않던 시절에는 어땠을까요? 초기 방화벽 시스템은 단순히 "차단" 또는 "허용"만 가능했습니다.
서버가 외부 API를 호출해야 하는데, 인바운드로 들어오는 악의적인 트래픽도 함께 허용되는 경우가 많았습니다. 관리자는 복잡한 규칙을 일일이 작성해야 했고, 실수로 중요한 트래픽을 차단하는 일도 빈번했습니다.
바로 이런 문제를 해결하기 위해 AWS는 인바운드와 아웃바운드를 분리된 규칙으로 관리합니다. 인바운드 규칙을 사용하면 외부에서 접근 가능한 포트만 정확히 열 수 있습니다.
또한 특정 IP 주소나 IP 범위만 허용하는 것도 가능합니다. 무엇보다 아웃바운드 규칙을 통해 서버가 의도하지 않은 외부 통신을 하는 것을 막을 수 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 authorize_security_group_ingress 메서드를 사용해 인바운드 규칙을 추가합니다.
IpProtocol에는 tcp를 지정하고, FromPort와 ToPort에 80을 설정해 HTTP 트래픽을 허용합니다. IpRanges에 0.0.0.0/0을 지정하면 모든 IP 주소에서의 접근을 허용합니다.
Description 필드로 규칙의 목적을 명확히 기록할 수 있습니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 내부 관리자 시스템을 구축한다고 가정해봅시다. 일반 사용자는 웹 페이지(80, 443번 포트)에만 접근할 수 있어야 합니다.
하지만 SSH 접속(22번 포트)은 회사 IP 주소에서만 허용해야 합니다. 인바운드 규칙에서 SSH는 회사 IP로 제한하고, HTTP/HTTPS는 전체 공개로 설정하면 됩니다.
아웃바운드는 기본적으로 모든 트래픽을 허용하지만, 보안이 중요한 경우 특정 도메인이나 IP로만 통신하도록 제한할 수 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 아웃바운드 규칙을 잘못 설정해 서버가 외부 서비스와 통신하지 못하게 만드는 것입니다. AWS에서 패키지를 설치하거나 API를 호출할 때 연결이 안 되면, 아웃바운드 규칙을 확인해야 합니다.
기본적으로 AWS는 모든 아웃바운드 트래픽을 허용하므로, 특별한 이유가 없다면 그대로 두는 것이 좋습니다. 김개발 씨는 이제 이해가 되기 시작했습니다.
"아, 그러니까 인바운드는 외부에서 내 서버로 들어오는 거고, 아웃바운드는 내 서버에서 밖으로 나가는 거네요!" 박클라우드 씨가 미소를 지었습니다. "정확해요.
이제 김개발 씨 서버에 3000번 포트 인바운드 규칙을 추가해 봅시다." 인바운드와 아웃바운드 규칙을 제대로 이해하면 필요한 통신은 허용하고, 위험한 접근은 차단하는 견고한 보안 시스템을 구축할 수 있습니다. 여러분도 서버를 구성할 때 각 규칙의 목적을 명확히 이해하고 설정해 보세요.
실전 팁
💡 - 인바운드 규칙만 설정해도 응답 트래픽은 자동으로 허용됩니다. 보안 그룹은 상태 저장 방화벽이기 때문입니다.
- 아웃바운드 규칙은 기본적으로 모든 트래픽을 허용합니다. 특별한 보안 요구사항이 없다면 변경하지 않는 것이 좋습니다.
- 규칙을 추가할 때는 반드시 Description을 작성해 나중에 규칙의 목적을 쉽게 파악할 수 있도록 합니다.
3. 포트 번호 이해
김개발 씨가 물었습니다. "그런데 왜 80번 포트는 HTTP고, 443번은 HTTPS인가요?
포트 번호는 어떻게 정해지는 건가요?" 박클라우드 씨는 이번에도 쉬운 예시를 들어주었습니다. "포트 번호는 마치 아파트 호수 같은 거예요."
포트 번호는 서버 안에서 실행되는 각 서비스를 구분하는 번호입니다. 마치 한 건물에 여러 사무실이 있고 각 사무실에 호수가 부여된 것처럼, 하나의 서버에서 여러 서비스가 실행될 때 포트 번호로 구분합니다.
잘 알려진 서비스들은 표준 포트 번호를 사용하며, 이를 이해하면 네트워크 통신을 정확히 제어할 수 있습니다.
다음 코드를 살펴봅시다.
# 주요 포트 번호와 용도를 확인하는 예제
import socket
# 잘 알려진 포트 번호 딕셔너리
well_known_ports = {
22: 'SSH (Secure Shell)',
80: 'HTTP (웹 서버)',
443: 'HTTPS (보안 웹 서버)',
3306: 'MySQL 데이터베이스',
5432: 'PostgreSQL 데이터베이스',
27017: 'MongoDB 데이터베이스',
6379: 'Redis 캐시',
3000: 'Node.js 개발 서버',
8080: '대체 HTTP 포트',
}
# 각 포트의 용도 출력
for port, description in well_known_ports.items():
print(f"포트 {port}: {description}")
박클라우드 씨는 다시 한번 비유를 들었습니다. "김개발 씨, 아파트를 생각해 봐요.
101호, 102호, 103호... 이렇게 각 집마다 호수가 있잖아요?
택배 기사가 아파트에 왔을 때, 정확한 호수를 알아야 물건을 전달할 수 있어요." 김개발 씨가 고개를 끄덕였습니다. "네, 맞아요." "서버도 마찬가지예요.
하나의 서버에 웹 서버도 실행되고, 데이터베이스도 실행되고, API 서버도 실행될 수 있어요. 그런데 외부에서 요청이 오면 어느 서비스로 전달해야 할까요?
바로 이때 포트 번호를 사용하는 거예요." 쉽게 비유하자면, 포트 번호는 마치 회사 내선번호와 같습니다. 회사 대표 전화로 전화를 걸면 교환원이 받습니다.
"영업팀은 내선 101번, 기술팀은 102번으로 연결해 드리겠습니다." 마찬가지로 서버의 IP 주소로 접속한 후, 포트 번호로 원하는 서비스에 연결됩니다. 포트 번호가 표준화되지 않던 시절에는 어땠을까요?
초기 인터넷에서는 각 서비스가 임의의 포트를 사용했습니다. 웹 서버가 8000번 포트를 쓸 수도 있고, 5000번을 쓸 수도 있었습니다.
사용자는 웹사이트에 접속할 때마다 "이 사이트는 몇 번 포트를 쓰지?"라고 확인해야 했습니다. 혼란스러웠고, 실수로 잘못된 포트에 접속하는 일도 많았습니다.
바로 이런 문제를 해결하기 위해 IANA라는 국제 기관에서 표준 포트 번호를 정했습니다. 표준 포트를 사용하면 사용자는 포트 번호를 신경 쓰지 않아도 됩니다.
브라우저에 "https://example.com"만 입력하면 자동으로 443번 포트로 접속합니다. 또한 개발자들도 일관된 규칙으로 서비스를 구성할 수 있습니다.
무엇보다 보안 그룹 설정이 훨씬 명확해집니다. 위의 코드를 한 줄씩 살펴보겠습니다.
딕셔너리에 주요 포트 번호와 용도를 정의했습니다. 22번은 SSH로 서버에 원격 접속할 때 사용합니다.
80번과 443번은 각각 HTTP와 HTTPS로 웹 서비스에 사용됩니다. 3306번은 MySQL, 5432번은 PostgreSQL, 27017번은 MongoDB 데이터베이스에 사용됩니다.
개발 환경에서는 3000번이나 8080번을 자주 사용합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 풀스택 웹 애플리케이션을 운영한다고 가정해봅시다. 프론트엔드는 80번과 443번 포트로 서비스됩니다.
백엔드 API는 3000번 포트에서 실행됩니다. MySQL 데이터베이스는 3306번 포트로 연결됩니다.
Redis 캐시는 6379번 포트를 사용합니다. 이렇게 각 서비스마다 표준 포트를 사용하면, 팀원들이 쉽게 이해하고 관리할 수 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 잘 알려진 포트를 임의로 변경하는 것입니다.
"보안을 위해 SSH를 22번이 아니라 12345번으로 바꿔야지"라고 생각할 수 있습니다. 물론 포트를 변경하면 자동화된 공격을 일부 막을 수 있지만, 팀원들이 혼란스러워하고 설정 실수가 발생할 수 있습니다.
차라리 표준 포트를 사용하고, 접근 가능한 IP를 제한하는 것이 더 나은 보안 방법입니다. 김개발 씨는 이제 확실히 이해했습니다.
"아, 그래서 제 Node.js 서버가 3000번 포트에서 실행되는데, 보안 그룹에 3000번 인바운드 규칙이 없으니까 접속이 안 됐던 거네요!" 박클라우드 씨가 웃으며 답했습니다. "정확해요.
이제 3000번 포트를 열어봅시다." 포트 번호를 제대로 이해하면 서버의 각 서비스를 정확히 제어하고, 필요한 통신만 허용하는 세밀한 보안 정책을 구현할 수 있습니다. 여러분도 서비스를 구성할 때 표준 포트를 활용해 보세요.
실전 팁
💡 - 포트 번호는 0부터 65535까지 사용할 수 있습니다. 0-1023번은 잘 알려진 포트, 1024-49151번은 등록된 포트, 49152-65535번은 동적 포트입니다.
- 개발 환경에서는 3000번이나 8080번을 자주 사용하지만, 프로덕션 환경에서는 80번과 443번을 사용하는 것이 일반적입니다.
- 데이터베이스 포트는 절대 외부에 공개하면 안 됩니다. 웹 서버에서만 접근할 수 있도록 보안 그룹을 설정해야 합니다.
4. SSH HTTP HTTPS 허용
"이제 실전입니다." 박클라우드 씨가 AWS 콘솔을 열며 말했습니다. "김개발 씨 서버에 꼭 필요한 세 가지 포트를 열어야 해요.
SSH, HTTP, HTTPS." 김개발 씨는 메모장을 꺼내 적기 시작했습니다. "각각 어떤 용도인가요?"
SSH(22번)는 서버에 안전하게 원격 접속하기 위한 프로토콜이고, HTTP(80번)와 HTTPS(443번)는 웹 서비스를 제공하기 위한 프로토콜입니다. 이 세 가지는 웹 서버를 운영할 때 가장 기본이 되는 포트들입니다.
적절히 설정하면 안전하게 서버를 관리하고, 사용자에게 웹 서비스를 제공할 수 있습니다.
다음 코드를 살펴봅시다.
# SSH, HTTP, HTTPS 포트를 허용하는 보안 그룹 규칙 설정
import boto3
ec2 = boto3.client('ec2', region_name='ap-northeast-2')
security_group_id = 'sg-0123456789abcdef0'
# 여러 인바운드 규칙을 한 번에 추가
ec2.authorize_security_group_ingress(
GroupId=security_group_id,
IpPermissions=[
# SSH - 특정 IP에서만 허용 (회사 IP로 제한)
{'IpProtocol': 'tcp', 'FromPort': 22, 'ToPort': 22,
'IpRanges': [{'CidrIp': '203.0.113.0/24', 'Description': 'SSH from office'}]},
# HTTP - 모든 곳에서 허용
{'IpProtocol': 'tcp', 'FromPort': 80, 'ToPort': 80,
'IpRanges': [{'CidrIp': '0.0.0.0/0', 'Description': 'HTTP from anywhere'}]},
# HTTPS - 모든 곳에서 허용
{'IpProtocol': 'tcp', 'FromPort': 443, 'ToPort': 443,
'IpRanges': [{'CidrIp': '0.0.0.0/0', 'Description': 'HTTPS from anywhere'}]},
]
)
print("SSH, HTTP, HTTPS 규칙이 추가되었습니다.")
박클라우드 씨는 화이트보드에 세 개의 화살표를 그렸습니다. "첫 번째, SSH.
이건 서버에 터미널로 접속할 때 사용해요. 마치 집 열쇠처럼 중요한 통로죠." 김개발 씨가 물었습니다.
"그럼 SSH는 누구나 접속할 수 있게 열어두면 안 되나요?" 박클라우드 씨는 고개를 저었습니다. "절대 안 돼요.
SSH는 서버의 모든 권한에 접근할 수 있는 통로예요. 만약 전체 공개로 열어두면, 전 세계의 해커들이 비밀번호를 무작위로 대입하며 접속을 시도할 거예요.
실제로 몇 분마다 수천 건의 공격이 들어옵니다." 쉽게 비유하자면, SSH는 마치 집 현관문의 열쇠와 같습니다. 아무리 튼튼한 자물쇠를 달아도, 길거리에 열쇠를 두면 누군가 가져가서 문을 열 수 있습니다.
따라서 SSH는 반드시 신뢰할 수 있는 IP 주소에서만 접근할 수 있도록 제한해야 합니다. 회사 IP, 집 IP, 또는 VPN 서버 IP만 허용하는 것이 안전합니다.
반면 HTTP와 HTTPS는 어떨까요? 웹 서비스를 제공하려면 전 세계 누구나 접속할 수 있어야 합니다.
고객이 어느 나라에 있든, 어떤 인터넷 환경을 사용하든 웹사이트에 접속할 수 있어야 합니다. 따라서 HTTP(80번)와 HTTPS(443번) 포트는 모든 IP 주소(0.0.0.0/0)에서 접근을 허용합니다.
HTTP와 HTTPS의 차이는 무엇일까요? HTTP는 평문으로 데이터를 전송합니다.
마치 엽서에 내용을 적어 보내는 것과 같습니다. 누구나 중간에 내용을 볼 수 있습니다.
반면 HTTPS는 암호화된 통신입니다. 편지를 봉투에 넣고 밀봉해서 보내는 것과 같습니다.
중간에 누가 가로채도 내용을 알 수 없습니다. 바로 이런 이유로 현대 웹사이트는 HTTPS를 필수로 사용합니다.
HTTPS를 사용하면 사용자의 비밀번호, 결제 정보, 개인정보가 안전하게 전송됩니다. 또한 검색엔진 순위에도 긍정적인 영향을 줍니다.
브라우저에서도 HTTP 사이트는 "안전하지 않음"이라는 경고를 표시합니다. 무엇보다 사용자의 신뢰를 얻을 수 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. IpPermissions 배열에 세 개의 규칙을 정의했습니다.
첫 번째는 SSH로, FromPort와 ToPort를 22로 설정했습니다. IpRanges에는 회사 IP 대역(203.0.113.0/24)만 지정해 보안을 강화했습니다.
두 번째는 HTTP로 80번 포트를 전체 공개(0.0.0.0/0)로 설정했습니다. 세 번째는 HTTPS로 443번 포트를 전체 공개로 설정했습니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 전자상거래 사이트를 운영한다고 가정해봅시다.
고객들은 HTTPS로 안전하게 쇼핑합니다. 개발팀은 SSH로 서버에 접속해 로그를 확인하고 문제를 해결합니다.
HTTP는 자동으로 HTTPS로 리디렉션되도록 설정합니다. 이렇게 구성하면 보안과 사용자 경험을 모두 만족시킬 수 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 SSH를 전체 공개(0.0.0.0/0)로 열어두는 것입니다.
"일단 되게 만들고 나중에 수정하지 뭐"라는 생각은 매우 위험합니다. AWS CloudWatch 로그를 보면 SSH 포트로 초당 수십 건의 공격 시도가 들어오는 것을 확인할 수 있습니다.
따라서 SSH는 반드시 IP를 제한해야 합니다. 김개발 씨는 이제 확실히 이해했습니다.
"그럼 제 Node.js 서버는 3000번 포트로 실행되니까, 3000번도 열어야 하는 거 아닌가요?" 박클라우드 씨가 답했습니다. "좋은 질문이에요.
실제로는 Nginx 같은 웹 서버를 앞에 두고, 80번과 443번 포트로 들어온 요청을 내부적으로 3000번 포트로 전달하는 방식을 사용해요. 이렇게 하면 3000번 포트는 외부에 노출되지 않아서 더 안전합니다." SSH, HTTP, HTTPS 포트를 제대로 설정하면 안전하게 서버를 관리하고, 사용자에게 신뢰할 수 있는 웹 서비스를 제공할 수 있습니다.
여러분도 새로운 서버를 구성할 때 이 세 가지 포트 설정을 가장 먼저 확인해 보세요.
실전 팁
💡 - SSH는 키 기반 인증을 사용하고, 비밀번호 인증은 비활성화하는 것이 더 안전합니다.
- HTTP는 HTTPS로 자동 리디렉션되도록 설정하는 것이 좋습니다. 사용자가 실수로 HTTP로 접속해도 안전합니다.
- Let's Encrypt를 사용하면 무료로 HTTPS 인증서를 발급받을 수 있습니다. certbot을 활용해 자동 갱신도 가능합니다.
5. 보안 그룹 모범 사례
박클라우드 씨가 커피를 한 모금 마시고 말했습니다. "이제 마지막으로 가장 중요한 이야기를 할게요.
보안 그룹을 제대로 관리하는 방법입니다." 김개발 씨는 자세를 고쳐 앉았습니다. "어떤 것들이 있나요?"
보안 그룹 모범 사례는 안전하고 효율적인 클라우드 환경을 구축하기 위한 핵심 원칙들입니다. 최소 권한 원칙을 적용하고, 명확한 이름과 설명을 사용하며, 정기적으로 규칙을 검토하는 것이 중요합니다.
이러한 원칙들을 따르면 보안 사고를 예방하고, 문제가 발생했을 때 빠르게 대응할 수 있습니다.
다음 코드를 살펴봅시다.
# 보안 그룹 모범 사례를 적용한 예제
import boto3
ec2 = boto3.resource('ec2', region_name='ap-northeast-2')
# 명확한 이름과 설명으로 보안 그룹 생성
security_group = ec2.create_security_group(
GroupName='web-server-sg', # 목적이 명확한 이름
Description='Security group for web servers (HTTP, HTTPS only)',
VpcId='vpc-0123456789abcdef0'
)
# 최소 권한 원칙: 필요한 포트만 열기
security_group.authorize_ingress(
IpPermissions=[
# HTTPS만 허용 (HTTP는 Nginx에서 리디렉션)
{'IpProtocol': 'tcp', 'FromPort': 443, 'ToPort': 443,
'IpRanges': [{'CidrIp': '0.0.0.0/0', 'Description': 'HTTPS for public access'}]},
]
)
# 태그 추가로 관리 편의성 향상
security_group.create_tags(Tags=[
{'Key': 'Name', 'Value': 'Web Server SG'},
{'Key': 'Environment', 'Value': 'Production'},
{'Key': 'Team', 'Value': 'Backend'},
])
print(f"보안 그룹 생성 완료: {security_group.id}")
박클라우드 씨는 지난 3년간 겪었던 여러 보안 사고 사례를 떠올렸습니다. "제가 이전 회사에서 일할 때, 어느 날 갑자기 데이터베이스가 해킹당했어요.
알고 보니 신입 개발자가 실수로 데이터베이스 포트를 전체 공개로 열어뒀던 거죠." 김개발 씨가 놀라며 물었습니다. "그럼 어떻게 됐나요?" "다행히 백업이 있어서 복구는 했지만, 며칠 동안 서비스가 중단됐고, 고객 신뢰도 크게 떨어졌어요.
그 이후로 우리 팀은 보안 그룹 관리에 엄격한 규칙을 적용하기 시작했습니다." 첫 번째 원칙은 최소 권한 원칙입니다. 쉽게 비유하자면, 최소 권한 원칙은 마치 회사의 출입카드 시스템과 같습니다.
인턴은 자기 층만 출입할 수 있고, 일반 직원은 업무 공간에만 출입할 수 있으며, 관리자만 서버실에 들어갈 수 있습니다. 필요한 최소한의 권한만 부여하는 것입니다.
보안 그룹도 마찬가지입니다. 필요한 포트만 열고, 필요한 IP 범위만 허용해야 합니다.
두 번째 원칙은 명확한 이름과 설명 사용입니다. 보안 그룹 이름을 "sg-1", "sg-2"처럼 짓지 마세요.
6개월 후에 "이게 뭐였더라?"라며 혼란스러워할 것입니다. 대신 "web-server-sg", "database-sg", "admin-ssh-sg"처럼 목적이 명확한 이름을 사용하세요.
각 규칙에도 Description을 반드시 작성해 "이 규칙은 왜 존재하는가?"를 명확히 해야 합니다. 세 번째 원칙은 정기적인 규칙 검토입니다.
프로젝트가 진행되면서 새로운 규칙이 추가되고, 오래된 규칙은 삭제되지 않은 채 남아있습니다. 3개월마다 한 번씩 모든 보안 그룹을 검토하고, 더 이상 사용하지 않는 규칙은 삭제하세요.
특히 0.0.0.0/0으로 열린 규칙이 정말 필요한지 다시 확인해야 합니다. 네 번째 원칙은 환경별 분리입니다.
개발 환경, 스테이징 환경, 프로덕션 환경의 보안 그룹을 분리하세요. 개발 환경은 상대적으로 느슨하게 설정할 수 있지만, 프로덕션 환경은 매우 엄격하게 관리해야 합니다.
각 환경에 태그를 붙여서 실수로 프로덕션 보안 그룹을 수정하는 일이 없도록 해야 합니다. 다섯 번째 원칙은 데이터베이스는 절대 공개하지 않기입니다.
데이터베이스 포트(3306, 5432 등)는 절대로 0.0.0.0/0으로 열어서는 안 됩니다. 심지어 회사 IP로도 제한하는 것이 좋습니다.
대신 웹 서버의 보안 그룹 ID를 소스로 지정하면, 해당 보안 그룹에 속한 인스턴스에서만 접근할 수 있습니다. 이렇게 하면 IP 주소가 변경되어도 규칙을 수정할 필요가 없습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 명확한 이름(web-server-sg)과 상세한 설명으로 보안 그룹을 생성합니다.
HTTPS 포트만 열고, HTTP는 Nginx에서 리디렉션 처리합니다. 이것이 최소 권한 원칙입니다.
태그를 추가해 Environment, Team 정보를 기록하면 나중에 검색하고 관리하기 쉽습니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 대형 쇼핑몰 서비스를 운영하는 팀이 있다고 가정해봅시다. 웹 서버 보안 그룹은 HTTPS만 허용합니다.
API 서버 보안 그룹은 웹 서버 보안 그룹에서만 접근을 허용합니다. 데이터베이스 보안 그룹은 API 서버 보안 그룹에서만 접근을 허용합니다.
이렇게 계층별로 보안 그룹을 구성하면, 한 계층이 뚫려도 다음 계층이 보호됩니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 문제가 생기면 "일단 전체 공개로 열어서 되게 만들자"는 생각입니다. 이것은 마치 자물쇠를 아예 제거해 버리는 것과 같습니다.
대신 로그를 확인하고, 어떤 IP에서 어떤 포트로 접근하려는지 정확히 파악한 후, 필요한 규칙만 추가해야 합니다. 김개발 씨는 진지한 표정으로 메모를 정리했습니다.
"최소 권한, 명확한 이름, 정기 검토, 환경 분리, 데이터베이스 보호. 이 다섯 가지를 꼭 기억하겠습니다." 박클라우드 씨가 미소를 지었습니다.
"좋아요. 이 원칙들을 따르면 대부분의 보안 사고를 예방할 수 있어요." 보안 그룹 모범 사례를 따르면 안전한 클라우드 환경을 구축하고, 장기적으로 유지보수하기 쉬운 시스템을 만들 수 있습니다.
여러분도 오늘 배운 원칙들을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - AWS Config를 사용하면 보안 그룹 변경 이력을 추적하고, 규정 위반 사항을 자동으로 감지할 수 있습니다.
- Terraform이나 CloudFormation으로 보안 그룹을 코드로 관리하면, 변경 이력이 Git에 남고 리뷰가 가능합니다.
- 주기적으로 AWS Trusted Advisor를 확인해 불필요하게 열린 포트가 있는지 점검하세요.
6. Elastic IP 할당
마지막으로 박클라우드 씨가 한 가지를 더 알려주었습니다. "김개발 씨, 지금 EC2 인스턴스의 퍼블릭 IP 주소를 메모해 뒀나요?" 김개발 씨가 고개를 끄덕이자, 박클라우드 씨가 말했습니다.
"그런데 서버를 재시작하면 그 IP 주소가 바뀔 거예요."
Elastic IP는 AWS에서 제공하는 고정 공인 IP 주소입니다. 일반 EC2 인스턴스는 재시작할 때마다 퍼블릭 IP가 변경되지만, Elastic IP를 할당하면 IP 주소가 변경되지 않습니다.
이를 통해 도메인 설정을 유지하고, 클라이언트가 일관되게 서버에 접속할 수 있습니다.
다음 코드를 살펴봅시다.
# Elastic IP 할당 및 EC2 인스턴스에 연결하는 예제
import boto3
ec2 = boto3.client('ec2', region_name='ap-northeast-2')
# Elastic IP 할당
allocation = ec2.allocate_address(Domain='vpc')
elastic_ip = allocation['PublicIp']
allocation_id = allocation['AllocationId']
print(f"Elastic IP 할당됨: {elastic_ip}")
# EC2 인스턴스에 Elastic IP 연결
instance_id = 'i-0123456789abcdef0' # 실제 인스턴스 ID로 변경
ec2.associate_address(
AllocationId=allocation_id,
InstanceId=instance_id
)
print(f"인스턴스 {instance_id}에 Elastic IP {elastic_ip}가 연결되었습니다.")
# Elastic IP 정보 조회
addresses = ec2.describe_addresses(AllocationIds=[allocation_id])
for addr in addresses['Addresses']:
print(f"연결된 인스턴스: {addr.get('InstanceId', '없음')}")
김개발 씨가 놀라며 물었습니다. "IP 주소가 바뀐다고요?
그럼 제가 도메인 DNS 설정을 해놨는데, 서버를 재시작하면 다시 설정해야 하나요?" 박클라우드 씨가 고개를 끄덕였습니다. "맞아요.
일반 EC2 인스턴스의 퍼블릭 IP는 동적으로 할당되기 때문에, 인스턴스를 중지했다가 다시 시작하면 새로운 IP를 받게 돼요. 이럴 때 사용하는 게 바로 Elastic IP입니다." 쉽게 비유하자면, Elastic IP는 마치 이사를 가도 계속 쓸 수 있는 휴대전화 번호와 같습니다.
일반 전화기는 이사를 가면 번호가 바뀌지만, 휴대전화는 어디로 이사를 가든 같은 번호를 유지할 수 있습니다. 마찬가지로 Elastic IP를 사용하면 서버를 재시작하거나 심지어 다른 인스턴스로 옮겨도 같은 IP 주소를 사용할 수 있습니다.
Elastic IP가 없던 시절에는 어땠을까요? 클라우드 초기에는 서버를 재시작할 때마다 IP 주소가 바뀌었습니다.
개발자들은 DNS 설정을 수동으로 업데이트하고, 클라이언트 애플리케이션의 설정 파일을 일일이 수정해야 했습니다. 특히 고객사에 제공한 API 서버의 IP가 바뀌면, 모든 고객에게 새로운 IP를 공지하고 설정을 변경해 달라고 요청해야 했습니다.
매우 번거롭고, 서비스 중단 시간도 길었습니다. 바로 이런 문제를 해결하기 위해 AWS는 Elastic IP를 제공합니다.
Elastic IP를 사용하면 서버를 재시작해도 같은 IP를 유지할 수 있습니다. 또한 서버에 문제가 생기면 즉시 다른 정상적인 인스턴스로 IP를 옮겨 장애 복구 시간을 단축할 수 있습니다.
무엇보다 도메인 DNS 설정을 한 번만 하면 계속 사용할 수 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 allocate_address 메서드로 새로운 Elastic IP를 할당받습니다. Domain 파라미터에 'vpc'를 지정하면 VPC용 Elastic IP가 생성됩니다.
반환된 AllocationId를 사용해 associate_address 메서드로 특정 EC2 인스턴스에 연결합니다. describe_addresses로 현재 Elastic IP의 상태와 연결된 인스턴스를 확인할 수 있습니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 B2B SaaS 서비스를 제공하는 회사가 있다고 가정해봅시다.
고객사들은 방화벽 설정에 API 서버의 IP 주소를 등록해야 합니다. 만약 서버를 업그레이드하면서 IP가 바뀌면, 모든 고객사에 공지하고 방화벽 설정을 변경해 달라고 요청해야 합니다.
하지만 Elastic IP를 사용하면 서버를 교체해도 IP가 그대로 유지되므로, 고객에게 영향을 주지 않고 인프라를 업그레이드할 수 있습니다. 하지만 주의할 점도 있습니다.
첫째, Elastic IP는 EC2 인스턴스에 연결되어 있을 때는 무료지만, 할당만 받고 사용하지 않으면 시간당 요금이 부과됩니다. AWS는 IP 주소를 효율적으로 사용하도록 유도하기 위해 이런 정책을 운영합니다.
따라서 사용하지 않는 Elastic IP는 즉시 해제해야 합니다. 둘째, 계정당 Elastic IP 할당 개수에 제한이 있습니다.
기본적으로 리전당 5개까지만 할당받을 수 있습니다. 더 필요하면 AWS 지원팀에 증량을 요청해야 합니다.
셋째, 프로덕션 환경에서는 Elastic IP보다 Load Balancer를 사용하는 것이 더 나은 경우가 많습니다. Load Balancer는 고정 DNS 이름을 제공하고, 여러 인스턴스로 트래픽을 분산하며, 자동으로 건강하지 않은 인스턴스를 제외합니다.
김개발 씨는 이제 완벽히 이해했습니다. "그럼 제 개발 서버에는 Elastic IP를 할당하고, 나중에 프로덕션으로 갈 때는 Load Balancer를 고려해 봐야겠네요." 박클라우드 씨가 웃으며 답했습니다.
"정확해요. 이제 김개발 씨도 AWS 네트워크를 제대로 이해했네요.
보안 그룹, 포트 관리, Elastic IP까지 모두 마스터했습니다." 며칠 후, 김개발 씨는 완벽하게 설정된 EC2 인스턴스에서 웹 서버를 운영하고 있었습니다. 보안 그룹은 필요한 포트만 열려 있었고, SSH는 회사 IP로 제한되어 있었으며, HTTPS로 안전하게 서비스가 제공되고 있었습니다.
무엇보다 Elastic IP 덕분에 서버를 재시작해도 도메인 설정을 변경할 필요가 없었습니다. Elastic IP를 제대로 활용하면 안정적인 네트워크 환경을 구축하고, 서버 관리의 유연성을 크게 높일 수 있습니다.
여러분도 프로젝트의 요구사항에 맞게 Elastic IP 또는 Load Balancer를 선택해 보세요.
실전 팁
💡 - Elastic IP는 EC2 인스턴스뿐만 아니라 NAT Gateway나 Network Interface에도 연결할 수 있습니다.
- 고가용성이 중요한 서비스라면 Route 53 Health Check와 함께 사용해 장애 시 자동으로 다른 인스턴스로 전환할 수 있습니다.
- Elastic IP를 해제할 때는 먼저 인스턴스에서 연결을 해제(disassociate)한 후, IP를 릴리스(release)해야 합니다.
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.