Iptables防火墙配置详解:保护服务器间通信安全
引言
将应用程序的离散组件部署到不同节点上是降低负载并实现横向扩展的常见做法。一个典型的例子是将数据库配置在与应用程序分离的服务器上。虽然这种架构有许多优势,但通过网络连接也带来了新的安全挑战。
在本指南中,我们将演示如何在分布式环境中为每台服务器设置防火墙。我们将配置特定的策略,允许组件之间进行预期流量传输,同时阻止其他所有未经授权的访问。
您也可以配置Silicon Cloud的云防火墙,它在Silicon Cloud基础设施上的服务器外部运行,作为额外的保护层。通过这种方法,您无需在服务器本身配置防火墙。
本指南的演示将使用两台Ubuntu 22.04服务器。一台将运行Nginx提供Web应用程序,另一台将托管该应用程序的MySQL数据库。尽管我们使用此设置作为示例,但所涉及的技术可以适应您自己的服务器需求。
先决条件
要开始实践,您需要准备两台全新的Ubuntu 22.04服务器。在每台服务器上添加一个具有sudo权限的普通用户账户。为此,请按照我们的Ubuntu 22.04初始服务器设置指南进行操作。
我们要保护的应用程序设置基于这个指南。如果您想按照该示例操作,请按照教程中的指示设置您的应用程序和数据库服务器。否则,您可以将本文作为通用参考。
步骤1——设置防火墙
首先,您将为每台服务器实施基线防火墙配置。我们将采用以安全为优先的策略。除SSH流量外,我们将几乎阻止所有其他流量,然后为特定应用程序在防火墙上打开必要的端口。
本指南遵循iptables的语法。在Ubuntu 22.04上,iptables会自动安装并使用nftables后端,因此您无需安装任何额外包。
使用nano或您喜欢的文本编辑器,打开/etc/iptables/rules.v4文件。
- sudo nano /etc/iptables/rules.v4
从防火墙模板指南中复制配置内容。
*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
# 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]
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键确认。
如果您在实际环境中进行实施,请暂时不要重新加载防火墙规则。按照这里提供的规则集加载后,将立即中断应用程序和数据库服务器之间的连接。在重新加载之前,您需要调整规则以符合实际运营需求。
第二步——发现服务正在使用的端口
为了允许组件之间通信,您需要了解正在使用的网络端口。您可以通过检查配置文件找出正确的网络端口,但一个不依赖具体应用的端口查找方法是检查每台机器上哪些服务正在监听连接。
您可以使用netstat工具找出这些信息。由于您的应用程序仅通过IPv4通信,我们将添加-4参数,但如果您也使用IPv6,可以将其删除。查找正在运行的服务所需的其他参数是-p、-l、-u、-n和-t,您可以组合为-plunt。
这些参数的含义如下:
- p: 显示每个套接字所属程序的PID和名称。
- l: 仅显示监听中的套接字。
- u: 显示UDP流量。
- n: 显示数字输出而非服务名称。
- t: 显示TCP流量。
- sudo netstat -4plunt
在您的Web服务器上,输出可能如下所示:
OutputActive 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地址表示所讨论的服务正在监听所有可用地址。
在您的数据库服务器上,输出可能如下所示:
- sudo netstat -4plunt
OutputActive 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限制为私有接口。
在本步骤中,记录您找到的值。这些是您调整防火墙配置所需的网络详细信息。
在您的Web服务器上,需要确保以下端口可访问:
- 所有地址上的端口80
- 所有地址上的端口22(防火墙规则中已考虑)
您的数据库服务器需要确保以下端口可访问:
- 地址192.0.2.30上的端口3306(或与其关联的接口)
- 所有地址上的端口22(防火墙规则中已考虑)
第三步——调整Web服务器防火墙规则
现在您已获得所需的端口信息,接下来将调整Web服务器的防火墙规则集。以sudo权限在编辑器中打开规则文件。
- sudo nano /etc/iptables/rules.v4
在Web服务器上,您需要将端口80添加到可接受的流量列表中。由于该服务器正在监听所有可用地址(通常Web服务器希望从任何位置都能访问),您不会通过接口或目标地址限制规则。
您的网络访问者将使用TCP协议连接。您的框架已有一个名为TCP的自定义链用于处理TCP应用程序异常。您可以在该链上的SSH端口异常后添加端口80。
*filter
. . .
# Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 80 -j ACCEPT
. . .
您的Web服务器将发起与数据库服务器的连接。您的出站流量在防火墙中没有任何限制,已建立连接相关的入站流量也被允许,因此您无需在此服务器上打开任何额外端口来允许此连接。
完成后,保存并关闭文件。您的Web服务器现在拥有允许所有合法流量同时阻止其他所有流量的防火墙策略。
测试您的规则文件是否有语法错误。
- sudo iptables-restore -t < /etc/iptables/rules.v4
如果未显示语法错误,请重新加载防火墙以实施新规则集。
- sudo service iptables-persistent reload
第四步 — 调整数据库服务器防火墙规则
在数据库服务器上,您需要允许访问服务器私有IP地址上的3306端口。在本例中,该地址为192.0.2.30。您可以根据该地址限制访问,也可以通过匹配分配了该地址的接口来限制访问。
要找到与该地址关联的网络接口,请运行 ip -4 addr show scope global。
- ip -4 addr show scope global
Output2: 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权限打开规则文件。
- sudo nano /etc/iptables/rules.v4
同样,您将在TCP链中添加一条规则,为Web和数据库服务器间的连接创建例外。
要根据实际地址限制访问权限,您可以添加如下规则:
*filter
. . .
# Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -d 192.0.2.30 -j ACCEPT
. . .
如果您更倾向于根据包含该地址的接口允许例外情况,可以添加类似此规则:
*filter
. . .
# Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -i eth1 -j ACCEPT
. . .
完成后保存并关闭文件。
使用此命令检查语法错误。
- sudo iptables-restore -t < /etc/iptables/rules.v4
准备就绪后,重新加载防火墙规则。
- sudo service iptables-persistent reload
您的两台服务器现在应该都得到了妥善保护,同时必要的数据流不受限制。
结论
在设置应用程序时,始终将实施适当的防火墙作为部署计划的重要组成部分。尽管我们在上述演示中使用了运行Nginx和MySQL的两台服务器,但所演示的技术无论您选择何种具体技术都适用。
要更深入了解防火墙和iptables,请参考以下指南:
- How To Choose an Effective Firewall Policy to Secure your Servers
- A Deep Dive into Iptables and Netfilter Architecture
- How To Test your Firewall Configuration with Nmap and Tcpdump