Linux 네트워크 이름 공간 및 가상 스위치를 사용하여 서버를 격리하는 컨테이너 네트워크 이해


Linux 이름 공간이란 무엇입니까?
Linux 이름 공간은 운영체제의 자원에 대한 추상적인 것입니다.우리는 명칭 공간을 하나의 상자로 볼 수 있다.이 상자에는 시스템 자원이 있습니다. 이것은 상자의 (명칭 공간) 유형에 완전히 달려 있습니다.현재 7가지 명칭 공간 Cgroup, IPC, 네트워크, Mount, PID, User, UTS가 있다.

네트워크 이름 공간이란 무엇입니까?
네트워크 이름 공간, man 7 네트워크 이름 공간에 따라:
네트워크 이름 공간은 네트워크와 관련된 시스템 자원을 분리했다. 네트워크 장치, IPv4와 IPv6 프로토콜 창고, IP 루트 테이블, 방화벽 규칙,/proc/net 디렉터리,/sys/class/net 디렉터리,/proc/sys/net 아래의 각종 파일, 포트 번호(플러그인) 등이다.

가상 인터페이스 및 브리지:
가상 인터페이스는 우리에게 물리적 네트워크 인터페이스의 가상화 표시를 제공했다.다리는 우리에게 가상의 스위치를 주었다.

저희가 뭘 보도해야 돼요?
  • 우리는 두 개의 네트워크 이름 공간(예를 들어 두 개의 독립된 서버), 두 개의 veth 쌍(예를 들어 두 개의 물리적 이더넷 케이블)과 하나의 브리지(예를 들어 이름 공간 사이의 루트 데이터에 사용)를 만들 것이다.
  • 그리고 우리는 이 두 명칭 공간이 서로 통신할 수 있기 때문에 브리지를 배치할 것이다.
  • 그리고 브리지를 호스트 및 인터넷에 연결
  • 마지막으로 우리는 명칭 공간의 전송 데이터(외부)를 공동으로 설정할 것이다.
  • 우리 시작합시다...


    0단계:


    호스트/루트 네임스페이스의 기본 네트워크 상태를 확인합니다.현재 상태를 추적해서 더 잘 이해할 수 있도록만 하면 된다.[나는 AWS에서ec2 실례(ubuntu)를 시작해서 이 실제 조작을 모의했다. VM은 심지어 일반적인 linux 기계도 가능하다.]
    # list all the interfaces
    sudo ip link
    
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
        link/ether 0a:1b:b1:bc:70:d0 brd ff:ff:ff:ff:ff:ff
    
    # find the routing table
    sudo route -n
    
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    0.0.0.0         172.31.0.1      0.0.0.0         UG    100    0        0 eth0
    172.31.0.0      0.0.0.0         255.255.240.0   U     0      0        0 eth0
    172.31.0.1      0.0.0.0         255.255.255.255 UH    100    0        0 eth0
    

    단계 1.1:


    두 개의 네트워크 이름 공간 만들기
    # add two two network namespaces using "ip netns" command
    sudo ip netns add ns1
    sudo ip netns add ns2
    
    # list the created network namespaces
    sudo ip netns list
    
    ns1
    ns2
    
    # By convention, network namespace handles created by
    # iproute2 live under `/var/run/netns`
    sudo ls /var/run/netns/
    
    ns1 ns2
    

    단계 1.2:


    기본적으로 이미 만들어진 네트워크의 네트워크 인터페이스가 닫히고 순환 인터페이스도 닫힙니다.지어내다.
    sudo ip netns exec ns1 ip link set lo up
    sudo ip netns exec ns1 ip link
    
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    
    sudo ip netns exec ns2 ip link set lo up
    sudo ip netns exec ns2 ip link
    
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    

    2.1단계:


    호스트에 브리지 네트워크 생성
    sudo ip link add br0 type bridge
    # up the created bridge and check whether it is created and in UP/UNKNOWN state
    sudo ip link set br0 up
    sudo ip link
    
    3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
        link/ether 12:38:75:40:c0:17 brd ff:ff:ff:ff:ff:ff
    

    2.2단계:


    브리지 네트워크에 IP 구성
    sudo ip addr add 192.168.1.1/24 dev br0
    # check whether the ip is configured and also ping to ensure
    sudo ip addr
    
    3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
        link/ether 12:38:75:40:c0:17 brd ff:ff:ff:ff:ff:ff
        inet 192.168.1.1/24 scope global br0
           valid_lft forever preferred_lft forever
        inet6 fe80::1038:75ff:fe40:c017/64 scope link 
           valid_lft forever preferred_lft forever
    
    ping -c 2 192.168.1.1
    
    --- 192.168.1.1 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 1026ms
    rtt min/avg/max/mdev = 0.020/0.029/0.039/0.009 ms
    

    단계 3.1:


    두 네트워크를 위해 두 개의veth 인터페이스를 만들고 브리지와 네트워크에 연결합니다
    # For ns1
    
    # creating a veth pair which have two ends identical veth0 and ceth0
    sudo ip link add veth0 type veth peer name ceth0
    # connect veth0 end to the bridge br0
    sudo ip link set veth0 master br0
    # up the veth0 
    sudo ip link set veth0 up 
    # connect ceth0 end to the netns ns1
    sudo ip link set ceth0 netns ns1
    # up the ceth0 using 'exec' to run command inside netns
    sudo ip netns exec ns1 ip link set ceth0 up
    # check the link status 
    sudo ip link
    
    3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
        link/ether 9a:af:0d:89:8b:81 brd ff:ff:ff:ff:ff:ff
    5: veth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UP mode DEFAULT group default qlen 1000
        link/ether 9a:af:0d:89:8b:81 brd ff:ff:ff:ff:ff:ff link-netns ns1
    
    # check the link status inside ns1
    sudo ip netns exec ns1 ip link
    
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    4: ceth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
        link/ether 3e:66:e5:b6:07:9a brd ff:ff:ff:ff:ff:ff link-netnsid 0
    
    
    # For ns2; do the same as ns1
    
    sudo ip link add veth1 type veth peer name ceth1
    sudo ip link set veth1 master br0
    sudo ip link set veth1 up
    sudo ip link set ceth1 netns ns2
    sudo ip netns exec ns2 ip link set ceth1 up
    
    sudo ip link 
    
    3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
        link/ether 1a:f5:b2:8e:ca:a5 brd ff:ff:ff:ff:ff:ff
    5: veth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UP mode DEFAULT group default qlen 1000
        link/ether 9a:af:0d:89:8b:81 brd ff:ff:ff:ff:ff:ff link-netns ns1
    7: veth1@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UP mode DEFAULT group default qlen 1000
        link/ether 1a:f5:b2:8e:ca:a5 brd ff:ff:ff:ff:ff:ff link-netns ns2
    
    sudo ip netns exec ns2 ip link
    
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    6: ceth1@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
        link/ether 3e:1e:48:de:47:07 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    

    단계 3.2:


    현재, 우리는 넷nsveth 인터페이스에 IP 주소를 추가하고, 루트표를 업데이트하여 브리지 네트워크와의 통신을 구축할 것이며, 두 넷ns 간에 브리지를 통해 통신을 할 수 있도록 할 것이다.
    # For ns1
    sudo ip netns exec ns1 ip addr add 192.168.1.10/24 dev ceth0
    sudo ip netns exec ns1 ping -c 2 192.168.1.10
    sudo ip netns exec ns1 ip route
    
    192.168.1.0/24 dev ceth0 proto kernel scope link src 192.168.1.10
    # check if you can reach bridge interface
    sudo ip netns exec ns1 ping -c 2 192.168.1.1
    
    --- 192.168.1.1 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 1020ms
    rtt min/avg/max/mdev = 0.046/0.050/0.054/0.004 ms
    
    # For ns2
    sudo ip netns exec ns2 ip addr add 192.168.1.11/24 dev ceth1
    sudo ip netns exec ns2 ping -c 2 192.168.1.11
    sudo ip netns exec ns2 ip route 
    
    192.168.1.0/24 dev ceth1 proto kernel scope link src 192.168.1.11
    # check if you can reach bridge interface
    sudo ip netns exec ns2 ping -c 2 192.168.1.1
    
    -------- 192.168.1.1 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 1020ms
    rtt min/avg/max/mdev = 0.046/0.050/0.054/0.004 ms
    

    4단계:


    두 네트워크 사이의 연결을 검증하면 작동할 수 있을 거야!
    # For ns1: 
    # we can log in to netns environment using below; 
    # it will be totally isolated from any other network
    sudo nsenter --net=/var/run/netns/ns1
    # ping to the ns2 netns to verify the connectivity
    ping -c 2 192.168.1.11
    
    -------- 192.168.1.11 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 1019ms
    rtt min/avg/max/mdev = 0.033/0.042/0.051/0.009 ms
    # exit from the ns1
    exit
    # For ns2
    sudo nsenter --net=/var/run/netns/ns2
    # ping to the ns1 netns to verify the connectivity
    ping -c 2 192.168.1.10
    
    -------- 192.168.1.10 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 1022ms
    rtt min/avg/max/mdev = 0.041/0.044/0.048/0.003 ms
    # exit from the ns2
    exit
    

    두 네트워크 이름 공간 사이의 브리지 연결이 완료되었습니다.



    the diagrom is taken from ops.tips blog


    5.1단계:


    지금은 인터넷에 접속할 때이다.보시다시피 ns1의 루트 테이블은 기본 게이트웨이가 없기 때문에 192.168.1.0/24 범위 밖의 다른 기기에서 접근할 수 없습니다.
    sudo ip netns exec ns1 ping -c 2 8.8.8.8
    ping: connect: Network is unreachable
    # check the route inside ns1
    sudo ip netns exec ns1 route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 ceth0
    # As we can see, no route is defined to carry other traffic than 192.168.1.0/24
    # we can fix this by using adding default route 
    sudo ip netns exec ns1 ip route add default via 192.168.1.1
    sudo ip netns exec ns1 route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 ceth0
    192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 ceth0
    
    # Do the same for ns2
    sudo ip netns exec ns2 ip route add default via 192.168.1.1
    sudo ip netns exec ns2 route -n
    
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 ceth1
    192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 ceth1
    
    # now first ping the host machine eth0
    ip addr | grep eth0
    
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
        inet 172.31.13.55/20 brd 172.31.15.255 scope global dynamic eth0
    # ping from ns1 to host ip
    sudo ip netns exec ns1 ping 172.31.13.55
    64 bytes from 172.31.13.55: icmp_seq=1 ttl=64 time=0.037 ms
    64 bytes from 172.31.13.55: icmp_seq=2 ttl=64 time=0.036 ms
    # we get the response from host machine eth0
    

    5.2단계:


    이제 ns1이 인터넷과 통신할 수 있는지 살펴보자. 우리는 tcpdump를 사용하여 데이터 패키지가 어떻게 전송되는지 분석할 수 있다.다른 터미널을 열고 tcpdump로 데이터를 포획합니다.
    # terminal-1
    # now trying to ping 8.8.8.8 again
    sudo ip netns exec ns1 ping 8.8.8.8
    
    # still unreachable
    # terminal 2
    # open tcpdump in eth0 to see the packet
    sudo tcpdump -i eth0 icmp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
    
    # no packet captured, let's capture traffic for br0
    sudo tcpdump -i br0 icmp
    
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on br0, link-type EN10MB (Ethernet), capture size 262144 bytes
    02:17:30.807072 IP ip-192-168-1-10.ap-south-1.compute.internal > dns.google: ICMP echo request, id 17506, seq 1, length 64
    02:17:31.829317 IP ip-192-168-1-10.ap-south-1.compute.internal > dns.google: ICMP echo request, id 17506, seq 2, length 64
    
    # we can see the traffic at br0 but we don't get response from eth0.
    # it's because of IP forwarding issue
    sudo cat /proc/sys/net/ipv4/ip_forward
    0
    
    # enabling ip forwarding by change value 0 to 1
    sudo sysctl -w net.ipv4.ip_forward=1
    sudo cat /proc/sys/net/ipv4/ip_forward
    1
    
    # terminal-2
    sudo tcpdump -i eth0 icmp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
    02:30:12.603895 IP ip-192-168-1-10.ap-south-1.compute.internal > dns.google: ICMP echo request, id 18103, seq 1, length 64
    02:30:13.621367 IP ip-192-168-1-10.ap-south-1.compute.internal > dns.google: ICMP echo request, id 18103, seq 2, length 64
    # as we can see now we are getting response eth0
    # but ping 8.8.8.8 still not working
    # Although the network is now reachable, there’s no way that 
    # we can have responses back - cause packets from external networks 
    # can’t be sent directly to our `192.168.1.0/24` network.
    

    5.3단계:


    이러한 상황을 피하기 위해서 우리는 iptables표의 POSTROUTING체인에 nat규칙을 놓아서 NAT(네트워크 주소 변환)를 사용할 수 있다.
    sudo iptables \
            -t nat \
            -A POSTROUTING \
            -s 192.168.1.0/24 ! -o br0 \
            -j MASQUERADE
    # -t specifies the table to which the commands
    # should be directed to. By default it's `filter`.
    # -A specifies that we're appending a rule to the
    # chain then we tell the name after it;
    # -s specifies a source address (with a mask in this case).
    # -j specifies the target to jump to (what action to take).
    
    # now we're getting response from google dns
    sudo ip netns exec ns1 ping -c 2 8.8.8.8
    
    -------- 8.8.8.8 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 1002ms
    rtt min/avg/max/mdev = 1.625/1.662/1.700/0.037 ms
    

    단계: 6


    이제 이름 공간에서 서비스를 열고 외부로부터 응답을 받도록 하겠습니다.
    sudo nsenter --net=/var/run/netns/netns1
    python3 -m http.server --bind 192.168.1.10 3000
    
    AWS에서 온ec2 실례가 있기 때문에 공공 IP가 추가되어 있습니다.특정 포트를 통해 외부에서 IP 에 액세스하려고 합니다.
    telnet 65.2.35.192 5000
    Trying 65.2.35.192...
    telnet: Unable to connect to remote host: Connection refused
    
    우리가 본 바와 같이 우리는 목적지에 도달할 수 없다.호스트에 전송된 데이터를 어디에 두는지 알려주지 않았기 때문이다.우리는 다시 NAT를 해야 한다. 이번에 우리는 목적지를 정의할 것이다.
    
    sudo iptables \
            -t nat \
            -A PREROUTING \
            -d 172.31.13.55 \
            -p tcp -m tcp --dport 5000 \
            -j DNAT --to-destination 192.168.1.10:5000
    # -p specifies a port type and --dport specifies the destination port
    # -j specifies the target DNAT to jump to destination IP with port.
    
    
    # from my laptop
    # now I can connect the destination with port.
    # We successfully recieved traffic from internet inside container network
    
    telnet 65.2.35.192 5000
    Trying 65.2.35.192...
    Connected to 65.2.35.192.
    Escape character is '^]'.
    

    이제 시작이다.우리가 이룩한 성과:

  • 호스트가 네트워크 이름 공간
  • 에 있는 모든 애플리케이션에 트래픽을 보낼 수 있음
  • 네트워크 이름 공간 내의 응용 프로그램은 호스트 응용 프로그램 및 기타 네트워크 이름 공간 응용 프로그램
  • 과 통신할 수 있다
  • 네트워크 이름 공간 내의 응용 프로그램은 인터넷에 연결할 수 있다
  • 네트워크 이름 공간 내의 응용 프로그램은 외부 인터넷으로부터의 요청을 감청할 수 있다
  • 마지막으로 우리는 Docker나 다른 용기 도구가 어떻게 엔진 뚜껑 아래에 네트워크를 구축하는지 이해했다."Docker run-p 5000:5000 프런트엔드 응용 프로그램"
  • 이라는 명령을 내릴 때 전체 프로세스를 자동화합니다.
    Only commands will be found here
    Find the github repo here

    리소스

  • https://man7.org/linux/man-pages/man7/network_namespaces.7.html
  • https://ops.tips/blog/using-network-namespaces-and-bridge-to-isolate-servers/
  • https://github.com/dipanjal/DevOps/tree/main/NetNS_Ingress_Egress_Traffic
  • https://man7.org/linux/man-pages/man8/iptables.8.html
  • 좋은 웹페이지 즐겨찾기