웹사이트 검색

Iptables를 사용하여 Linux 게이트웨이를 통해 포트를 전달하는 방법


소개

NAT 또는 네트워크 주소 변환은 패킷을 대체 주소로 리디렉션하기 위해 패킷을 맹글링하는 일반적인 용어입니다. 일반적으로 이것은 트래픽이 네트워크 경계를 초월하도록 허용하는 데 사용됩니다. NAT를 구현하는 호스트는 일반적으로 두 개 이상의 네트워크에 액세스할 수 있으며 네트워크 간에 트래픽을 라우팅하도록 구성됩니다.

포트 전달은 특정 포트에 대한 요청을 다른 호스트, 네트워크 또는 포트로 전달하는 프로세스입니다. 이 프로세스는 비행 중인 패킷의 목적지를 수정하므로 일종의 NAT 작업으로 간주됩니다.

이 자습서에서는 iptables를 사용하여 NAT 기술을 사용하여 방화벽 뒤의 호스트에 포트를 전달하는 방법을 보여줍니다. 이는 사설 네트워크를 구성했지만 지정된 게이트웨이 시스템을 통해 내부의 특정 트래픽을 허용하려는 경우에 유용합니다.

전제 조건

이 가이드를 따르려면 다음이 필요합니다.

  • 비공개 네트워킹이 활성화된 동일한 데이터 센터에 두 개의 Ubuntu 20.04 서버 설정. 이러한 각 컴퓨터에서 sudo 권한이 있는 루트가 아닌 사용자 계정을 설정해야 합니다. Ubuntu 20.04 초기 서버 설정 가이드에서 이 작업을 수행하는 방법을 배울 수 있습니다. 이 튜토리얼을 진행하는 동안 방화벽을 설정하고 구성할 것이므로 이 가이드의 4단계는 건너뛰십시오.
  • 서버 중 하나에서 방화벽 서버로 작동할 수 있도록 iptables로 방화벽 템플릿을 설정합니다. Ubuntu 20.04에서 Iptables로 기본 방화벽을 구현하는 방법에 대한 가이드를 따라 이를 수행할 수 있습니다. 완료되면 방화벽 서버에서 다음을 사용할 준비가 되어 있어야 합니다.\n
    • iptables-persistent 설치
    • 기본 규칙 집합을 /etc/iptables/rules.v4
    • 에 저장했습니다.\n
    • 규칙 파일을 편집하거나 iptables 명령을 사용하여 규칙을 추가하거나 조정하는 방법 이해

    방화벽 템플릿을 설정한 서버는 개인 네트워크의 방화벽 및 라우터 역할을 합니다. 시연을 위해 두 번째 호스트는 전용 인터페이스를 통해서만 액세스할 수 있는 웹 서버로 구성됩니다. 공개 인터페이스에서 수신한 요청을 비공개 인터페이스에서 도달할 웹 서버로 전달하도록 방화벽 시스템을 구성할 것입니다.

    호스트 세부정보

    시작하기 전에 두 서버에서 사용 중인 인터페이스와 주소를 알아야 합니다.

    네트워크 세부 정보 찾기

    자신의 시스템에 대한 세부 정보를 얻으려면 네트워크 인터페이스를 찾는 것부터 시작하십시오. 다음을 실행하여 시스템의 인터페이스 및 인터페이스와 연결된 주소를 찾을 수 있습니다.

    1. ip -4 addr show scope global
    Sample Output
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 203.0.113.1/18 brd 45.55.191.255 scope global eth0 valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 10.0.0.1/16 brd 10.132.255.255 scope global eth1 valid_lft forever preferred_lft forever

    강조 표시된 출력에는 두 개의 인터페이스(eth0eth1)와 각각에 할당된 주소(203.0.113.110.0.0.1 각각). 이러한 인터페이스 중 어느 것이 공용 인터페이스인지 확인하려면 다음 명령을 실행하십시오.

    1. ip route show | grep default
    Output
    default via 111.111.111.111 dev eth0

    이 출력의 인터페이스 정보(이 예에서는 eth0)가 기본 게이트웨이에 연결된 인터페이스가 됩니다. 이것은 거의 확실하게 공용 인터페이스입니다.

    각 시스템에서 이러한 값을 찾아 이 가이드의 나머지 부분을 따라하는 데 사용하십시오.

    이 가이드에 사용된 샘플 데이터

    더 명확하게 하기 위해 이 자습서 전체에서 다음과 같은 빈 주소 및 인터페이스 할당을 사용합니다. 다음에 나열된 값을 자신의 값으로 대체하십시오.

    웹 서버 네트워크 세부 정보:

    • 공개 IP 주소: 203.0.113.1
    • 비공개 IP 주소: 10.0.0.1
    • 공개 인터페이스: eth0
    • 비공개 인터페이스: eth1

    방화벽 네트워크 세부 정보:

    • 공개 IP 주소: 203.0.113.2
    • 비공개 IP 주소: 10.0.0.2
    • 공개 인터페이스: eth0
    • 비공개 인터페이스: eth1

    웹 서버 설정

    sudo 사용자로 로그인하여 웹 서버 호스트에 연결을 시작하십시오.

    Nginx 설치

    첫 번째 단계는 웹 서버 호스트에 Nginx를 설치하고 비공개 인터페이스만 수신하도록 잠그는 것입니다. 이렇게 하면 포트 포워딩을 올바르게 설정한 경우에만 웹 서버를 사용할 수 있습니다.

    로컬 패키지 캐시를 업데이트하여 시작합니다.

    1. sudo apt update

    다음으로 apt를 사용하여 소프트웨어를 다운로드하고 설치합니다.

    1. sudo apt install nginx

    Nginx를 사설 네트워크로 제한

    Nginx가 설치된 후 기본 서버 블록 구성 파일을 열어 개인 인터페이스만 수신하도록 합니다. 원하는 텍스트 편집기를 사용하여 파일을 엽니다. 여기서는 nano를 사용합니다.

    1. sudo nano /etc/nginx/sites-enabled/default

    내부에서 listen 지시문을 찾습니다. 구성 상단을 향해 연속으로 두 번 나열되어야 합니다.

    server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;
    
        . . .
    }
    

    첫 번째 listen 지시문에서 80 앞에 웹 서버의 사설 IP 주소와 콜론을 추가하여 Nginx가 사설 인터페이스에서만 수신하도록 지시합니다. 이 가이드에서는 IPv4 전달만 시연하므로 IPv6용으로 구성된 두 번째 수신 지시문을 제거할 수 있습니다.

    다음으로 listen 지시문을 다음과 같이 수정합니다.

    server {
        listen 10.0.0.1:80 default_server;
    
        . . .
    }
    

    완료되면 파일을 저장하고 닫습니다. nano를 사용한 경우 CTRL + X를 누른 다음 Y, ENTER를 눌러 이 작업을 수행할 수 있습니다.

    이제 구문 오류가 있는지 파일을 테스트합니다.

    1. sudo nginx -t
    Output
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful

    출력에 오류가 없으면 Nginx를 다시 시작하여 새 구성을 활성화합니다.

    1. sudo systemctl restart nginx

    네트워크 제한 확인

    이 시점에서 웹 서버에 대한 액세스 수준을 확인하는 것이 유용합니다.

    방화벽 서버에서 다음 명령을 사용하여 개인 인터페이스에서 웹 서버에 액세스하십시오.

    1. curl --connect-timeout 5 10.0.0.1

    성공하면 다음 메시지가 출력됩니다.

    Output
    <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> . . .

    공용 인터페이스를 사용하려고 하면 연결할 수 없다는 메시지가 표시됩니다.

    1. curl --connect-timeout 5 203.0.113.1
    Output
    curl: (7) Failed to connect to 203.0.113.1 port 80: Connection refused

    이러한 결과가 예상됩니다.

    포트 80을 전달하도록 방화벽 구성

    이제 방화벽 시스템에서 포트 포워딩을 구현하는 작업을 할 것입니다.

    커널에서 전달 활성화

    가장 먼저 해야 할 일은 커널 수준에서 트래픽 전달을 활성화하는 것입니다. 기본적으로 대부분의 시스템은 전달이 꺼져 있습니다.

    이 세션에 대해서만 포트 전달을 켜려면 다음을 실행하십시오.

    1. echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
    Output
    1

    포트 전달을 영구적으로 켜려면 /etc/sysctl.conf 파일을 편집해야 합니다. sudo 권한으로 파일을 열면 됩니다.

    1. sudo nano /etc/sysctl.conf

    파일 내에서 다음과 같은 행을 찾아 주석 처리를 제거하십시오.

    net.ipv4.ip_forward=1
    

    완료되면 파일을 저장하고 닫습니다.

    그런 다음 이 파일의 설정을 적용합니다. 먼저 다음 명령을 실행합니다.

    1. sudo sysctl -p
    Output
    net.ipv4.ip_forward = 1

    그런 다음 동일한 명령을 실행하되 -p 플래그를 --system으로 바꿉니다.

    1. sudo sysctl --system
    Output
    . . . * Applying /usr/lib/sysctl.d/50-pid-max.conf ... kernel.pid_max = 4194304 * Applying /etc/sysctl.d/99-cloudimg-ipv6.conf ... net.ipv6.conf.all.use_tempaddr = 0 net.ipv6.conf.default.use_tempaddr = 0 * Applying /etc/sysctl.d/99-sysctl.conf ... net.ipv4.ip_forward = 1 * Applying /usr/lib/sysctl.d/protect-links.conf ... fs.protected_fifos = 1 fs.protected_hardlinks = 1 fs.protected_regular = 2 fs.protected_symlinks = 1 * Applying /etc/sysctl.conf ... net.ipv4.ip_forward = 1

    기본 방화벽에 전달 규칙 추가

    다음으로 포트 80에서 공용 인터페이스(eth0)로 흐르는 트래픽이 개인 인터페이스(eth1)로 전달되도록 방화벽을 구성합니다. >).

    전제 조건 자습서에서 구성한 방화벽에는 기본적으로 FORWARD 체인이 DROP 트래픽으로 설정되어 있습니다. 웹 서버로 연결을 전달하도록 허용하는 규칙을 추가해야 합니다. 보안을 위해 전달하려는 연결만 허용되도록 상당히 단단히 잠급니다.

    FORWARD 체인에서는 공용 인터페이스에서 시작하여 개인 인터페이스로 이동하는 80 포트로 향하는 새로운 연결을 수락합니다. 새 연결은 conntrack 확장으로 식별되며 특히 다음과 같이 TCP SYN 패킷으로 표시됩니다.

    1. sudo iptables -A FORWARD -i eth0 -o eth1 -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT

    이렇게 하면 연결을 설정하기 위한 첫 번째 패킷이 방화벽을 통과하게 됩니다. 또한 해당 연결에서 발생하는 양방향 트래픽을 모두 허용해야 합니다. 공개 인터페이스와 비공개 인터페이스 간에 ESTABLISHEDRELATED 트래픽을 허용하려면 다음 명령을 실행합니다. 먼저 공용 인터페이스의 경우:

    1. sudo iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

    그런 다음 개인 인터페이스의 경우:

    1. sudo iptables -A FORWARD -i eth1 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

    FORWARD 체인의 정책이 DROP로 설정되어 있는지 다시 확인하십시오.

    1. sudo iptables -P FORWARD DROP

    이 시점에서 공개 인터페이스와 비공개 인터페이스 간의 특정 트래픽이 방화벽을 통과하도록 허용했습니다. 그러나 iptables에게 트래픽을 변환하고 전달하는 방법을 실제로 알려주는 규칙을 구성하지 않았습니다.

    NAT 규칙을 직접 패킷에 올바르게 추가

    다음으로 트래픽을 라우팅하는 방법을 iptables에 알려주는 규칙을 추가합니다. 클라이언트가 웹 서버와 통신할 수 있도록 iptables가 패킷을 올바르게 변경하려면 두 가지 개별 작업을 수행해야 합니다.

    DNAT라고 하는 첫 번째 작업은 nat 테이블의 PREROUTING 체인에서 수행됩니다. DNAT는 패킷이 네트워크 간에 전달될 때 올바르게 라우팅할 수 있도록 패킷의 대상 주소를 변경하는 작업입니다. 공용 네트워크의 클라이언트는 방화벽 서버에 연결되며 개인 네트워크 토폴로지에 대해 알지 못합니다. 따라서 각 패킷의 대상 주소를 변경하여 사설 네트워크로 보낼 때 웹 서버에 올바르게 도달하는 방법을 알 수 있도록 해야 합니다.

    포트 전달만 구성하고 방화벽에 도달하는 모든 패킷에 대해 NAT를 수행하지 않으므로 규칙에서 포트 80을 일치시키는 것이 좋습니다. 포트 80을 대상으로 하는 패킷을 웹 서버의 사설 IP 주소(다음 예에서 10.0.0.1)와 일치시킵니다.

    1. sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1

    이 프로세스는 그림의 절반을 처리합니다. 패킷은 웹 서버로 올바르게 라우팅되어야 합니다. 그러나 현재 패킷은 여전히 클라이언트의 원래 주소를 소스 주소로 가지고 있습니다. 서버는 해당 주소로 직접 회신을 보내려고 시도하므로 합법적인 TCP 연결을 설정할 수 없습니다.

    DigitalOcean에서 다른 소스 주소를 가진 Droplet을 떠나는 패킷은 실제로 하이퍼바이저에 의해 삭제되므로 이 단계의 패킷은 웹 서버에 도달하지도 않습니다(잠시 SNAT를 구현하여 수정됨). 이것은 요청에서 원본 주소를 위조하여 대량의 데이터를 피해자의 컴퓨터로 전송하도록 요청하는 공격을 방지하기 위해 마련된 스푸핑 방지 조치입니다. 자세히 알아보려면 커뮤니티에서 이 답변을 읽어보세요.

    적절한 라우팅을 구성하려면 패킷이 웹 서버로 가는 도중에 방화벽을 떠날 때 패킷의 소스 주소도 수정해야 합니다. 소스 주소를 방화벽 서버의 사설 IP 주소(다음 예에서 10.0.0.2)로 수정해야 합니다. 그러면 회신이 방화벽으로 다시 전송되고 예상대로 클라이언트로 다시 전달할 수 있습니다.

    이 기능을 활성화하려면 패킷이 네트워크에서 전송되기 직전에 평가되는 nat 테이블의 POSTROUTING 체인에 규칙을 추가합니다. IP 주소와 포트로 웹 서버로 향하는 패킷을 일치시킵니다.

    1. sudo iptables -t nat -A POSTROUTING -o eth1 -p tcp --dport 80 -d 10.0.0.1 -j SNAT --to-source 10.0.0.2

    이 규칙이 적용되면 웹 브라우저에서 방화벽 시스템의 공용 주소를 가리켜 웹 서버에 액세스할 수 있어야 합니다.

    1. curl 203.0.113.2
    Output
    <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> . . .

    이제 포트 포워딩 설정이 완료되었습니다.

    영구 규칙 세트 조정

    이제 포트 포워딩을 설정했으므로 이를 영구 규칙 세트에 저장할 수 있습니다.

    현재 규칙 세트에 있는 주석이 손실되는 것에 대해 신경 쓰지 않는다면 netfilter-persistent 명령을 사용하여 iptables 서비스를 사용하고 규칙을 저장하십시오.

    1. sudo service netfilter-persistent save
    Output
    * Saving netfilter rules... run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables save run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables save [ OK ]

    주석을 파일에 보관하려면 파일을 열고 수동으로 편집하십시오.

    1. sudo nano /etc/iptables/rules.v4

    추가된 FORWARD 체인 규칙에 대해 filter 테이블에서 구성을 조정해야 합니다. 또한 PREROUTINGPOSTROUTING 규칙을 추가할 수 있도록 nat 테이블을 구성하는 섹션을 조정해야 합니다. 내용은 다음과 같습니다.

    *filter
    # Allow all outgoing, but drop incoming and forwarding packets by default
    :INPUT DROP [0:0]
    :FORWARD DROP [0:0]
    :OUTPUT ACCEPT [0:0]
    
    # Custom per-protocol chains
    :UDP - [0:0]
    :TCP - [0:0]
    :ICMP - [0:0]
    
    # Acceptable UDP traffic
    
    # Acceptable TCP traffic
    -A TCP -p tcp --dport 22 -j ACCEPT
    
    # Acceptable ICMP traffic
    
    # Boilerplate acceptance policy
    -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    -A INPUT -i lo -j ACCEPT
    
    # Drop invalid packets
    -A INPUT -m conntrack --ctstate INVALID -j DROP
    
    # Pass traffic to protocol-specific chains
    ## Only allow new connections (established and related should already be handled)
    ## For TCP, additionally only allow new SYN packets since that is the only valid
    ## method for establishing a new TCP connection
    -A INPUT -p udp -m conntrack --ctstate NEW -j UDP
    -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
    -A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP
    
    # Reject anything that's fallen through to this point
    ## Try to be protocol-specific w/ rejection message
    -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
    -A INPUT -p tcp -j REJECT --reject-with tcp-reset
    -A INPUT -j REJECT --reject-with icmp-proto-unreachable
    
    # Rules to forward port 80 to our web server
    
    # Web server network details:
    
    # * Public IP Address: 203.0.113.1
    # * Private IP Address: 10.0.0.1
    # * Public Interface: eth0
    # * Private Interface: eth1
    # 
    # Firewall network details:
    # 
    # * Public IP Address: 203.0.113.2
    # * Private IP Address: 10.0.0.2
    # * Public Interface: eth0
    # * Private Interface: eth1
    -A FORWARD -i eth0 -o eth1 -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT
    -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    -A FORWARD -i eth1 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    # End of Forward filtering rules
    
    # Commit the changes
    
    COMMIT
    
    *raw
    :PREROUTING ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    COMMIT
    
    *nat
    :PREROUTING ACCEPT [0:0]
    :INPUT ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    :POSTROUTING ACCEPT [0:0]
    
    # Rules to translate requests for port 80 of the public interface
    # so that we can forward correctly to the web server using the
    # private interface.
    
    # Web server network details:
    
    # * Public IP Address: 203.0.113.1
    # * Private IP Address: 10.0.0.1
    # * Public Interface: eth0
    # * Private Interface: eth1
    # 
    # Firewall network details:
    # 
    # * Public IP Address: 203.0.113.2
    # * Private IP Address: 10.0.0.2
    # * Public Interface: eth0
    # * Private Interface: eth1
    -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1
    -A POSTROUTING -d 10.0.0.1 -o eth1 -p tcp --dport 80 -j SNAT --to-source 10.0.0.2
    # End of NAT translations for web server traffic
    COMMIT
    
    *security
    :INPUT ACCEPT [0:0]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    COMMIT
    
    *mangle
    :PREROUTING ACCEPT [0:0]
    :INPUT ACCEPT [0:0]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    :POSTROUTING ACCEPT [0:0]
    COMMIT
    

    콘텐츠를 추가하고 자신의 네트워크 환경을 반영하도록 값을 조정한 후 파일을 저장하고 닫습니다.

    다음으로 규칙 파일의 구문을 테스트합니다.

    1. sudo sh -c "iptables-restore -t < /etc/iptables/rules.v4"

    오류가 감지되지 않으면 규칙 세트를 로드합니다.

    1. sudo service netfilter-persistent reload
    Output
    * Loading netfilter rules... run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables start run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables start [ OK ]

    이제 방화벽의 공용 IP 주소를 통해 웹 서버에 계속 액세스할 수 있는지 테스트합니다.

    1. curl 203.0.113.2

    이전과 동일하게 작동해야 합니다.

    결론

    지금쯤이면 iptables를 사용하여 Linux 서버에서 포워딩 포트에 익숙해졌을 것입니다. 이 프로세스에는 커널 수준에서 전달 허용, 방화벽 시스템의 두 인터페이스 간에 특정 포트의 트래픽 전달을 허용하도록 액세스 설정, 패킷이 올바르게 라우팅될 수 있도록 NAT 규칙 구성이 포함됩니다. 이것은 다루기 힘든 프로세스처럼 보일 수 있지만 netfilter 패킷 필터링 프레임워크와 iptables 방화벽의 유연성도 보여줍니다. 이것은 서비스 트래픽이 게이트웨이 방화벽 시스템을 통해 자유롭게 흐르도록 허용하면서 사설 네트워크의 토폴로지를 위장하는 데 사용할 수 있습니다.