Profile picture

[Linux] 리눅스 서버를 게이트웨이로 만드는 방법 (iptables)

JaehyoJJAng2025년 10월 19일

개요

"Main 서버는 외부 인터넷 접속이 가능한데, 이 서버에만 연결된 Sub 서버는 왜 인터넷이 안 될까?"


apt 업데이트나 curl 명령어가 동작하지 않는 '오프라인 서버'는 개발 및 운영에 큰 불편함을 줍니다.


이 문제를 해결하기 위해 고가의 전용 네트워크 장비가 항상 필요한 것은 아닙니다.

리눅스 서버의 네트워킹 기능을 활용하면, Main 서버를 Sub 서버의 모든 트래픽을 처리하는 게이트웨이 로 만들 수 있습니다!


이번 게시글에서는 Main 서버가 Sub 서버의 인터넷 요청을 받아 외부로 전달하고,

그 응답을 다시 Sub 서버로 돌려주도록 설정하는 전체 실습 과정을 다룹니다.


실습 핵심 내용

이번 실습에서는 Main 서버에 두 가지 핵심 리눅스 네트워크 기능을 설정할 예정입니다.

  1. IP 포워딩(IP Forwarding) 활성화 리눅스 커널은 기본적으로 자신이 목적지가 아닌 패킷을 받으면 버리도록 설정되어 있습니다. sysctl 설정을 변경하여, Sub 서버에서 들어온 외부행 패킷을 다른 네트워크 인터페이스(외부망)로 '전달(forward)' 할 수 있도록 커널의 라우팅 기능을 활성화합니다.

  2. NAT (네트워크 주소 변환) 설정 Sub 서버는 사설 IP 대역(10.10.10.x)을 사용하므로 외부 인터넷과 직접 통신할 수 없습니다. 리눅스의 방화벽 도구인 iptables 를 사용해, Sub 서버의 사설 IP를 Main 서버의 공인 IP로 '변환(MASQUERADE)' 하는 NAT 규칙을 적용합니다.



게이트웨이를 왜 리눅스 서버로?

"라우터는 라우터 장비가 하고, 방화벽은 방화벽 장비가 해야지!" 라고 보통 생각들을 하시겠죠?


리눅스 서버를 게이트웨이로 사용하는 이유는 단순합니다.

그 이유는 압도적인 유연성과 비용 효율성 때문이죠.


유연성

  • 방화벽(Firewall): iptablesnftables를 이용해 패킷 레벨의 정교한 방화벽 규칙을 마음대로 설정할 수 있습니다.
  • VPN 서버: WireGuardOpenVPN을 설치하면, 이 게이트웨이 서버가 곧바로 VPN 서버 역할을 겸할 수 있습니다. (재택근무자 연결용)
  • 모니터링 & 로깅: tcpdump로 패킷을 분석하고, Prometheus로 트래픽을 모니터링하며, 모든 접속 로그를 원하는 대로 저장하고 분석할 수 있습니다.
  • 기타 기능 추가: 웹 필터링을 위한 프록시 서버(Squid), 광고 차단(Pi-hole), 침입 탐지 시스템(Suricata/Snort) 등 원하는 모든 오픈소스를 설치해 기능을 무한히 확장할 수 있습니다.

비용 효율성

고성능 라우터나 방화벽 장비는 매우 비싸죠?

하지만 리눅스 OS는 무료이고, 일반적인 PC나 저렴한 서버 하드웨어에서도 훌륭한 성능을 내줍니다.


실습 구성도

실습에 사용될 구성도는 대략 다음과 같습니다! image


NAT Gateway 서버를 통해 Private Network 트래픽들이 eno0 포트로 NAT 되어 통신이 가능해지도록 설정해보겠습니다!


PART 1: 기본 설정 - 희망편

이론대로라면 성공해야 하는 기본 설정에 대해 알아보겠습니다.

1. Sub 서버 설정

Sub 서버의 네트워크 설정에서 게이트웨이를 Main 서버의 내부 IP(10.10.10.1)로 지정해줍시다.


2. Main 서버 설정

1. IP 포워딩 활성화

/etc/sysctl.conf 파일에서 net.ipv4.ip_forward=1로 설정하고 sysctl -p로 적용합시다.


2. NAT 규칙 추가

"Sub 서버 대역(10.10.10.0/24)에서 온 트래픽이 외부 인터페이스(eno0)로 나갈 때, 주소를 변환(MASQUERADE)하라"는 규칙을 추가합니다.

iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o eno1 -j MASQUERADE

여기까지 하고 Sub 서버에서 ping 1.1.1.1을 날렸는데 .... 안됩니다.


PART 2: 트러블슈팅 - 절망편

"왜 안되지" 물음과 함께 기나긴 삽질의 터널로 들어섭니다 ...


추적 1: 도커의 FORWARD 체인

가장 먼저 iptables-save로 전체 규칙을 확인했습니다.


그러자 *filter 테이블의 FORWARD 체인이 Docker와 Libvirt(가상머신)에 의해 복잡하게 설정된 것을 발견했죠. image


이 내용은 마치 안내 데스크(NAT)는 일을 잘하고 있지만,

건물 내부 보안 검색대(FORWARD 체인) 가 "Docker 관련 통행증 없는 사람들은 모두 막으세요!" 라며 Sub 서버의 트래픽을 모두 차단하는 상황이었습니다.


이를 해결하기 위해서 Docker가 사용자를 위해 열어둔 전용 통로 DOCKER-USER"Sub 서버에서 온 건 통과시켜줘!" 라는 허용 규칙을 추가했습니다.

iptables -I DOCKER-USER -s 10.10.10.0/24 -j ACCEPT

그러나 여전히 ping은 실패합니다 ..


추적 2: 사라진 응답 추적 - tcpdump

tcpdump 라는 도구를 통해서 패킷의 흐름을 파악해야 했습니다.


메인 서버에서 다음 명령을 실행하여 내부 인터페이스(eno1) 트래픽을 모니터링 해보기로 하였습니다.

tcpdump -i eno1 host 10.10.10.101 and icmp

결과는 다음과 같았습니다.

IP 10.10.10.101 > one.one.one.one: ICMP echo request
IP 10.10.10.101 > one.one.one.one: ICMP echo request

Sub 서버가 보낸 요청(request)은 Main 서버에 잘 도착하고 있네요.

하지만 응답(reply) 패킷은 전혀 보이고 있지 않습니다.


이는 돌아오는 길에 명백한 문제가 있다는 거겠죠?


추적 3: 잘못 찍힌 iptables 설정 값

마지막으로 iptablesnat 테이블을 다시 한번 들여다봤습니다.

그리고 마침내 해당 테이블에 설정 값이 잘못되었다는 것을 확인하였습니다.

# iptables -t nat -L -v -n 의 출력 결과
...
pkts bytes target      prot opt in out     source           destination
 10  1897 MASQUERADE  all  --  * eno1    10.10.10.0/24    0.0.0.0/0

보이시나요? out eno1?


이 규칙의 의미는 패킷이 eno1(내부 문)로 나갈 때 주소를 변환하라 는 것이었습니다.


하지만 인터넷 통신이 되려면 어디로 나가야 합니까?


ISP와 연결된 인터페이스인 eno0으로 나가야 합니다!

해외로 나가는 소포에 '인천공항 출국장'(eno0) 도장을 찍어야 하는데, 실수로 '사내 게이트'(eno1) 도장을 찍으라고 설정해 둔 것입니다. 당연히 패킷은 주소 변환에 실패한 채 사설 IP 그대로 외부로 나가려다 버려지고 있었던 거죠.


PART 3: 해결

원인을 파악했으니 수정을 해줘야겠죠.


1. 잘못된 규칙 삭제

실수로 만들었던 설정을 다시 삭제해줍니다.

iptables -t nat -D POSTROUTING -s 10.10.10.0/24 -o eno1 -j MASQUERADE

2. 올바른 규칙 추가

2-1. NAT 규칙 (주소 변환)

"Sub 서버에서 온 트래픽이 외부 정문(eno0)으로 나갈 때 주소를 변환하라"는 올바른 규칙을 추가합니다.

iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o eno0 -j MASQUERADE

2-2. FORWARD 규칙 (통행 허가)

"돌아오는 트래픽"과 "새로 나가는 트래픽" 모두를 허용하는 규칙을 DOCKER-USER 체인에 추가하겠습니다.

# 이미 연결된 통신(모든 응답 패킷)을 최우선으로 허용
iptables -I DOCKER-USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Sub 서버에서 시작되는 새로운 통신을 허용
sudo iptables -I DOCKER-USER 2 -s 10.10.10.0/24 -j ACCEPT

이제 Sub 서버에서 외부 통신을 시도하면 정상적으로 될겁니다!


Loading script...