Contents

Linux IP forwarding

IP Forwardingrouting와 같은 말이다. 리눅스 운영체제에서 말하는 IP 포워딩은 커널 기반의 라우팅 포워딩을 의미한다.

라우터는 여러 개의 네트워크 인터페이스를 가지고 있다. 라우터는 하나의 인터페이스로 들어온 패킷을 읽어서 일치하는 서브넷을 가지는 다른 네트워크 인터페이스를 패킷을 포워딩(forwarding)하는 일을 한다.

리눅스 시스템이 두개의 NIC를 가지고 있다고 가정해보자. NIC 1은 192.168.2.1/24 이고 NIC 2는 192.168.5.1/24다. 만약 포워딩 기능이 활성화 돼 있다면, NIC 1으로 들어온 패킷의 목적지가 192.168.5.5라면, 운영체제는 패킷을 NIC 2로 보낸다. 패킷의 목적지가 어떤 인터페이스의 서브넷과도 매칭되지 않는다면, 디폴트 게이트웨이(Default gateway)로 전송한다.

모든 리눅스 운영체제는 라우팅을 할 수 있지만 보통 라우터를 만들려고 사용하지는 않기 때문에, 기본 설정은 IP 포워딩을 비활성화다. IP 포워딩 기능을 사용하기 위해서는 sysctl로 포워딩 기능을 활성화 해야 한다. 먼저 IP 포워딩 설정을 가져와보자.
# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0
-w 옵션으로 값을 쓸 수 있다.
# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

설정된 값은 리눅스 프로시져 파일 시스템에서 확인 할 수 있다. systemctl로 제어 할 수 있는 것들은 /proc/sys 디렉토리 밑에서 찾아볼 수 있다.
# cat /proc/sys/net/ipv4/ip_forward
1

리눅스 운영체제를 라우터나 게이트웨이 용도로 사용하려면 IP 포워딩을 활성화 해야 한다.

응용

가상환경에서의 포워딩

가상화의 경우(VM기반 이든 컨테이너 기반이든) 구성할 수 있는 네트워크는 아래의 3개 중 하나가 될 것이다.
  1. 브릿지 네트워크 구성 : 기본 구성이다.
  2. 오버레이 네트워크 구성 : OpenVSwitch와 VxLAN 조합으로 구성한다.
  3. 플랫(Flat) 네트워크 구성 : 가상화를 위한 네트워크를 만들지 않고, 인스턴스가 포함된 서브넷 네트워크를 그대로 사용한다.
1과 2의 경우 리눅스 입장에서는 서로 다른 서브넷을 가진 가상의 인터페이스가 연결되는 것이다. 따라서 IP 포워딩을 활성화해서 패킷 포워딩이 가능하도록 해야 한다.

NAT에서의 포워딩

 NAT Gateway 구성

위 그림은 NAT게이트웨이의 구성을 보여주고 있다. NAT 게이트웨이는 2개의 인터페이스를 가지고 있다. eth1로 들어온 패킷은 SNAT을 이용해서 인터넷으로 패킷을 보낸다. NAT는 서로 다른 서브넷으로 패킷을 보낼 수 있어야 하므로 IP 포워딩 기술을 사용해야 한다.

네트워크 디바이스 별 IP 포워딩 설정

가상화 환경을 구성하는 인스턴스들은 트래픽을 분리하기 위해서 최소한 3개 이상의 NIC를 가진다. 서비스 트래픽, 관리 트래픽, 스토리지 트래픽등으로 나눌 수 있을 것이다.

테스트를 위해서 아래의 구성을 만들었다.

 멀티닉 테스트 환경

VirtualBox로 구성했다. 두 개의 호스트 전용 네트워크를 만들었다. NET-1은 서비스 네트워크로 192.168.56.0/24 서브넷으로 설정했다. NET-2는 스토리지 네트워크로 192.168.57.0/24로 설정했다. 두 개의 인스턴스를 만들었다. 하나는 Service Instance로 컨테이너를 실행하기 위해, 다른 하나는 Storage Instance로 Service Instance에 볼륨을 제공하기 위해서 사용한다.

리눅스 브릿지를 이용하는 도커의 기본 네트워크는 마스커레이딩(Masquerade)을 이용해서 컨테이너의 패킷을 바깥으로 보낸다. 마스커레이딩은 iptables로 구현한다. 도커 컨테이너의 iptables 규칙은 다음과 같다.
iptables -t NAT -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

Service Instance를 보자. 라우팅 테이블은 아래와 같다.
# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.56.1    0.0.0.0         UG    0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.56.0    0.0.0.0         255.255.255.0   U     0      0        0 eth0
192.168.57.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1
  • 172.17.0.0/16 : 컨테이너 네트워크
  • 192.168.56.0/24 : 서비스 네트워크
  • 192.168.57.0/24 : 스토리지 네트워크
Service Instance는 Volume Manager 프로그램을 이용해서 컨테이너에 볼륨을 제공한다. 당연하지만 유저의 컨테이너는 스토리지 네트워크에 접근 하면 안된다. 하지만 지금의 라우팅 테이블로는 유저의 패킷이 eth1으로 흐르는 걸 막을 수 없다. Service Instance에서 컨테이너를 만들고 스토리지 인스턴스로 ping 테스트를 해봤다.
root@2e5c0fc39174:/# ping 192.168.57.5
PING 192.168.57.5 (192.168.57.5) 56(84) bytes of data.
64 bytes from 192.168.57.5: icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from 192.168.57.5: icmp_seq=2 ttl=64 time=0.048 ms
패킷의 흐름을 막아야 한다. 해결 방법은 두 가지다.

먼저 iptables를 이용해서 막는 방법이 있다. 172.17.0.0/16이 출발지이고 192.168.57.0/24이 목적지인 패킷을 모두 Drop 하면 된다.

다음 eth1의 IP 포워딩을 disable 하는 방법이 있다. 전체 IP 포워딩을 disable하면 안된다. 그렇게 되면, 컨테이너도 외부와 통신 할 수 없게 된다. eth1만 선택해서 disable 해야 한다. 리눅스 운영체제는 NIC 별로 IP 포워딩을 설정할 수 있다. eth1의 포워딩 설정을 확인해 보자.
# sysctl net.ipv4.conf.eth1.forwarding
net.ipv4.conf.eth1.forwarding = 1
포워딩이 enable 돼 있다. 0으로 바꾸자.
# sysctl -w net.ipv4.conf.eth1.forwarding=0
net.ipv4.conf.eth1.forwarding = 0
ping으로 테스트 해보면 막힌 걸 확인 할 수 있을 것이다. 컨테이너는 172.17.0.0/16 서브넷이고, 스토리지 네트워크는 192.168.57.0/24로 서로 다른 서브넷이다. 따라사 IP 포워딩을 해야 하는데, IP 포워딩을 disable했으므로 패킷이 드랍된다.

이 설정을 /etc/sysctl.conf에 저장하자.