[인턴십] 전용선 환경에서 KCB API 연결 과정(3) - Layer 3 Transparent Proxy 구현
상황
KCB 사업자 신용조회 API 연동이 필요고, KCB 측에서 보안상 전용선 환경에서의 접근만 가능한 상황이었습니다.
사내에는 이미 전용선 구축 업체인 KINX 기반의 AWS Direct Connect 전용선 환경이 구축되어 있었고, 기존 EC2 인스턴스는 구축된 전용선 환경에서 KCB의 개인신용정보조회 엔드포인트에 연결되어 정보를 얻고 있었습니다. 새로 구축한 서버의 IP는 KINX 전용선 화이트리스트에 포함되지 않아 직접 접근이 불가능했습니다. 여기서 화이트리스트란 KINX 측에서 전용선으로 라우팅 되도록 허용한 IP 대역 리스트입니다.
초기에는 Spring Cloud Gateway를 통해 기존 인스턴스에서 구현된 API 호출을 검토했지만, 서비스 도메인이 완전히 달랐습니다.
기존 시스템은 개인 신용조회 배치 서비스였고, 구현해야 하는 것은 사업자 신용조회 실시간 서비스였습니다.
결국 전용선 환경이 구축된 기존 인스턴스를 네트워크 프록시로 활용하는 방식으로 접근했습니다.
인프라 제약 조건
첫 번째 시도로 새로운 VPC에 인스턴스를 생성했으나 실패했습니다. AWS Direct Connect의 Virtual Gateway가 기존 VPC에만 연결되어 있었습니다.
두 번째 시도에서는 새로운 VPC에 생성했던 새로운 인스턴스를 AMI로 복사하여 VGW(Virtual Gateway)가 연결된 기존 인스턴스와 동일 서브넷에 새 인스턴스를 배포했습니다. 이후 기존 인스턴스(전용선과 연결되어 KCB API를 호출하는)와 같은 서브넷, 라우팅 테이블, 인바운드 아웃바운드 규칙을 적용했습니다.
# 수동 설정된 라우팅 테이블
0.0.0.0/0 → igw-0xxxxxxxxxxxxxxxx (인터넷 게이트웨이)
219.xxx.xxx.x/32 → vgw-0xxxxxxxxxxxxxxxx (KCB 운영 서버)
219.xxx.xxx.x/32 → vgw-0xxxxxxxxxxxxxxxx (KCB 테스트 서버)
219.xxx.xxx.x/32 → vgw-0xxxxxxxxxxxxxxxx (KCB 추가 서버)
하지만 라우팅 테이블, 보안 그룹 등 모든 설정을 기존 인스턴스와 동일했음에도 불구하고 새 인스턴스에서 전용선을 통한 KCB 연결에 실패했습니다. 더욱 의외였던 것은 traceroute 명령으로 확인한 네트워크 패킷의 이동 결과가 완전히 다르게 나타났다는 점입니다.
VGW로 라우팅되는 라우팅 테이블을 공유하고 있었기 때문에 두 인스턴스가 모두 VGW로 라우팅 되어 전용선에 연결될 줄 알았지만 결과적으로 아래와 같이 새 인스턴스에서는 KINX 전용선으로 라우팅 되지 못하고 공용선으로 라우팅 되었습니다.
# 새 인스턴스(10.x.x.xxx)에서의 traceroute - 인터넷 게이트웨이 경로
1 169.254.xxx.xxx → AWS 내부 라우터
2 169.254.xxx.xxx → AWS 경계 라우터
3 169.254.xxx.xxx → AWS 에지
4 * * * → 외부 ISP에서 차단
# 기존 인스턴스(10.y.y.yyy)에서의 traceroute - VGW 경로
1 169.254.xxx.xxx → AWS 내부 라우터
2 169.254.xxx.xxx → AWS 경계 라우터
3 169.254.xxx.xxx → AWS 에지
4 192.168.xxx.xxx → KINX 전용선 진입점
5 192.168.xxx.xxx → KINX 내부 라우터
핵심 문제는 IP 대역 차이였습니다.
KINX 전용선은 소스 IP 주소 기반 접근 제어를 적용하고 있었고, 사전 승인된 특정 대역에서만 전용선을 사용하여 KCB API 서버로의 접근을 허용했습니다. 동일한 서브넷에 위치해도 새로 생성한 인스턴스의 새로운 IP는 KINX가 허용하는 IP 대역이 아니었던 것입니다.
허용된 IP 대역에서 오는 트래픽은 VGW를 통해 전용선으로 라우팅 되지만, 허용되지 않은 IP 대역에서 오는 트래픽은 일반 인터넷 경로로 전송되어 최종적으로 차단되는 구조였습니다. KINX 측에서 소스 IP 주소를 기반으로 트래픽을 분류하고 있기 때문에 새로운 인스턴스 IP에서 전용선을 사용하려면 KINX에 IP 대역 추가를 요청해야 했습니다.
KINX에 IP 대역 추가 없이 기능을 개발하기 위했기 때문에 결국 기존 전용선 환경이 구축된 인스턴스를 네트워크 프록시로 사용하는 방법으로 새 인스턴스 IP로 간접적으로 KCB에 접근하도록 구현했습니다.
Layer 3 Transparent Proxy 설계
전용선과 연결되어 KCB와 소통하고 있는데 인스턴스를 OSI 7계층 모델에서 네트워크 계층에서 동작하는 프록시를 구현하기로 결정했습니다.
Layer 3 프록시는 IP 패킷의 헤더 정보만 수정하고 페이로드는 그대로 전달하는 방식으로, 애플리케이션 레벨에서는 완전히 투명하게 동작합니다. HTTP 헤더나 데이터를 전혀 건드리지 않고 네트워크 주소만 변경하기 때문에 애플리케이션 코드 수정 없이 새 인스턴스에서 마치 KCB와 직접 통신하는 것처럼 동작합니다.
DNS 리다이렉션 구현
애플리케이션 레벨에서의 투명성을 확보하기 위해 hosts 파일을 통한 DNS 오버라이드를 구현했습니다.
새 인스턴스에서는 KCB의 DNS(Domain Name System) 주소로 요청을 보내지만, 아래 hosts 파일의 설정을 통해 KCB의 DNS 요청이 들어오면 이를 기존 인스턴스로 연결합니다. 따라서 새 인스턴스에서는 마치 KCB DNS로 요청을 보내는 듯싶지만, 실제 네트워크 패킷은 기존 인스턴스(10.y.y.yyy)로 전달됩니다.
# 새 인스턴스의 /etc/hosts 파일에 추가
echo "10.y.y.yyy KCB-DNS" | sudo tee -a /etc/hosts
Linux의 이름 해석 순서는 /etc/nsswitch.conf에 정의되어 있으며, 일반적으로 files, dns, myhostname 순서로 검색합니다. 먼저 /etc/hosts 파일을 검색하고, 찾지 못하면 DNS 서버에 질의하며, 마지막으로 systemd-resolved에 질의하는 방식입니다. hosts 파일에 도메인을 프록시 서버 IP로 매핑해 두면 애플리케이션이 KCB 도메인을 질의할 때 자동으로 프록시 서버로 연결되어 코드 수정 없이 투명한 프록시가 구현됩니다.
Linux Netfilter Framework 구현
이제 새 인스턴스에서 기존 인스턴스로 보낸 네트워크 패킷을 수정해야 합니다. 지금부터의 작업들은 기존 인스턴스에서 진행됩니다.
먼저 iptables를 통해 Linux 커널의 Netfilter 프레임워크를 제어하여 네트워크 패킷의 목적지 IP와 도착지 IP를 변경합니다.
Netfilter는 패킷이 커널 네트워킹 스택을 통과하는 여러 지점에서 hook을 제공하며, iptables는 이러한 hook 지점에서의 패킷 처리 규칙을 설정하는 사용자 공간 도구입니다.
[linux] netfilter 소개 및 동작 방식
개요 linux의 netfilter가 무엇인지 알아보겠습니다. netfilter란? Netfilter는 리눅스 커널에서 네트워크 패킷을 처리하는 프레임워크입니다 전체 구성도 https://en.wikipedia.org/wiki/Netfilter 에 잘나와 있습니
doitnow-man.tistory.com
Netfilter와 iptables의 개념은 위 글을 참고하면 좋을 것 같습니다.
아래 코드는 전용선을 통한 KCB와 연결된 기존 인스턴스에 했던 작업들입니다.
# IP 포워딩 활성화 - 커널이 라우터 역할 수행
sudo sysctl -w net.ipv4.ip_forward=1
# PREROUTING에서 DNAT - 들어오는 패킷의 목적지 변경
sudo iptables -t nat -A PREROUTING -p tcp --dport 3xxxx \
-j DNAT --to-destination 219.xxx.xxx.xxx:3xxxx
# POSTROUTING에서 MASQUERADE - 나가는 패킷의 소스 변경
sudo iptables -t nat -A POSTROUTING -d 219.xxx.xxx.xxx \
-j MASQUERADE
# FORWARD 체인에서 패킷 전달 허용
sudo iptables -A FORWARD -p tcp --dport 3xxxx -j ACCEPT
첫 번째 명령어인 IP 포워딩 활성화는 Linux 커널이 라우터 역할을 수행하도록 합니다.
Linux 커널은 라우터 역할을 수행하지 않고 엔드 호스트로 동작하여 자신에게 오는 패킷만 처리합니다.
때문에 이 설정으로 다른 목적지로 향하는 패킷도 전달할 수 있게 됩니다. 이는 커널의 네트워킹 스택 동작을 변경하는 중요한 설정입니다.
두 번째 명령어 PREROUTING 체인에서의 DNAT 규칙은 들어오는 패킷의 목적지 주소를 실제 KCB 서버 주소로 변경합니다.
세 번째 명령어 POSTROUTING 체인의 MASQUERADE 규칙은 나가는 패킷의 소스 주소를 프록시 서버인 기존 서버의 IP로 변경합니다.
내부적으로는 나가는 인터페이스의 IP 주소를 감지하고, 사용 가능한 포트 번호 풀에서 임의 포트를 선택하여 소스 IP와 포트를 변경한 다음 Connection Tracking 테이블에 매핑 정보를 기록합니다.
이렇게 iptables을 통해 기존 인스턴스에서 네트워크 패킷의 ip를 변경해 주면 어떻게 될까요?
1. 새 인스턴스 -> 기존 인스턴스
출발지 IP: 새 인스턴스 IP
목적지 IP: KCB DNS hosts 파일에 의해 기존 인스턴스 IP로 보내짐
2. 기존 인스턴스에서 1번 과정의 패킷을 iptables을 통해 DNAT, SNAT을 조작
출발지 IP: POSTROUTING의 SNAT(MASQUERADE) 규칙에 의해 기존 인스턴스 IP (KINX에서 허용한 IP 대역)로 설정
목적지 IP: PREROUTING의 DNAT 규칙에 의해 KCB IP로 설정
3. 기존 인스턴스 -> KINX 전용선 -> KCB
KINX와 KCB에서 네트워크 패킷의 출발지 IP, 목적지 IP를 확인하고
기존 인스턴스의 요청으로 판단하여 연결 허용
Connection Tracking 메커니즘
Linux의 conntrack 시스템이 NAT 변환 정보를 자동으로 추적하여 양방향 통신을 처리합니다. 요청 패킷이 MASQUERADE 규칙을 거칠 때 원본 연결 정보와 변환된 연결 정보를 테이블에 저장하고, 응답 패킷이 돌아올 때 이 정보를 참조하여 자동으로 역변환을 수행합니다. 각 연결 엔트리에는 프로토콜 타입, 연결 타임아웃, 연결 상태, 원본 연결 정보와 응답 패킷용 역변환 정보가 포함됩니다. 이를 통해 복잡한 상태 관리 없이 안정적인 양방향 통신이 가능합니다.
패킷 변환 과정 분석
실제 패킷 변환 과정을 단계별로 분석해 보면 다음과 같습니다.
먼저 애플리케이션에서 KCB API에 POST 요청을 생성하면 DNS 해석 과정에서 hosts 파일에 의해 도메인이 프록시 서버 IP로 해석됩니다. 이후 새 인스턴스에서 프록시 서버로 TCP 연결을 시도하는 패킷이 생성되는데, 이 패킷이 프록시 서버에 도착하면 PREROUTING 체인에서 DNAT가 적용되어 목적지가 실제 KCB 서버 주소로 변경됩니다.
변경된 패킷은 커널 라우팅 테이블을 참조하여 KCB 서버로의 경로가 결정되고, POSTROUTING 체인에서 MASQUERADE가 적용되어 소스 주소가 프록시 서버의 허용된 IP로 변경됩니다. 이때 Connection Tracking 테이블에 원본 연결 정보와 변환된 연결 정보가 기록됩니다. KCB 서버에서 응답이 돌아오면 conntrack 시스템이 테이블을 검색하여 매칭되는 엔트리를 찾고 자동으로 역변환을 수행해 최종적으로 새 인스턴스의 애플리케이션이 정상적인 응답을 받게 됩니다.
중요한 점은 이 전 과정에서 HTTP 페이로드는 전혀 건드려지지 않는다는 것입니다. Ethernet, IP, TCP 헤더의 주소 정보만 변경되고 실제 HTTP 데이터는 그대로 전달되기 때문에 애플리케이션 입장에서는 KCB 서버와 직접 통신하는 것과 완전히 동일하게 동작합니다.
구현된 네트워크 흐름은 다음과 같습니다.
새 인스턴스(10.x.x.xxx)
→ stapi.koreacb.com:3xxx (hosts 파일로 10.y.y.yyy로 해석)
→ 기존 인스턴스(10.y.y.yyy) (iptables DNAT로 219.xxx.xxx.xxx:3xxxx으로 전달)
→ VGW → KINX 전용선 → KCB 서버
다른 해결 방법과의 비교
Application Load Balancer를 고려했으나 근본적인 한계가 있었습니다. ALB는 Layer 7에서 동작하며 요청을 새 서버로 전달만 할 뿐, 실제 외부 API 호출은 새 서버가 자기 IP로 수행하기 때문입니다. 따라서 Layer 3의 IP 화이트리스트 문제를 해결할 수 없습니다. 마찬가지로 API Gateway 방식도 최종적으로 새 서버에서 외부 API를 호출하는 구조는 변하지 않기 때문에 동일한 문제를 가집니다.
반면 iptables NAT를 이용한 Layer 3 프록시는 실제 KCB API 호출을 허용된 IP가 대신 수행하는 구조입니다. 새 서버는 프록시 서버로 요청을 보내고, 프록시 서버가 자기 IP로 위장하여 KCB에 요청함으로써 KINX의 IP 기반 접근 제어를 우회할 수 있었습니다.
검증 및 디버깅
구현 후에는 여러 레벨에서 검증을 수행했습니다. 먼저 기본적인 네트워크 연결성을 테스트했습니다.
# 새로운 인스턴스에서 기존 인스턴스에 연결 성공 확인
nc -zv 10.y.y.yyy 3xxxx
Connection to 10.y.y.yyy 3xxxx port [tcp/*] succeeded!
# 도메인을 통한 투명한 연결
nc -zv stapi.koreacb.com 3xxxx
Connection to stapi.koreacb.com (10.y.y.yyy) 3xxxx port [tcp/*] succeeded!
프록시 서버에 직접 연결과 도메인을 통한 투명 연결 모두 성공함을 확인했습니다. 실제 KCB 서버에서는 정상적인 보안 응답을 받을 수 있었는데, HTTP 요청에 대한 "SSL-enabled server port" 에러와 HTTPS 요청에 대한 "400 Unknown Reason" 응답은 모두 KCB 서버가 직접 생성한 응답으로 네트워크 연결이 성공했음을 의미합니다.
디버깅 과정에서는 tcpdump를 이용한 실시간 패킷 캡처를 통해 양방향 트래픽을 모니터링했고, iptables 규칙 통계를 확인하여 실제 패킷이 규칙을 통과하는지 실시간으로 추적했습니다. Connection Tracking 테이블을 조회하여 연결 상태와 매핑 정보가 올바르게 기록되고 있음도 확인할 수 있었습니다.
결론
Layer 3 Transparent Proxy 방식은 애플리케이션 코드 변경 없이 네트워크 제약을 우회할 수 있는 효과적인 해결책이었습니다. Linux Netfilter Framework의 NAT 기능과 Connection Tracking 메커니즘을 활용하여 안정적이고 투명한 프록시를 구현할 수 있었으며, 특히 엄격한 IP 기반 접근 제어 환경에서 기존 인프라를 재활용하면서도 새로운 인스턴스에서도 전용선 환경으로 KCB API에 접근할 수 있게 되었습니다.