프록시 서버를 활용한 API 화이트리스트 우회
회사에서 외부 API와 통신할 일이 생겼는데, 해당 API 서버가 화이트리스트 방식으로 특정 IP에서만 요청을 받을 수 있도록 제한되어 있었다. 보통 이런 경우 NAT 게이트웨이를 통해 할당받은 Public IP로 통신을 한다. 하지만 문제는 로컬 개발 환경에서는 VPC 상의 NAT 게이트웨이를 통할수 없어 직접 API 호출을 할 수 없다는 점이었다. API 테스트를 할때마다 배포를 할 순 없기 때문에, EC2 기반의 프록시 서버를 활용해 로컬에서도 외부 API에 접근할 수 있도록 구성했다.
NAT 게이트웨이와 프록시 서버의 차이
NAT 게이트웨이와 프록시 서버는 모두 내부 네트워크에서 외부로 나갈 때 사용하는 방식이지만, 역할과 방식이 다르다.
NAT 게이트웨이 | 프록시 서버(EC2 기반) | |
주요 목적 | 프라이빗 서브넷의 리소스가 인터넷에 접근할 수 있도록 함 | 특정 트래픽(예: HTTP, HTTPS)을 프록시 서버를 통해 전달 |
사용 방식 | 라우팅 테이블을 통해 프라이빗 서브넷에서 NAT 게이트웨이를 거쳐 인터넷 접속 | 클라이언트가 직접 프록시 서버를 사용하도록 설정해야 함 |
적용 대상 | EKS 노드, DB, 내부 서비스 등 전체 네트워크 | 특정 애플리케이션이나 서비스 (예: API 요청) |
운영 방식 | AWS에서 관리, 별도 설정 없이 사용 가능 | EC2에서 Squid 설치 및 관리 필요 |
NAT 게이트웨이는 VPC 네트워크 레벨에서 전체 트래픽을 제어하는 방식이고, 프록시 서버는 특정 요청을 우회하는 방식이다. EKS 클러스터에서 나가는 트래픽을 특정 IP로 통일하려면 NAT 게이트웨이를 설정해야 한다.
AWS EC2 프록시 서버 설정
AWS EC2 인스턴스를 프록시 서버로 설정하고 인스턴스의 Public IP를 화이트리스트에 등록 하면, 앞으로 이 프록시 서버를 통해 외부 API로 요청을 보낼 수 있다. 프록시 서버는 Squid를 사용 했다.
Squid 설치 및 설정
# 설치
sudo yum install -y squid
# 설정 수정 (/etc/squid/squid.conf)
http_port 3128
acl allowed_clients src 0.0.0.0/0 # 모든 IP 허용
http_access allow allowed_clients
# 재시작
sudo systemctl restart squid
이렇게 하면 EC2 인스턴스가 프록시 서버로 동작하게 된다.
SSH 터널링
배포된 프록시 서버를 활용하기 위해 로컬에서 SSH 터널링을 사용했다.
SSH 터널링이란?
- Secure Shell(SSH) 프로토콜을 이용해 두 지점 간에 암호화된 통신 채널을 생성하는 기술
- 네트워크 경로를 우회하거나 보안이 취약한 네트워크 상에서도 안전하게 데이터를 주고 받을 수 있음
- 포트 포워딩 사용
- 한쪽의 특정 포트로 들어오는 트래픽을 SSH 서버를 통해 다른쪽 특정 포트로 전달
- 동적 포워딩으로 로컬머신의 한 포트를 SOCKS 프록시처럼 사용가능
SOCKS란?
- 네트워크 프록시 프로토콜 중 하나로, 클라이언트와 서버 간 통신을 중계하는 역할
- 세션 레벨에서 작동하기 때문에 클라이언트 애플리케이션에서 SOCKS 프록시를 설정하면 모든 트래픽이 해당 프록시를 통해 전달됨
- SSH 터널링에서 -D 옵션을 사용할 경우, 로컬 포트가 동적 포트 포워딩 역할을 하게되어 SOCKS 프록시 서버로 동작하게 됨
우리는 EC2를 이용할 것이기 때문에 아래 명령어를 실행해 프록시 서버를 터널링 할 수 있다.
ssh -i “KEY.pem" -D 8080 ec2-user@<EC2_PUBLIC_IP> -N
- -i <KEY.pem> : AWS EC2에 접속할 때 필요한 SSH 키 사용.
- -D 8080 : 로컬에서 8080 포트를 SOCKS 프록시로 사용.
- -N : 원격 명령을 실행하지 않고, 포트 포워딩만 수행.
로컬에서 파이썬으로 프록시 테스트
로컬에서 SSH 터널링을 통해 생성된 SOCKS 프록시를 이용해 파이썬으로 API 요청을 보낼 수 있다.
이번 작업에는 aiohttp 라이브러리를 사용했으며 아래는 간단한 예시 코드이다.
from aiohttp import ClientSession
from aiohttp_socks import ProxyConnector
from http import HTTPStatus
# SOCKS5 프록시 서버 설정 (SSH 터널링을 통해 로컬 8080 포트 사용)
connector = ProxyConnector.from_url("socks5://127.0.0.1:8080")
async with aiohttp.ClientSession(connector=connector) as session:
async with session.post(url, headers=headers, json=request) as response:
if response.status == HTTPStatus.OK:
result = await response.json()
return result
else:
error_message = await response.text()
raise ValueError(f"오류 발생: HTTP {response.status}, 메시지: {error_message}")
요청 흐름 정리
- 로컬에서 SSH 터널링 실행
- ssh -D 8080 명령어로 SSH 터널을 열어, 로컬의 8080 포트를 SOCKS5 프록시로 설정
- 로컬에서 API 요청 전송
- API 서버를 실행(예: uvicorn FastAPI)해서 요청을 보내거나, 파이썬을 이용해 직접 요청을 보냄
- 파이썬 클라이언트가 로컬의 8080 포트를 통해 SOCKS5 프록시 요청 전송
- 프록시 서버로 요청 전달
- EC2의 프록시 서버로 요청이 전달되고 프록시 서버의 IP가 외부에 노출
- 외부 API로 요청 전송 및 응답 반환
- 화이트리스트에 등록된 EC2 인스턴스의 IP를 사용하여 외부 API 서버에 요청
- 외부 API에서 받은 응답을 다시 로컬 클라이언트로 전달
EC2 인스턴스에 Squid를 설치하여 프록시 서버로 구성하고, SSH 터널링을 통해 로컬 환경에서도 해당 프록시를 사용할 수 있게 만들었다. 이제 로컬 환경에서도 외부 API 테스트가 가능하다.