서버 간 트래픽 보호를 위한 Iptables 방화벽 설정하기

소개

응용 프로그램 설정의 개별 구성 요소를 다른 노드에 배치하여 부하를 감소시키고 가로로 확장하는 것은 일반적인 방법입니다. 일반적인 예로는 응용 프로그램에서 데이터베이스를 별도의 서버에 구성하는 것이 있습니다. 이러한 설정의 장점은 여러 가지가 있지만, 네트워크를 통한 연결은 새로운 보안 문제를 야기할 수 있습니다.

이 가이드에서는 분할된 설정의 각 서버에 방화벽을 설정하는 방법을 설명합니다. 우리는 의도한 트래픽을 허용하고 다른 트래픽은 거부하는 정책을 구성할 것입니다.

또한 DigitalOcean의 클라우드 방화벽을 구성할 수도 있습니다. 이는 DigitalOcean 인프라에서 서버 자체에 방화벽을 구성할 필요가 없도록 외부 계층으로 작동합니다.

이 가이드에서는 두 개의 Ubuntu 22.04 서버를 사용할 것입니다. 하나는 Nginx로 제공되는 웹 응용 프로그램을 갖고 있으며, 다른 하나는 응용 프로그램의 MySQL 데이터베이스를 호스팅할 것입니다. 이 예시로 사용할 것이지만, 이와 같은 기술을 사용하여 자체 서버 요구 사항에 맞게 적용할 수 있어야 합니다.

필수 조건

시작하려면 두 개의 새로운 Ubuntu 22.04 서버가 필요합니다. 각각에 sudo 권한이 있는 일반 사용자 계정을 추가하십시오. 이를 위해 Ubuntu 22.04 초기 서버 설정 가이드를 따르십시오.

보안할 애플리케이션 설정은 이 가이드에 기반합니다. 이 예제에 따라 진행하려면 애플리케이션 및 데이터베이스 서버를 해당 튜토리얼에 나와 있는대로 설정하십시오. 그렇지 않은 경우 이 문서를 일반 참고 자료로 사용할 수 있습니다.

단계 1 — 방화벽 설정

서버마다 기본적인 방화벽 구성을 구현하여 시작합니다. 구현할 정책은 보안 우선 접근 방식을 취합니다. SSH 트래픽을 제외한 거의 모든 것을 차단하고 특정 애플리케이션을 위해 방화벽에 구멍을 뚫게 될 것입니다.

이 가이드는 iptables 구문을 따릅니다. Ubuntu 22.04에는 nftables 백엔드를 사용하여 iptables가 자동으로 설치되므로 추가 패키지를 설치할 필요가 없어야 합니다.

nano 또는 좋아하는 텍스트 편집기를 사용하여 /etc/iptables/rules.v4 파일을 엽니다:

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

방화벽 템플릿 가이드에서 구성을 붙여넣습니다.

/etc/iptables/rules.v4
*filter
# 모든 외부 트래픽은 허용하고, 기본적으로 들어오는 및 전달되는 패킷은 차단합니다
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

# 사용자 정의 프로토콜 체인
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]

# 허용 가능한 UDP 트래픽

# 허용 가능한 TCP 트래픽
-A TCP -p tcp --dport 22 -j ACCEPT

# 허용 가능한 ICMP 트래픽

# 기본 수락 정책
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT

# 잘못된 패킷은 버립니다
-A INPUT -m conntrack --ctstate INVALID -j DROP

# 프로토콜별 체인으로 트래픽 전달
## 새로운 연결만 허용합니다 (확립된 및 관련된 연결은 이미 처리되었을 것입니다)
## TCP의 경우, 새로운 SYN 패킷만 허용합니다. 이는 새로운 TCP 연결을 확립하는
## 유일한 유효한 방법입니다
-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

# 여기까지 도달한 경우 모든 패킷을 거부합니다
## 프로토콜별로 거부 메시지를 전달하려고 시도합니다
-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

# 변경 사항을 적용합니다
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]
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

파일을 저장하고 닫습니다. 만약 nano를 사용하는 경우, 종료하려면 Ctrl+X를 누르고, 프롬프트가 나타나면 Y를 입력한 다음 Enter 키를 누릅니다.

실제 환경에서 이를 구현하는 경우, 방화벽 규칙을 아직 다시로드하지 마십시오. 여기서 설명한 규칙 세트를 로드하면 응용 프로그램과 데이터베이스 서버 간의 연결이 즉시 끊어집니다. 다시로드하기 전에 운영 요구 사항을 반영하도록 규칙을 조정해야 합니다.

2단계 – 서비스에서 사용 중인 포트 확인

컴포넌트 간의 통신을 허용하려면 사용되는 네트워크 포트를 알아야 합니다. 구성 파일을 조사하여 올바른 네트워크 포트를 찾을 수 있지만, 올바른 포트를 찾는 응용 프로그램에 상관없는 방법은 각 기기에서 연결을 수신 대기하는 서비스를 확인하는 것입니다.

이를 알아내기 위해 netstat 도구를 사용할 수 있습니다. 응용 프로그램이 IPv4로만 통신하는 경우 -4 인수를 추가하지만, IPv6를 사용하는 경우 이를 제거할 수 있습니다. 실행 중인 서비스를 찾기 위해 필요한 기타 인수는 -p, -l, -u, -n, -t이며, -plunt로 제공할 수 있습니다.

이러한 인수는 다음과 같이 분해할 수 있습니다:

  • p: Show the PID and name of the program to which each socket belongs.
  • l: Show only listening sockets.
  • u: Show UDP traffic.
  • n: Show numeric output instead of service names.
  • t: Show TCP traffic.
  1. sudo netstat -4plunt

웹 서버에서 출력은 다음과 같을 수 있습니다:

Output
Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1058/sshd tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 4187/nginx

첫 번째 강조 표시된 열은 해당 줄의 끝 부분에서 강조된 서비스가 수신 대기 중인 IP 주소와 포트를 보여줍니다. 특별한 0.0.0.0 주소는 해당 서비스가 모든 가능한 주소에서 수신 대기 중임을 의미합니다.

데이터베이스 서버에서 출력은 다음과 같을 수 있습니다:

  1. sudo netstat -4plunt
Output
Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1097/sshd tcp 0 0 192.0.2.30:3306 0.0.0.0:* LISTEN 3112/mysqld

이 열들은 정확히 같은 방식으로 읽을 수 있습니다. 이 예에서 192.0.2.30 주소는 데이터베이스 서버의 사설 IP 주소를 나타냅니다. 사전 튜토리얼에서 보안상의 이유로 MySQL을 사설 인터페이스로 제한했습니다.

이 단계에서 찾은 값을 기억하세요. 이는 방화벽 구성을 조정하는 데 필요한 네트워킹 세부 정보입니다.

웹 서버에서 다음 포트에 접근할 수 있도록 해야 합니다:

  • 모든 주소에서 포트 80을 사용합니다.
  • 모든 주소에서 포트 22를 사용합니다 (방화벽 규칙에 이미 포함되어 있음)

데이터베이스 서버는 다음 포트에 접근 가능해야 합니다:

  • IP 주소 192.0.2.30 (또는 해당 인터페이스)에서 포트 3306을 사용합니다.
  • 모든 주소에서 포트 22를 사용합니다 (방화벽 규칙에 이미 포함되어 있음)

3단계 — 웹 서버 방화벽 규칙 조정

필요한 포트 정보를 얻었으므로 웹 서버의 방화벽 규칙을 조정할 차례입니다. 편집기에서 규칙 파일을 sudo 권한으로 엽니다:

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

웹 서버에서 허용하는 트래픽 목록에 포트 80을 추가해야 합니다. 서버는 모든 가능한 주소에서 수신 대기하므로 (웹 서버는 일반적으로 어디서든 접근 가능하다고 가정함) 인터페이스나 대상 주소로 규칙을 제한하지 않습니다.

웹 방문자는 TCP 프로토콜을 사용하여 연결합니다. 프레임워크에서는 이미 TCP 응용프로그램 예외를 위한 사용자 정의 체인인 TCP가 있습니다. SSH 포트 다음에 포트 80을 해당 체인에 추가할 수 있습니다:

/etc/iptables/rules.v4
*filter
. . .

# 허용 가능한 TCP 트래픽
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 80 -j ACCEPT

. . .

당신의 웹 서버는 데이터베이스 서버와의 연결을 시작합니다. 방화벽에서는 외부 트래픽이 제한되지 않으며, 확립된 연결과 관련된 들어오는 트래픽은 허용됩니다. 따라서 이 연결을 허용하기 위해 해당 서버에 추가 포트를 열 필요가 없습니다.

작업을 마치면 파일을 저장하고 닫으세요. 이제 웹 서버에는 모든 합법적인 트래픽을 허용하고 그 외의 모든 것을 차단하는 방화벽 정책이 있습니다.

구문 오류를 확인하기 위해 규칙 파일을 테스트하세요:

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

구문 오류가 표시되지 않으면 방화벽을 다시로드하여 새 규칙 세트를 적용하세요:

  1. sudo service iptables-persistent reload

4단계 – 데이터베이스 서버 방화벽 규칙 조정

데이터베이스 서버에서는 서버의 사설 IP 주소인 포트 3306에 대한 액세스를 허용해야 합니다. 이 경우 해당 주소는 192.0.2.30이었습니다. 이 주소로 대상을 제한하거나 해당 주소가 할당된 인터페이스와 일치시켜 액세스를 제한할 수 있습니다.

해당 주소와 관련된 네트워크 인터페이스를 찾으려면 ip -4 addr show scope global를 실행하세요:

  1. ip -4 addr show scope global
Output
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 203.0.113.5/24 brd 104.236.113.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 192.0.2.30/24 brd 192.0.2.255 scope global eth1 valid_lft forever preferred_lft forever

형광색으로 표시된 부분은 해당 주소와 eth1 인터페이스가 연결되어 있음을 나타냅니다.

다음으로 데이터베이스 서버의 방화벽 규칙을 조정합니다. 데이터베이스 서버에서 sudo 권한으로 규칙 파일을 엽니다.

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

다시 한 번, 웹 서버와 데이터베이스 서버 간의 연결을 예외로 처리하기 위해 TCP 체인에 규칙을 추가할 것입니다.

실제 주소를 기반으로 액세스를 제한하려면 다음과 같이 규칙을 추가하면 됩니다:

/etc/iptables/rules.v4
*filter
. . .

# 허용 가능한 TCP 트래픽
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -d 192.0.2.30 -j ACCEPT

. . .

대신 주소를 포함하는 인터페이스를 기반으로 예외를 허용하고 싶다면 다음과 유사한 규칙을 추가할 수 있습니다:

/etc/iptables/rules.v4
*filter
. . .

# 허용 가능한 TCP 트래픽
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -i eth1 -j ACCEPT

. . .

작업을 완료하면 파일을 저장하고 닫으세요.

다음 명령을 사용하여 구문 오류를 확인하세요:

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

준비가 되었다면 방화벽 규칙을 다시 로드하세요:

  1. sudo service iptables-persistent reload

이제 두 서버 모두 필요한 데이터 흐름을 제한하지 않으면서 보호될 것입니다.

결론

애플리케이션을 설치할 때 적절한 방화벽을 구현하는 것은 항상 배포 계획의 일부여야 합니다. 위에서 Nginx와 MySQL을 실행하는 두 서버를 사용하여 이 구성을 설명했지만, 위에 설명된 기술은 특정 기술 선택과 관계없이 적용할 수 있습니다.

방화벽과 iptables에 대해 더 알아보려면 다음 가이드를 참조하세요:

Source:
https://www.digitalocean.com/community/tutorials/how-to-set-up-an-iptables-firewall-to-protect-traffic-between-your-servers