
在 甲骨文(Oracle Cloud Infrastructure)云服务器上部署网站、管理面板或其他网络服务时,有一种比较常见的问题:
云平台安全规则明明已经放行了某个端口,但外部仍然无法正常访问。
这类问题很可能是因为服务器操作系统内部的 iptables / ip6tables 仍然存在入站限制。
云平台防火墙和服务器内部防火墙是两层独立的控制机制。
即使云平台允许某个端口,如果服务器内部的防火墙拒绝该连接,外部仍然无法访问。
本文不讨论具体的排查过程,只介绍一种简单、稳定、容易维护的解决方案:
使用 iptables 建立入站端口白名单,只开放真正需要公网访问的端口,其余入站连接默认拒绝。
一、什么是端口白名单?
端口白名单的核心思想非常简单:
只有明确允许的端口才能从公网访问,其他端口全部拒绝。
例如服务器上运行了多个服务:
| 服务 | 公网访问需求 | 防火墙处理 |
|---|---|---|
| SSH | 需要远程管理 | 开放对应端口 |
| 网站 | 需要公网访问 | 开放 HTTP/HTTPS 端口 |
| 管理面板 | 需要远程管理 | 根据实际需求开放 |
| API 服务 | 需要公网访问 | 开放对应端口 |
| 数据库 | 仅服务器内部使用 | 不开放公网 |
| 内部服务 | 仅内部使用 | 不开放公网 |
这里最重要的原则是:
服务器上运行某个服务,并不代表这个服务必须对公网开放。
只有确实需要从互联网访问的服务,才应该开放对应端口。
二、配置 IPv4 防火墙白名单
首先清理当前 INPUT 链中的旧规则:
sudo iptables -F INPUT
允许已经建立的连接:
sudo iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
允许本机回环:
sudo iptables -A INPUT -i lo -j ACCEPT
允许 ICMP:
sudo iptables -A INPUT -p icmp -j ACCEPT
然后开放实际需要公网访问的 TCP 端口:
sudo iptables -A INPUT -p tcp --dport <端口1> -j ACCEPT
sudo iptables -A INPUT -p tcp --dport <端口2> -j ACCEPT
sudo iptables -A INPUT -p tcp --dport <端口3> -j ACCEPT
如果某个服务使用 UDP,则添加 UDP 规则:
sudo iptables -A INPUT -p udp --dport <UDP端口> -j ACCEPT
最后增加一条统一拒绝规则:
sudo iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited
最终形成:
允许已经建立的连接
↓
允许本机通信
↓
允许 ICMP
↓
允许需要公网访问的 TCP/UDP 端口
↓
其他全部拒绝
三、IPv6 也要配置
如果服务器启用了 IPv6,只配置 IPv4 是不完整的。
首先清理 IPv6 INPUT 规则:
sudo ip6tables -F INPUT
允许已经建立的连接:
sudo ip6tables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
允许本机回环:
sudo ip6tables -A INPUT -i lo -j ACCEPT
允许 IPv6 ICMP:
sudo ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
开放需要公网访问的 TCP 端口:
sudo ip6tables -A INPUT -p tcp --dport <端口1> -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport <端口2> -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport <端口3> -j ACCEPT
如果需要 UDP:
sudo ip6tables -A INPUT -p udp --dport <UDP端口> -j ACCEPT
最后:
sudo ip6tables -A INPUT -j REJECT --reject-with icmp6-port-unreachable
四、最重要的一点:REJECT 必须放在最后
iptables 按照规则从上到下依次匹配。
例如下面这种配置是错误的:
允许端口 A
允许端口 B
REJECT 所有其他连接
允许端口 C
端口 C 实际上无法访问。
因为数据包到达 REJECT 规则时已经被拒绝,后面的规则不会再执行。
正确结构应该是:
ACCEPT
ACCEPT
ACCEPT
ACCEPT
ACCEPT
REJECT
因此:
所有需要开放的端口,都必须位于最后一条 REJECT 规则之前。
五、保存防火墙规则
iptables 修改的是当前运行状态。
为了防止服务器重启后规则丢失,可以使用:
sudo netfilter-persistent save
然后设置开机自动加载:
sudo systemctl enable netfilter-persistent
检查服务:
sudo systemctl status netfilter-persistent
保存后的规则通常位于:
/etc/iptables/rules.v4
/etc/iptables/rules.v6
这样服务器重启之后,防火墙规则可以自动恢复。
六、以后新增服务如何开放端口?
这是这套方案最实用的地方。
假设以后服务器新增一个服务:
端口:45678
协议:TCP
只需要在防火墙白名单中增加这个端口即可。
IPv4:
sudo iptables -I INPUT 10 -p tcp --dport 45678 -j ACCEPT
如果需要 IPv6:
sudo ip6tables -I INPUT 10 -p tcp --dport 45678 -j ACCEPT
然后保存:
sudo netfilter-persistent save
这样新的服务端口就加入了公网访问白名单。
七、TCP 和 UDP 要区分
新增端口时,不仅要知道端口号,还要知道服务使用的是 TCP 还是 UDP。
TCP 示例:
sudo iptables -I INPUT 10 -p tcp --dport 45678 -j ACCEPT
UDP 示例:
sudo iptables -I INPUT 10 -p udp --dport 45678 -j ACCEPT
如果一个服务同时需要 TCP 和 UDP,那么需要分别添加两条规则:
sudo iptables -I INPUT 10 -p tcp --dport 45678 -j ACCEPT
sudo iptables -I INPUT 10 -p udp --dport 45678 -j ACCEPT
IPv6 同理。
八、为什么新增端口要使用 -I,而不是简单使用 -A?
如果当前 INPUT 链最后已经存在:
REJECT all
那么直接执行:
sudo iptables -A INPUT -p tcp --dport 45678 -j ACCEPT
会把新的 ACCEPT 规则放到 REJECT 后面。
这样新端口仍然会被前面的 REJECT 拒绝。
因此在这种白名单结构中,应该把新规则插入到 REJECT 前面,例如:
sudo iptables -I INPUT 10 -p tcp --dport 45678 -j ACCEPT
这里的数字 10 是示例位置。
实际操作时,建议先查看当前规则:
sudo iptables -L INPUT -n -v --line-numbers
找到最后的 REJECT 规则所在位置,然后将新的 ACCEPT 规则插入到 REJECT 之前。
九、新增端口后的完整操作流程
以后服务器增加任何需要公网访问的新服务,都可以按照下面的流程操作:
第一步:安装并启动新服务
↓
第二步:确认服务监听的端口
↓
第三步:确认使用 TCP 还是 UDP
↓
第四步:在 OCI 安全规则中允许该端口
↓
第五步:在 iptables 中加入白名单
↓
第六步:IPv6 环境同步配置 ip6tables
↓
第七步:保存 netfilter-persistent
↓
第八步:测试外部访问
这样以后无论新增什么服务,都不需要修改整个防火墙策略。
只需要增加对应的端口规则即可。
十、为什么推荐白名单,而不是开放所有端口?
有些服务器为了方便,会直接让:
INPUT ACCEPT
这样确实简单,但服务器上只要有服务监听某个端口,该服务就有可能直接暴露到公网。
例如服务器可能运行:
- 数据库
- FTP
- RPC
- 缓存服务
- 内部管理服务
- 其他系统服务
这些服务通常并不需要公网访问。
因此更加合理的做法是:
服务可以运行,但只有明确需要公网访问的端口才加入白名单。
这样即使某个服务意外启动并监听端口,也不会自动暴露到公网。
十一、只开放几个端口,会不会被扫描和暴力破解?
会。
需要明确:
端口白名单的作用是减少攻击面,而不是阻止公网扫描。
例如服务器开放了 SSH、网站和其他公网服务,那么这些开放端口仍然可能被互联网扫描器发现。
但是,与开放大量端口相比,白名单可以让大量不需要公网访问的服务直接处于不可访问状态。
可以简单理解为:
开放大量端口:
互联网
↓
大量端口
↓
发现各种服务
↓
针对不同服务进行攻击
端口白名单:
互联网
↓
少量必要端口
↓
其他端口直接拒绝
因此:
白名单不能防止扫描,但能够显著减少可被攻击的入口。
十二、管理入口需要重点保护
并不是所有开放端口的安全风险都一样。
例如 SSH、服务器管理面板等属于管理入口。
如果攻击者成功获取管理账号,风险通常远高于单纯扫描普通业务端口。
因此建议:
- SSH 使用 SSH Key
- 尽可能关闭 SSH 密码登录
- 禁止 root 直接远程登录
- 使用强密码
- 必要时限制管理入口的来源 IP
- 可以使用 Fail2ban 等机制防止连续登录尝试
- 管理面板及时更新
理想状态:
管理入口
↓
限制访问来源
↓
强身份认证
↓
必要时使用 SSH Key
↓
关闭不必要的密码登录
十三、网站端口无法避免公网扫描
如果服务器需要提供公网网站,HTTP/HTTPS 端口通常必须对公网开放。
因此网站可能会遇到:
- 端口扫描
- 漏洞扫描
- 后台路径扫描
- 登录尝试
- 恶意请求
这是公网网站比较常见的现象。
网站本身应该做好:
- 网站程序及时更新
- 后台账号使用强密码
- 不使用默认账号
- 及时修复程序漏洞
- 对重要后台增加额外保护
- 根据实际情况使用 CDN/WAF 等安全服务
十四、OCI 安全规则和服务器防火墙是两层防护
需要特别注意:
OCI Security List / NSG 与服务器内部的 iptables 是两套独立的防火墙。
可以简单理解为:
互联网
↓
OCI Security List / NSG
↓
服务器网卡
↓
iptables / ip6tables
↓
具体服务
一个端口要能够正常访问,通常需要两个层面都允许。
例如新增一个服务:
新服务端口
│
├── OCI 安全规则 → Allow
│
└── 服务器 iptables → ACCEPT
两个条件都满足,外部才能正常连接。
因此以后新增服务端口时,需要同时注意:
- OCI 安全规则是否允许
- IPv4 iptables 是否允许
- IPv6 ip6tables 是否允许(如果使用 IPv6)
- 服务是否真正监听该端口
- 端口使用的是 TCP 还是 UDP
十五、推荐的长期管理方式
对于个人服务器或者中小型服务器,推荐采用这种简单的结构:
OCI
│
└── 负责云平台层面的端口控制
服务器 iptables
│
└── 负责公网入站白名单
各种服务器服务
│
└── 负责实际业务
各自负责自己的事情:
| 组件 | 主要职责 |
|---|---|
| OCI Security List / NSG | 云平台层面的网络访问控制 |
| iptables | 服务器 IPv4 入站访问控制 |
| ip6tables | 服务器 IPv6 入站访问控制 |
| 具体服务 | 提供实际业务功能 |
这样职责清晰,出现问题时也更容易处理。
十六、日常只需要记住这些命令
查看 IPv4 防火墙:
sudo iptables -L INPUT -n -v --line-numbers
查看 IPv6:
sudo ip6tables -L INPUT -n -v --line-numbers
新增 TCP 端口:
sudo iptables -I INPUT 10 -p tcp --dport <端口号> -j ACCEPT
新增 UDP 端口:
sudo iptables -I INPUT 10 -p udp --dport <端口号> -j ACCEPT
IPv6 TCP:
sudo ip6tables -I INPUT 10 -p tcp --dport <端口号> -j ACCEPT
IPv6 UDP:
sudo ip6tables -I INPUT 10 -p udp --dport <端口号> -j ACCEPT
保存规则:
sudo netfilter-persistent save
设置开机自动恢复:
sudo systemctl enable netfilter-persistent
十七、总结
OCI 云服务器出现:
云平台端口已经放行,但外部仍然无法访问
时,除了 OCI 的安全规则之外,还需要考虑服务器内部的 iptables / ip6tables。
一种简单、可靠并且容易维护的方案就是:
建立端口白名单 + 最后一条统一 REJECT + netfilter-persistent 持久化。
以后服务器增加新的公网服务,不需要重新设计整个防火墙。
只需要根据服务实际使用的协议和端口,增加对应的 ACCEPT 规则即可:
TCP 服务
↓
增加 TCP 端口
UDP 服务
↓
增加 UDP 端口
TCP + UDP 服务
↓
分别增加 TCP 和 UDP 规则
仅服务器内部使用的服务
↓
无需开放公网端口
最后保存:
sudo netfilter-persistent save
需要注意的是:
端口白名单不能阻止公网扫描,只能减少公网暴露面。
因此服务器安全应该采用多层防护:
OCI Security List / NSG
↓
iptables / ip6tables
↓
管理入口安全
↓
应用程序自身安全
↓
必要时增加 WAF / Fail2ban 等防护
最终原则只有一句话:
需要对公网开放的端口,明确加入白名单;没有明确开放的端口,默认拒绝。
这样既能保证服务器上的公网服务正常运行,也能最大限度减少不必要的公网暴露面。