2.4 安全组与 VPC:端口开放最小权限

场景:机器上线的第一天就被扫

三台 CVM 挂上公网 IP 的当晚,云监控告警就来了:某台实例的 SSH 端口在过去一小时被尝试登录两万三千次。来源 IP 遍布全球。所幸我们用的是密钥登录,爆破没成功,但这件事把一个事实甩到脸上:公网上没有侥幸,你今天忘了关的端口,明天就有人在打。

复盘时我们翻了三台机器的安全组设置,简直是事故现场:一台放开了全部端口给 0.0.0.0/0(原因:当初调试图省事),一台 MySQL 的 3306 对公网开放,还有一台连 ICMP 都没限制。这一节就是我们从这种状态起步,把网络收拢到最小权限的全过程。

方案:先规划 VPC,再收安全组

私有网络 VPC 是云上网络的逻辑边界:你在里面规划网段、子网、路由,实例之间内网通信,不经过公网。官方文档在 cloud.tencent.com/document/product/213 与各产品的网络配置章节都有说明。我们当初三台机器是默认 VPC 里随手开的,网段混乱,后来从头规整,网段规划如下:

用途 网段 说明
生产 VPC 主网段 10.8.0.0/16 整个生产环境
业务子网 10.8.1.0/24 三个 Go 服务
中间件子网 10.8.2.0/24 MySQL、Redis、RocketMQ
测试环境 10.9.0.0/16 与生产物理隔离

分业务子网和中间件子网不是为了好看,而是让安全组规则可以按网段写:中间件子网只接受业务子网的连接,业务子网只开放 8080 等对外端口。网络结构本身就是第一道权限管理。另外测试环境用了独立 VPC,彻底隔离——谁敢把测试库和生产库放一个网段,谁就得为某天连错库负责。

顺带提一个容易忽略的点:子网掩码别切太细。我们最早按服务把掩码切成 /28,想着每服务一小段整齐好看,结果后来扩容加机器,某段子网地址瞬间不够用,又得开新子网调路由。 subnet 留足余量,业务子网用 /24 是个稳妥的起点,毕竟云上重新规划网段比 rector 代码还折腾人。

规划设计完后,迁移动作也要有节奏:先在新 VPC 开子网、迁实例、最后改老实例。整个过程中业务不停机不现实,我们选在大促后流量最低的凌晨做,回滚方案就靠 上一节的快照。迁移前还做了一件事:把三个服务的配置项里所有硬编码 IP 换成域名或环境变量,这样网段一变,配置跟着一改就齐活,不用回去翻代码。这点功夫其实是对 上一章拆分约定 的回归检验——拆分时说好的所有地址走配置,谁没做到,迁移当晚立马现形。

安全组:最小权限三条军规

安全组是有状态的虚拟防火墙,放行是显式的,默认拒绝。我们整理的三条军规:

第一,对外只开必要端口。Web 服务开 80/443,SSH 改成只允许公司出口 IP 或跳板机访问,绝不给 0.0.0.0/0。MySQL、Redis 一律不进公网,只在内网子网间通行。

第二,按角色分组而不是按机器分组。三个服务对应三个安全组,规则跟着角色走,新增机器直接套用,不用人肉配一遍。

第三,任何临时开放的端口必须有登记和关闭时间。调试时开放 3306 到家宽 IP 是人之常情,但不登记不关闭就是定时炸弹。 最终的规则收敛成下面这张表:

方向 协议端口 来源/目标 说明
入站 TCP 80,443 0.0.0.0/0 对外业务
入站 TCP 2222 公司出口 IP 跳板 SSH
入站 TCP 3306 10.8.1.0/24 仅业务子网连库
入站 TCP 6379 10.8.1.0/24 仅业务子网连缓存
出站 全部 0.0.0.0/0 暂保留,后续收敛

顺带分享一个加固小技巧:把这几条规则配好之后,用一台 VPC 外的机器做连通性自测,逐个端口 nmap 一遍,确认放开的真的只有期望的那几个。我们第一次自测就发现 8080 端口被一条历史规则放行了——那是某次调试遗留的,谁都没注意。自测脚本跑进 CI 后,网络配置也变成了可验证的资产,而不是管理员心里的默契。这类验证看着多此一举,出过一次事你就知道它在救命。

出站全开看起来不够最小权限,但初期服务要拉外部依赖,先跑起来,等稳定后逐步收敛到具体域名 IP。我们也确实见过出站被恶意程序利用连外网的案例,收敛出站是长期目标。

补充一句实践层面的提醒:安全组规则在控制台改完后,建议顺手导出一份存档,或者用 TerraForm 之类工具把规则写成代码统一管理。团队小的时候手动改尚可,机器一多,手动变更就没有可追溯性了,出事的往往是那些记不清由来的老规则。

踩坑与排查

坑一:端口漂移不等于安全。 把 SSH 从 22 改到 2222 只是减少日志噪音,不是防护。真正的防护是密钥登录加来源 IP 限制。别把端口漂移当安全措施写在方案里,会误导后来者。事实上绝大多数扫描器是无差别扫全端口的,你换到 2222 它照样敲,所以图清净可以,别以为这是防御。

坑二:安全组有状态。 放行入站后,回包不用再单独配出站规则。刚开始有人给每条入站规则配对一条出站,规则表臃肿还容易错配。理解机制后规则少了一半。

坑三:内网互通也看安全组。 两台同 VPC 机器连不上,Ping 不通,排查半天发现是安全组互拒。VPC 只保证链路在,通不通还得看安全组。后来排查这类问题的固定三步:看网段、看路由、看安全组。

坑四:改安全组前留后路。 有同事把 SSH 入站规则整个删掉想收紧,结果自己的会话还在,新会话死活连不上,险些把自己锁在外面。养成习惯:改 SSH 相关规则前,先确认至少一个已连接会话在线,或者通过 云监控 的外网带宽指标确认自己没把路堵死。

坑五:安全组变更没有审计。 规则一多就没人记得谁加的、为什么加。我们的做法是给每条规则加备注(原子需求编号或工单号),配合周期性 review,把没人认领的规则清掉。安全组规则和代码一样会腐坏。

坑六:公网带宽告警误判。 收紧安全组那几天,云监控的公网带宽曲线出现若干尖峰,大家吓一跳以为被打。结果是正常用户请求加几台压测机的流量叠加。这提醒我们:网络治理要与监控告警同步进行,否则收拢动作本身会被误读成事故。后来 CLS 日志平台 建起来后,这类判断有了日志佐证才踏实。

坑七:跳板机成了单点。 所有 SSH 收敛到跳板机后,有次跳板机自身配置失误,全员断连二十分钟。教训是跳板机也要有备份与快照,并且安全组里保留一条应急通道(比如特定管理 IP 直连),关键时刻不至于无路可走。

与后续章节的衔接

网络收拢之后,前面几节的成果才真正串起来:本地 docker-compose 定义的端口约定在云上变成安全组规则,自定义镜像 开出来的机器天然套用对应角色的安全组,回滚时切流量也走安全组入口。这套网络底子,后面 API 网关 一节会在它之上架设更细的流量治理。

另外提一句成本心里话:这套规范看着繁琐,初期确实慢,大概多花了两三天。但对比被扫那一晚的心跳加速和后续零事故的睡眠质量,这是团队做过最划算的投资之一。新购买的云服务器上配置网络安全,可以参考腾讯云 CVM 活动页快速上手:https://cloud.tencent.com/act/cps/redirect?redirect=3002&cps_key=2886aaf7fd441c23ba682c6fda7a9aff&from=console 。

小结

安全组与 VPC 的核心是最小权限与默认拒绝:网段规划分清内外,端口只开真正需要的,规则按角色而非按机器组织,变更留痕定期清理。这些原则不难,难的是每次图省事的时候都坚持住。被扫那一晚之后我们养成了一个条件反射:任何端口开放,先问一句到底谁在访问;任何网段规划,先问一句五年后还够不够用。第二篇到这里收尾——服务、机器、镜像、网络四件事都到位,下一篇我们终于可以聊微服务之间怎么说话了。

延伸阅读