防火墙规则 · 网络安全入门
防火墙是怎么决定哪些流量能进来的?
课程目录为什么需要它
互联网上的每台服务器都在被不停地扫描。防火墙是第一道过滤:让你预期的流量到达正确的端口,其余的全部丢弃。读写防火墙规则是安全和运维的核心技能,大多数错误都出在规则顺序和忘了默认规则上。
直觉理解
想象一个拿着规则清单的门卫,规则从上往下检查。第一条匹配的规则说了算,后面的都不再看。如果一条都不匹配,就按默认规则处理,安全的默认是「拒绝」。
地址按块来写,用的是 CIDR 记法:192.0.2.0/24 表示「前 24 位相同的所有地址」,也就是 192.0.2.0 到 192.0.2.255(256 个地址)。0.0.0.0/0 匹配所有地址。Python 的 ipaddress 模块帮你算这些二进制位:ip_address(x) in ip_network(block)。
端口说明访问的是机器上的哪个服务:22 是 SSH(远程登录),443 是 HTTPS(网站)。
定义
首条匹配规则
Rules are checked in order and the first matching rule decides. Order changes the result.规则按顺序检查,第一条匹配的规则决定结果。顺序不同,结果就可能不同。
CIDR 地址块
An address range written as address/prefix length. A /24 block has 256 addresses; /32 is one address; /0 is all of them.用「地址/前缀长度」写出的地址范围。/24 块有 256 个地址;/32 只有一个地址;/0 是全部地址。
示例
这个地址在地址块里吗?
动手试试办公室网络是 192.0.2.0/24。
import ipaddress
office = ipaddress.ip_network("192.0.2.0/24")
for ip in ["192.0.2.10", "192.0.2.255", "192.0.3.10"]:
print(ip, ipaddress.ip_address(ip) in office)
print(office.num_addresses)- L2代表整个地址块的网络对象。
- L4
in检查地址是否落在这个块里。192.0.2.10 True - L4192.0.3.x 是另一个块。
192.0.3.10 False - L5/24 留出 8 位给主机:2⁸ = 256。
256
第一条匹配的说了算
动手试试SSH 只允许办公室访问,HTTPS 谁都能访问,其余全部拒绝。
import ipaddress
rules = [
("allow", "192.0.2.0/24", 22),
("deny", "0.0.0.0/0", 22),
("allow", "0.0.0.0/0", 443),
]
def decide(ip, port):
for action, net, p in rules:
if port == p and ipaddress.ip_address(ip) in ipaddress.ip_network(net):
return action
return "deny"
print(decide("192.0.2.8", 22))
print(decide("203.0.113.9", 22))
print(decide("203.0.113.9", 443))
print(decide("203.0.113.9", 3306))- L9规则从上往下检查。
- L11第一条匹配就立刻返回。
- L12没有规则匹配:默认拒绝。
- L14办公室地址,端口 22:第 1 条允许。
allow - L15外部地址,端口 22:第 1 条不匹配,第 2 条拒绝。
deny - L17端口 3306(数据库)谁都不匹配:默认拒绝。
deny
常见错误
宽泛的规则放在最上面
如果 allow 0.0.0.0/0 22 排在第一条,下面「只允许办公室」的规则就永远用不上:谁都能 SSH。具体的规则要放在宽泛的规则前面。
默认放行
没有明确拒绝就放行的防火墙,会让每一个被遗忘的服务都暴露在外。默认拒绝,只放行你需要的。
小测验
1. 这段代码会输出什么?
import ipaddress
rules = [("allow", "0.0.0.0/0", 22), ("deny", "203.0.113.0/24", 22)]
def decide(ip, port):
for action, net, p in rules:
if port == p and ipaddress.ip_address(ip) in ipaddress.ip_network(net):
return action
return "deny"
print(decide("203.0.113.66", 22), decide("203.0.113.66", 80))查看答案
答案:A
放行所有人的规则排在第一,所以端口 22 永远轮不到下面的拒绝规则。端口 80 没有规则匹配:默认拒绝。
2. 10.0.0.0/30 里有多少个地址?
查看答案
答案:A
32 − 30 = 2 位留给主机,2² = 4。
动手练习
3 道练习,分三层:先预测程序的输出,再按新需求改程序,最后从零写一个。代码在浏览器里运行,提交马上判题。
- 预测输出判断每个数据包
准确写出这段程序的输出。
- 改需求任意端口
两处修改:
- 规则里的端口可以是
None,表示任意端口 - 没有规则匹配时返回
"deny"(现在的代码返回的是"allow")
列表最后那条新规则要在所有端口上封掉整个 198.51.100.0/24 网段,它必须排在最前面才会生效:把它移到列表顶部。
- 规则里的端口可以是
- 从零写从文本读规则
防火墙规则常常写成文本。写一个函数
decide(ip, port, rules),其中rules是这样的字符串列表:allow 192.0.2.0/24 22 deny 0.0.0.0/0 *每条规则是「动作 地址块 端口」,端口
*表示任意端口。按顺序检查规则,返回第一条匹配规则的动作,都不匹配就返回"deny"。port是整数。
本课术语
| 术语 | 中文 | 英文定义 |
|---|---|---|
| Firewall | 防火墙 | A filter that allows or blocks network traffic according to rules. |
| CIDR notation | CIDR 记法 | Writing an address range as address/prefix, such as 192.0.2.0/24. |
| Port | 端口 | A number that selects a service on a machine, such as 22 for SSH or 443 for HTTPS. |
| Default deny | 默认拒绝 | Blocking everything that no rule explicitly allows. |