一、从"内网服务器被攻击"说起
2019年,我参与的一个金融项目出了大事:内网一台开发测试服务器被黑了。
奇怪的是,这台服务器明明没有暴露公网IP,为什么会被攻击?后来排查发现:
- 服务器部署在阿里云经典网络
- 同一区域的另一家公司服务器被攻破
- 攻击者通过经典网络的内网横扫了一片服务器
- 我们的测试服务器"躺着中枪"
这次事故之后,公司所有新项目一律强制使用VPC网络,不再使用经典网络。
这就是VPC的价值:即使在同一朵云上,不同租户的网络也是完全隔离的。
二、VPC基础概念:搞懂这些名词
2.1 什么是VPC
VPC(Virtual Private Cloud,虚拟私有云)是公有云提供的私有网络服务。你可以把它理解为一个完全隔离的虚拟数据中心,在云上构建一个属于自己的私有网络环境。
传统经典网络:
┌─────────────────────────────────────────┐
│ 公有云区域 │
│ │
│ 租户A的服务器 ←→ 租户B的服务器 ←→ ... │
│ 所有用户共享内网,可以互相访问 │
└─────────────────────────────────────────┘
VPC网络:
┌─────────────────────────────────────────┐
│ 公有云区域 │
│ │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ VPC-A │ │ VPC-B │ │
│ │ 192.168.0.0/16│ │ 10.0.0.0/16 │ │
│ │ │ │ │ │
│ │ 服务器A1 │ │ 服务器B1 │ │
│ │ 服务器A2 │ │ 服务器B2 │ │
│ └───────────────┘ └───────────────┘ │
│ VPC之间完全隔离,无法直接互通 │
└─────────────────────────────────────────┘
2.2 VPC核心组件
| 组件 | 作用 | 类比 |
|---|---|---|
| VPC | 虚拟私有网络的基础 | 数据中心的边界 |
| 子网(Subnet) | VPC内的网络分段 | 机房的不同区域 |
| 路由表(Route Table) | 控制流量的走向 | 交通枢纽的指示牌 |
| 网关(Gateway) | 连接VPC与外部网络 | 数据中心的出口 |
| 安全组(Security Group) | 虚拟防火墙,控制入站/出站流量 | 服务器的安保系统 |
| 网络ACL(NACL) | 子网级别的访问控制 | 机房门口的安检 |
2.3 VPC网络规划
一个好的VPC网络规划要考虑:
1. IP地址段选择
- 不要使用公网常见的IP段(避免路由冲突)
- 建议使用RFC 1918定义的私有地址段:
- 10.0.0.0/8(很大,适合大型企业)
- 172.16.0.0/12(中等,适合中型企业)
- 192.168.0.0/16(较小,适合小型企业)
2. 可用区规划
- 公有子网:放置需要公网访问的服务
- 私有子网:放置纯内部服务
- 核心子网:放置数据库等核心组件
3. 分层设计
┌─────────────────────────────────────┐
│ 公有子网(DMZ) │
│ 负载均衡器、Web服务器、API网关 │
└─────────────────┬───────────────────┘
│
┌─────────────────┴───────────────────┐
│ 私有子网(应用层) │
│ 应用服务器、中间件 │
└─────────────────┬───────────────────┘
│
┌─────────────────┴───────────────────┐
│ 核心子网(数据层) │
│ 数据库、缓存 │
└─────────────────────────────────────┘
三、子网设计:分层的艺术
3.1 为什么要分层
典型的三层Web应用架构:
用户请求
↓
负载均衡(ELB/ALB)
↓
Web服务器(公有子网)
↓
应用服务器(私有子网)
↓
数据库(核心子网)
不同层次的服务器有不同的安全需求:
┌──────────────────────────────────────────────────┐
│ VPC (10.0.0.0/16) │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 公有子网 (10.0.1.0/24) │ │
│ │ 可访问公网,可被公网访问 │ │
│ │ 放置:CLB, Nginx, WAF │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 应用子网 (10.0.2.0/24) │ │
│ │ 不可访问公网,仅内网通信 │ │
│ │ 放置:Spring Boot应用 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 数据子网 (10.0.3.0/24) │ │
│ │ 完全隔离,仅特定端口允许访问 │ │
│ │ 放置:MySQL, Redis, Elasticsearch │ │
│ └─────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
3.2 子网配置示例(阿里云)
yaml
# VPC和子网创建
vpc:
name: my-vpc
cidr_block: 10.0.0.0/16
subnets:
- name: public-subnet-az1
vpc_id: my-vpc
cidr: 10.0.1.0/24
zone: cn-beijing-a
type: public # 有公网访问权限
- name: public-subnet-az2
vpc_id: my-vpc
cidr: 10.0.1.128/24
zone: cn-beijing-b
type: public
- name: app-subnet-az1
vpc_id: my-vpc
cidr: 10.0.2.0/24
zone: cn-beijing-a
type: private # 无公网访问权限
- name: app-subnet-az2
vpc_id: my-vpc
cidr: 10.0.2.128/24
zone: cn-beijing-b
type: private
- name: data-subnet-az1
vpc_id: my-vpc
cidr: 10.0.3.0/24
zone: cn-beijing-a
type: private
- name: data-subnet-az2
vpc_id: my-vpc
cidr: 10.0.3.128/24
zone: cn-beijing-b
type: private
3.3 路由表配置
yaml
# 公有子网路由表
public_route_table:
name: public-rt
routes:
# 本VPC内通信
- destination: 10.0.0.0/16
next_hop: local
# 默认路由到NAT网关(访问公网)
- destination: 0.0.0.0/0
next_hop: nat-gw-01
# 私有子网路由表
private_route_table:
name: private-rt
routes:
# 本VPC内通信
- destination: 10.0.0.0/16
next_hop: local
# 不需要默认路由,私有子网不能主动访问公网
3.4 可用区容灾
yaml
# 推荐:每个可用区都部署完整的子网
availability_zones:
- cn-beijing-a
- cn-beijing-b
subnet_mapping:
# 应用层跨AZ部署
app_servers:
- subnet: app-subnet-az1 # AZ1
weight: 100
- subnet: app-subnet-az2 # AZ2
weight: 100
# 数据库层跨AZ部署
database:
primary: data-subnet-az1
secondary: data-subnet-az2
四、跨VPC通信:打通路与路
4.1 为什么需要跨VPC通信
场景1:多账号管理
公司有多个子公司,每个子公司一个VPC
需要总公司可以访问子公司的服务
场景2:开发-测试-生产分离
不同环境在不同VPC
需要从测试环境访问生产环境的某些服务
场景3:混合云架构
公有云VPC + 私有IDC
需要互通
场景4:并购整合
收购的公司有自己的VPC
需要整合网络
4.2 方式一:VPC对等连接(Peering)
最简单的跨VPC通信方式,两个VPC之间建立直接连接。
┌─────────────┐ ┌─────────────┐
│ VPC-A │ │ VPC-B │
│ 10.0.0.0/16│◄──────────►│ 172.16.0.0/16│
│ │ 对等连接 │ │
└─────────────┘ └─────────────┘
yaml
# 阿里云VPC对等连接配置
# 1. 创建对等连接(两端都要操作)
# VPC-A端:
peering_connection:
name: connect-a-to-b
vpc_id: vpc-a
peer_vpc_id: vpc-b
role: initiator # 发起端
# 2. 配置路由表(VPC-A的路由表)
vpc_a_route_table:
routes:
# 添加指向VPC-B的路由
- destination: 172.16.0.0/16
next_hop_type: PeeringConnection
next_hop: pcx-xxxxx
# 3. 配置路由表(VPC-B的路由表)
vpc_b_route_table:
routes:
# 添加指向VPC-A的路由
- destination: 10.0.0.0/16
next_hop_type: PeeringConnection
next_hop: pcx-yyyyy
注意:
- 对等连接不传递路由(需要手动配置两端的路由)
- 不支持TTL(不推荐用于三层以上嵌套)
- 跨账号、跨Region需要更复杂配置
4.3 方式二:云企业网(CEN/TR)
适合多VPC、多账号的大规模网络互联。
┌─────────┐
│ VPC-A │ ──┐
└─────────┘ │ ┌────────────┐ ┌─────────┐
├────►│ 云企业网 │────┤ VPC-C │
┌─────────┐ │ │ (CEN) │ └─────────┘
│ VPC-B │ ──┘ └────────────┘
└─────────┘ │
┌─────────┴─────────┐
│ │
┌────▼────┐ ┌────▼────┐
│ IDC专线 │ │ 其他云 │
│ 接入 │ │ 接入 │
└─────────┘ └─────────┘
yaml
# 阿里云CEN配置
cen:
name: company-cen
# 挂载VPC
attachments:
- name: attach-vpc-a
vpc_id: vpc-a
CenId: cen-xxxxx
- name: attach-vpc-b
vpc_id: vpc-b
CenId: cen-xxxxx
- name: attach-vpc-c
vpc_id: vpc-c
CenId: cen-xxxxx
# 配置路由策略(可选)
route_policy:
- name: allow-all-within-cen
source_region: cn-beijing
destination_region: cn-beijing
action: ACCEPT
4.4 方式三:VPN连接
通过公网建立加密隧道连接两个VPC。
┌─────────────┐ Internet ┌─────────────┐
│ VPC-A │ ══════════════ │ VPC-B │
│ │ IPSec VPN │ │
└─────────────┘ └─────────────┘
yaml
# VPN网关配置
# VPC-A端的VPN配置
vpn_gateway:
name: vpn-gw-a
vpc_id: vpc-a
bandwidth: 10Mbps
# 用户网关(VPC-B的公网IP)
customer_gateway_a:
name: cgw-a
ip: 1.2.3.4 # VPC-B的公网IP
# IPSec连接
vpn_connection:
name: vpn-a-to-b
vpn_gateway_id: vpn-gw-a
customer_gateway_id: cgw-a
ipsec_config:
ike_version: ikev2
ike_auth_algorithm: sha2-256
ike_encryption_algorithm: aes-256
ipsec_auth_algorithm: sha2-256
ipsec_encryption_algorithm: aes-256
pfs: dh-group14
# VPC-A路由表添加VPN路由
route_table:
- destination: 172.16.0.0/16
next_hop_type: VPN
next_hop: vpn-gw-a
4.5 方式四:专线/云连接(最可靠)
通过物理专线连接,速度快、延迟低、带宽大。
┌─────────────┐ ┌─────────────┐
│ VPC-A │ │ IDC │
│ 阿里云 │ ═══════════ │ 私有机房 │
│ │ 专线 │ │
└─────────────┘ └─────────────┘
yaml
# 专线配置
# 1. 创建专线网关
express_connect:
virtual_border_router:
name: vbr-to-idc
vpc_id: vpc-a
circuit_code: "ALibaba-CC-XXXX" # 运营商专线编码
# 2. 配置VLAN
vlan:
vlan_id: 100
client_ip: 10.0.1.10 # 客户端网关IP
Alibaba_ip: 10.0.1.9 # 阿里云网关IP
# 3. 路由配置
route_table:
- destination: 192.168.0.0/16 # IDC内网网段
next_hop_type: VBR
next_hop: vbr-xxxxx
五、安全组与网络ACL:守护网络的安全门神
5.1 安全组 vs 网络ACL
| 维度 | 安全组(Security Group) | 网络ACL(NACL) |
|---|---|---|
| 层级 | 实例级别(网卡) | 子网级别 |
| 有状态 | 有状态(自动放行返回流量) | 无状态(需手动配置双向) |
| 规则 | 白名单(允许) | 白名单(允许)+ 黑名单(拒绝) |
| 评估 | 多个安全组叠加评估 | 按顺序匹配 |
| 范围 | 单个实例 | 整个子网 |
5.2 安全组配置最佳实践
yaml
# Web服务器安全组
web_server_sg:
name: web-server-sg
vpc_id: vpc-a
# 入站规则
ingress_rules:
# HTTP/HTTPS
- protocol: tcp
port: 80,443
source: 0.0.0.0/0
description: "允许HTTP/HTTPS访问"
# SSH(限制来源)
- protocol: tcp
port: 22
source: 10.0.0.0/16
description: "仅允许内网SSH"
# ICMP(监控使用)
- protocol: icmp
port: -1
source: 10.0.0.0/16
# 出站规则(默认允许所有)
egress_rules:
- protocol: all
port: all
destination: 0.0.0.0/0
yaml
# 应用服务器安全组
app_server_sg:
name: app-server-sg
vpc_id: vpc-a
ingress_rules:
# 仅允许来自Web服务器的安全组
- protocol: tcp
port: 8080-8090
source_group: web_server_sg
description: "仅允许Web服务器访问"
# 允许内网访问
- protocol: all
port: all
source: 10.0.0.0/16
egress_rules:
# 仅允许访问数据库
- protocol: tcp
port: 3306
destination_group: db_server_sg
# 允许访问Redis
- protocol: tcp
port: 6379
destination_group: redis_server_sg
# 允许访问公网(NAT网关)
- protocol: all
port: all
destination: 0.0.0.0/0
yaml
# 数据库安全组
db_server_sg:
name: db-server-sg
vpc_id: vpc-a
ingress_rules:
# 仅允许应用服务器访问
- protocol: tcp
port: 3306
source_group: app_server_sg
# 仅允许内网访问(禁止公网)
- protocol: all
port: all
source: 10.0.0.0/16
egress_rules:
# 数据库通常不需要主动出站
- protocol: all
port: all
destination: 127.0.0.1
5.3 安全组层级设计
┌─────────────────────────────────────────────┐
│ 用户请求 (0.0.0.0/0) │
└─────────────────┬───────────────────────────┘
│
┌─────────▼─────────┐
│ WAF/CDN → 公网IP │
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ CLB (公网负载均衡) │
│ 绑定: web-sg │
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ Web服务器 (Nginx) │
│ 绑定: web-sg │
│ 绑定: internal-sg │
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ App服务器 (Java) │
│ 绑定: app-sg │
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ 数据库 (MySQL) │
│ 绑定: db-sg │
└───────────────────┘
5.4 网络ACL配置
yaml
# 子网级别的访问控制
subnet_acl:
public_subnet:
name: public-subnet-nacl
# 入站规则(按优先级排序)
ingress_rules:
- rule_number: 100
protocol: tcp
port: 80
source: 0.0.0.0/0
action: allow
- rule_number: 101
protocol: tcp
port: 443
source: 0.0.0.0/0
action: allow
- rule_number: 200
protocol: all
port: all
source: 0.0.0.0/0
action: deny # 拒绝其他所有
# 出站规则
egress_rules:
- rule_number: 100
protocol: all
port: all
destination: 0.0.0.0/0
action: allow
六、NAT网关:让私有子网也能上网
6.1 公有子网 vs 私有子网
公有子网:
- 有公网IP地址
- 可以主动访问公网
- 可以被公网访问
- 放置:负载均衡、Web服务器、VPN网关
私有子网:
- 没有公网IP
- 不能主动访问公网
- 不能被公网访问
- 放置:应用服务器、数据库、中间件
6.2 NAT网关工作原理
私有子网的服务器想访问公网:
┌─────────────┐
│ App服务器 │ 请求:访问 baidu.com
│ 10.0.2.10 │ 源IP:10.0.2.10
└──────┬──────┘
│ 包:源IP=10.0.2.10, 目标=公网
▼
┌─────────────┐
│ NAT网关 │ 转换:源IP改为NAT公网IP
│ 公网IP: 1.2.3.4│
└──────┬──────┘
│ 包:源IP=1.2.3.4, 目标=公网
▼
Internet
│
│ 响应:目标IP=1.2.3.4
▼
┌─────────────┐
│ NAT网关 │ 反向转换:目标IP改为10.0.2.10
│ │
└──────┬──────┘
│ 包:目标IP=10.0.2.10
▼
┌─────────────┐
│ App服务器 │ 收到响应!
│ 10.0.2.10 │
└─────────────┘
6.3 NAT网关配置
yaml
# 1. 创建NAT网关
nat_gateway:
name: my-nat-gw
vpc_id: vpc-a
specification: small # 小型/中型/大型
billing_type: PayByTraffic # 按流量计费
# 2. 绑定弹性公网IP
eip:
name: nat-gw-eip
bandwidth: 100Mbps
# 3. 配置SNAT规则(让私有子网可以上公网)
snat_rule:
name: snat-for-app-subnet
nat_gateway_id: nat-gw-xxx
source_vswitch_ids:
- vswitch-app-1 # 10.0.2.0/24
- vswitch-app-2 # 10.0.2.128/24
eip_id: eip-xxx
# 4. 配置DNAT规则(可选,让公网可以访问私有服务器)
dnat_rule:
name: dnat-for-app
nat_gateway_id: nat-gw-xxx
mappings:
- external_ip: 1.2.3.4
external_port: 8080
internal_ip: 10.0.2.10
internal_port: 8080
protocol: tcp
6.4 NAT网关 vs NAT实例
| 维度 | NAT网关(云服务) | NAT实例(自建) |
|---|---|---|
| 可用性 | 多AZ自动容灾 | 需要自己配置高可用 |
| 带宽 | 可按需扩展 | 受限于实例规格 |
| 维护 | 完全托管 | 需要自己维护 |
| 费用 | 按流量计费 | 按实例计费 |
| 功能 | 有限 | 可自定义(iptables) |
| 性能 | 高 | 取决于实例规格 |
七、私有DNS:内网的"电话簿"
7.1 为什么需要私有DNS
在VPC内部,如果不用私有DNS:
- 你得记住所有服务器的IP(噩梦)
- IP可能会变(扩容、迁移时)
- 服务发现困难
私有DNS让你可以用域名来访问内网服务:
# 不用私有DNS
curl http://10.0.2.10:8080/api/users
# 用私有DNS
curl http://user-service.internal/api/users
7.2 私有DNS配置
yaml
# 1. 创建私有DNS Zone
private_dns_zone:
name: internal.local
vpc_ids:
- vpc-a
# 2. 添加DNS记录
dns_records:
# 应用服务
- name: user-service
type: A
ttl: 60
values:
- 10.0.2.10
- name: order-service
type: A
ttl: 60
values:
- 10.0.2.20
# 数据库(别名指向RDS)
- name: mysql-primary
type: CNAME
ttl: 60
value: rm-xxxxx.mysql.rds.aliyuncs.com
- name: redis
type: CNAME
ttl: 60
value: r-xxxxx.redis.rds.aliyuncs.com
7.3 DNS解析配置
bash
# 在VPC内使用阿里云DNS
# /etc/sysconfig/network-scripts/ifcfg-eth0
DNS1=100.100.2.136 # 阿里云VPC内网DNS
DNS2=100.100.2.138
# 验证DNS解析
nslookup user-service.internal
八、网络监控与故障排查
8.1 常见网络问题
问题1:VPC内服务器之间不通
bash
# 排查步骤
# 1. 检查安全组规则
security_group_check sg-xxxx
# 2. 检查网络ACL
nacl_check acl-xxxx
# 3. 检查路由表
route_table_check rt-xxxx
# 4. 检查VPC Peering配置
peering_check pcx-xxxx
# 5. 用tcpdump抓包分析
tcpdump -i eth0 host 10.0.2.10 -nn
问题2:私有子网无法访问公网
bash
# 排查步骤
# 1. 检查NAT网关是否可用
nat_gateway_status nat-gw-xxx
# 2. 检查SNAT规则
snat_rule_check snat-xxxx
# 3. 检查私有子网的路由表
# 确认有 0.0.0.0/0 -> nat-gw 的路由
# 4. 检查NAT网关的EIP是否可用
eip_status eip-xxxx
# 5. 测试从NAT网关出去
ping 8.8.8.8
curl https://www.google.com
问题3:跨VPC访问不通
bash
# 排查步骤
# 1. 确认对等连接状态
peering_status pc-xxxx
# 2. 检查两端路由表配置
# VPC-A路由表有指向VPC-B的路由
# VPC-B路由表有指向VPC-A的路由
# 3. 检查两端安全组
# VPC-A的安全组允许VPC-B的网段
# VPC-B的安全组允许VPC-A的网段
# 4. 检查带宽包/流量限制
bandwidth_limit pc-xxxx
8.2 网络连通性测试脚本
bash
#!/bin/bash
# network_check.sh - VPC网络连通性检查
VPC_CIDR="10.0.0.0/16"
APP_SUBNET="10.0.2.0/24"
DB_SUBNET="10.0.3.0/24"
echo "=== VPC网络连通性检查 ==="
# 1. 检查基础网络
echo -e "\n[1] 检查基础网络..."
ping -c 2 100.100.2.136 # VPC DNS
# 2. 检查公网访问(通过NAT)
echo -e "\n[2] 检查公网访问..."
curl -s --connect-timeout 5 https://www.aliyun.com > /dev/null && echo "✓ 公网访问正常" || echo "✗ 公网访问失败"
# 3. 检查内网DNS
echo -e "\n[3] 检查内网DNS..."
nslookup user-service.internal 127.0.0.1 > /dev/null 2>&1 && echo "✓ DNS解析正常" || echo "✗ DNS解析失败"
# 4. 检查服务端口
echo -e "\n[4] 检查服务端口..."
nc -zv 10.0.3.10 3306 -w 3 && echo "✓ MySQL可连接" || echo "✗ MySQL连接失败"
# 5. 检查安全组
echo -e "\n[5] 检查安全组..."
sg=$(curl -s http://100.100.100.200/latest/meta-data/security-groups)
echo "当前实例安全组: $sg"
echo -e "\n=== 检查完成 ==="
九、踩坑实录:VPC网络那些坑
坑1:VPC CIDR太小,IP不够用
某公司创建VPC时用了192.168.0.0/24,后来业务扩张,需要多个子网,发现IP不够了。
教训:VPC创建后CIDR不能改!一定要提前规划,预留足够空间。建议使用/16或/20的CIDR。
坑2:安全组规则太宽松
某项目为了"方便调试",把安全组设置成了0.0.0.0/0全开放。结果被扫描到弱密码,服务器被黑了。
教训:安全组遵循最小权限原则,只开放必需的端口和来源IP。
坑3:NAT网关忘记配置SNAT
部署了一套新应用在私有子网,发现应用无法访问外部API。
排查:私有子网默认不能访问公网,需要配置NAT网关的SNAT规则。
教训:私有子网部署服务时,先确认是否需要公网访问,如果需要,提前配置NAT网关。
坑4:对等连接忘记配置反向路由
创建了VPC-A到VPC-B的对等连接,但只配了VPC-A的路由,忘了配VPC-B的路由,导致单向不通。
教训:对等连接需要两端都配置路由,缺一不可。
坑5:混合云VPN不稳定
通过VPN连接阿里云VPC和公司IDC,VPN经常断线,影响业务。
解决:使用专线替换VPN,或者配置VPN双机热备。
教训:VPN适合低频、非关键业务,关键业务建议用专线。
十、总结
VPC网络是云上安全架构的基石:
- 网络隔离:不同租户VPC完全隔离
- 分层设计:公有子网、私有子网、核心子网各司其职
- 安全防护:安全组+网络ACL双重防护
- 灵活互联:VPC对等、CEN、VPN、专线多种方式
- 私有DNS:用域名代替IP,服务发现更灵活
最佳实践:
- VPC创建时选择足够大的CIDR
- 每个可用区创建完整的子网(高可用)
- 安全组遵循最小权限原则
- 私有子网通过NAT网关访问公网
- 使用私有DNS管理内网域名
- 定期审计网络配置,发现潜在风险
血的教训:
VPC网络安全无小事。任何一条过于宽松的规则,都可能成为攻击者的入口。建议使用配置管理工具(如Terraform)管理VPC配置,每次变更有审批流程,重要规则变更后做安全审计。
思考题: 你们公司的云上网络是如何规划的?有没有遇到过头疼的VPC网络问题?如果有一天需要把VPC和IDC打通,你会选择VPN还是专线?
个人观点,仅供参考