安全组和访问控制
点对点安全组策略
为什么使用安全组引用
传统方式(IP 地址)的问题:
❌ 硬编码 IP 地址
规则示例:
├─ 允许来自 10.0.11.0/24 的 3306 端口
├─ 允许来自 10.0.12.0/24 的 3306 端口
└─ 允许来自 10.0.13.0/24 的 3306 端口
问题:
├─ 子网变更需要更新所有规则
├─ 难以管理和审计
├─ 容易出错(复制粘贴错误)
└─ 无法表达"应用层"这样的逻辑概念
安全组引用方式(推荐):
✅ 使用安全组 ID 引用
规则示例:
└─ 允许来自 sg-app-layer 的 3306 端口
优势:
├─ 逻辑清晰:明确表达"应用层可访问数据库"
├─ 易于管理:安全组成员变更自动生效
├─ 灵活性:实例可以有多个安全组
├─ 可审计:清晰的访问关系图
└─ 避免 IP 硬编码
安全组架构设计
分层策略:
互联网
↓
┌───────────────┐
│ ALB 安全组 │
│ sg-alb │
└───────┬───────┘
↓
┌───────────────┐
│ Web 层 │
│ sg-web │
└───────┬───────┘
↓
┌───────────────┐
│ 应用层 │
│ sg-app │
└───┬───────┬───┘
│ │
┌────────┘ └────────┐
↓ ↓
┌─────────────┐ ┌──────────────┐
│ 缓存层 │ │ 数据库层 │
│ sg-redis │ │ sg-rds │
└─────────────┘ └──────────────┘
安全组命名规范
命名模式:
格式:{environment}-{service}-{layer}-sg
示例:
├─ prod-alb-public-sg (生产 ALB)
├─ prod-eks-nodes-sg (生产 EKS 节点)
├─ prod-rds-postgres-sg (生产 RDS)
├─ prod-redis-cluster-sg (生产 Redis)
├─ dev-app-private-sg (开发应用)
└─ shared-vpce-sg (共享 VPC Endpoint)
安全组详细设计
1. ALB 安全组
用途: Application Load Balancer
入站规则:
协议 端口 源 说明
-------------------------------------------------------
TCP 443 0.0.0.0/0 HTTPS from 互联网
TCP 80 0.0.0.0/0 HTTP from 互联网(重定向到 HTTPS)
出站规则:
协议 端口 目标 说明
-------------------------------------------------------
TCP ALL sg-eks-nodes 转发到 EKS 节点(动态端口)
创建脚本:
#!/bin/bash
# create-alb-sg.sh
source vpc-config.sh
REGION="us-east-1"
SG_NAME="prod-alb-public-sg"
DESCRIPTION="Security group for production ALB"
# 创建安全组
ALB_SG_ID=$(aws ec2 create-security-group \
--group-name $SG_NAME \
--description "$DESCRIPTION" \
--vpc-id $VPC_ID \
--region $REGION \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=$SG_NAME},
{Key=Environment,Value=production},
{Key=Layer,Value=loadbalancer}
]" \
--query 'GroupId' \
--output text)
echo "ALB 安全组 ID: $ALB_SG_ID"
# 添加入站规则
echo "添加入站规则..."
# HTTPS from 互联网
aws ec2 authorize-security-group-ingress \
--group-id $ALB_SG_ID \
--protocol tcp \
--port 443 \
--cidr 0.0.0.0/0 \
--region $REGION
# HTTP from 互联网
aws ec2 authorize-security-group-ingress \
--group-id $ALB_SG_ID \
--protocol tcp \
--port 80 \
--cidr 0.0.0.0/0 \
--region $REGION
echo "入站规则添加完成"
# 保存 SG ID
echo "export ALB_SG_ID=$ALB_SG_ID" >> sg-config.sh
2. EKS 控制平面安全组
用途: EKS 集群控制平面
入站规则:
协议 端口 源 说明
-------------------------------------------------------
TCP 443 sg-eks-nodes 节点访问 API Server
TCP 443 sg-bastion 管理员访问(kubectl)
TCP 443 办公室IP/32 管理员远程访问
出站规则:
协议 端口 目标 说明
-------------------------------------------------------
TCP ALL sg-eks-nodes 控制平面到节点通信
TCP 443 0.0.0.0/0 访问 AWS API
创建脚本:
#!/bin/bash
# create-eks-control-plane-sg.sh
source vpc-config.sh
SG_NAME="prod-eks-control-plane-sg"
DESCRIPTION="Security group for EKS control plane"
OFFICE_IP="203.0.113.0/24" # 替换为实际办公室 IP
# 创建安全组
EKS_CP_SG_ID=$(aws ec2 create-security-group \
--group-name $SG_NAME \
--description "$DESCRIPTION" \
--vpc-id $VPC_ID \
--region us-east-1 \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=$SG_NAME},
{Key=Environment,Value=production}
]" \
--query 'GroupId' \
--output text)
echo "EKS 控制平面安全组 ID: $EKS_CP_SG_ID"
# 注意:节点安全组的入站规则需要在节点 SG 创建后添加
# 添加办公室 IP 访问
aws ec2 authorize-security-group-ingress \
--group-id $EKS_CP_SG_ID \
--protocol tcp \
--port 443 \
--cidr $OFFICE_IP \
--region us-east-1
echo "export EKS_CP_SG_ID=$EKS_CP_SG_ID" >> sg-config.sh
3. EKS Worker Nodes 安全组
用途: EKS 工作节点(EC2 实例)
入站规则:
协议 端口 源 说明
---------------------------------------------------------
TCP ALL sg-eks-nodes 节点间通信(Pod 网络)
TCP ALL sg-eks-cp 控制平面到节点
TCP ALL sg-alb ALB 健康检查和流量
TCP 22 sg-bastion SSH 访问(调试)
TCP 10250 sg-eks-cp Kubelet API
出站规则:
协议 端口 目标 说明
-------------------------------------------------------
TCP ALL 0.0.0.0/0 所有出站流量(拉取镜像、访问 AWS API)
创建脚本:
#!/bin/bash
# create-eks-nodes-sg.sh
source vpc-config.sh
source sg-config.sh
SG_NAME="prod-eks-nodes-sg"
DESCRIPTION="Security group for EKS worker nodes"
# 创建安全组
EKS_NODES_SG_ID=$(aws ec2 create-security-group \
--group-name $SG_NAME \
--description "$DESCRIPTION" \
--vpc-id $VPC_ID \
--region us-east-1 \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=$SG_NAME},
{Key=Environment,Value=production},
{Key=kubernetes.io/cluster/production-eks-cluster,Value=owned}
]" \
--query 'GroupId' \
--output text)
echo "EKS Nodes 安全组 ID: $EKS_NODES_SG_ID"
# 添加入站规则
# 1. 节点间通信(所有协议和端口)
echo "添加节点间通信规则..."
aws ec2 authorize-security-group-ingress \
--group-id $EKS_NODES_SG_ID \
--protocol -1 \
--source-group $EKS_NODES_SG_ID \
--region us-east-1
# 2. 控制平面到节点
echo "添加控制平面访问规则..."
aws ec2 authorize-security-group-ingress \
--group-id $EKS_NODES_SG_ID \
--protocol -1 \
--source-group $EKS_CP_SG_ID \
--region us-east-1
# 3. ALB 访问(所有端口,因为 NodePort 是动态的)
echo "添加 ALB 访问规则..."
aws ec2 authorize-security-group-ingress \
--group-id $EKS_NODES_SG_ID \
--protocol tcp \
--port 30000-32767 \
--source-group $ALB_SG_ID \
--region us-east-1
# 4. Kubelet API
echo "添加 Kubelet API 访问规则..."
aws ec2 authorize-security-group-ingress \
--group-id $EKS_NODES_SG_ID \
--protocol tcp \
--port 10250 \
--source-group $EKS_CP_SG_ID \
--region us-east-1
# 更新 ALB 出站规则(指向节点)
echo "更新 ALB 出站规则..."
aws ec2 authorize-security-group-egress \
--group-id $ALB_SG_ID \
--protocol tcp \
--port 30000-32767 \
--source-group $EKS_NODES_SG_ID \
--region us-east-1
# 更新控制平面入站规则(来自节点)
echo "更新控制平面入站规则..."
aws ec2 authorize-security-group-ingress \
--group-id $EKS_CP_SG_ID \
--protocol tcp \
--port 443 \
--source-group $EKS_NODES_SG_ID \
--region us-east-1
# 更新控制平面出站规则(到节点)
echo "更新控制平面出站规则..."
aws ec2 authorize-security-group-egress \
--group-id $EKS_CP_SG_ID \
--protocol -1 \
--destination-security-group $EKS_NODES_SG_ID \
--region us-east-1
echo "export EKS_NODES_SG_ID=$EKS_NODES_SG_ID" >> sg-config.sh
4. RDS PostgreSQL 安全组
用途: RDS 数据库实例
入站规则:
协议 端口 源 说明
-------------------------------------------------------
TCP 5432 sg-eks-nodes 应用 Pod 访问数据库
TCP 5432 sg-bastion 管理员访问(psql)
出站规则:
无需出站规则(数据库不主动发起连接)
创建脚本:
#!/bin/bash
# create-rds-sg.sh
source vpc-config.sh
source sg-config.sh
SG_NAME="prod-rds-postgres-sg"
DESCRIPTION="Security group for RDS PostgreSQL"
# 创建安全组
RDS_SG_ID=$(aws ec2 create-security-group \
--group-name $SG_NAME \
--description "$DESCRIPTION" \
--vpc-id $VPC_ID \
--region us-east-1 \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=$SG_NAME},
{Key=Environment,Value=production},
{Key=Service,Value=rds}
]" \
--query 'GroupId' \
--output text)
echo "RDS 安全组 ID: $RDS_SG_ID"
# 添加入站规则
# 1. EKS 节点访问(应用 Pod)
echo "添加 EKS 节点访问规则..."
aws ec2 authorize-security-group-ingress \
--group-id $RDS_SG_ID \
--protocol tcp \
--port 5432 \
--source-group $EKS_NODES_SG_ID \
--region us-east-1
# 2. Bastion 访问(管理)
if [ -n "$BASTION_SG_ID" ]; then
echo "添加 Bastion 访问规则..."
aws ec2 authorize-security-group-ingress \
--group-id $RDS_SG_ID \
--protocol tcp \
--port 5432 \
--source-group $BASTION_SG_ID \
--region us-east-1
fi
# 移除默认出站规则
echo "移除默认出站规则..."
DEFAULT_EGRESS_RULE=$(aws ec2 describe-security-groups \
--group-ids $RDS_SG_ID \
--query 'SecurityGroups[0].IpPermissionsEgress[0]' \
--region us-east-1)
aws ec2 revoke-security-group-egress \
--group-id $RDS_SG_ID \
--ip-permissions "$DEFAULT_EGRESS_RULE" \
--region us-east-1
echo "RDS 安全组无出站规则(按最小权限原则)"
echo "export RDS_SG_ID=$RDS_SG_ID" >> sg-config.sh
5. Redis 安全组
用途: ElastiCache Redis 集群
入站规则:
协议 端口 源 说明
-------------------------------------------------------
TCP 6379 sg-eks-nodes 应用 Pod 访问 Redis
出站规则:
无需出站规则
创建脚本:
#!/bin/bash
# create-redis-sg.sh
source vpc-config.sh
source sg-config.sh
SG_NAME="prod-redis-cluster-sg"
DESCRIPTION="Security group for Redis cluster"
# 创建安全组
REDIS_SG_ID=$(aws ec2 create-security-group \
--group-name $SG_NAME \
--description "$DESCRIPTION" \
--vpc-id $VPC_ID \
--region us-east-1 \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=$SG_NAME},
{Key=Environment,Value=production},
{Key=Service,Value=redis}
]" \
--query 'GroupId' \
--output text)
echo "Redis 安全组 ID: $REDIS_SG_ID"
# 添加入站规则
echo "添加 EKS 节点访问规则..."
aws ec2 authorize-security-group-ingress \
--group-id $REDIS_SG_ID \
--protocol tcp \
--port 6379 \
--source-group $EKS_NODES_SG_ID \
--region us-east-1
# 移除默认出站规则
echo "移除默认出站规则..."
DEFAULT_EGRESS_RULE=$(aws ec2 describe-security-groups \
--group-ids $REDIS_SG_ID \
--query 'SecurityGroups[0].IpPermissionsEgress[0]' \
--region us-east-1)
aws ec2 revoke-security-group-egress \
--group-id $REDIS_SG_ID \
--ip-permissions "$DEFAULT_EGRESS_RULE" \
--region us-east-1
echo "export REDIS_SG_ID=$REDIS_SG_ID" >> sg-config.sh
6. VPC Endpoint 安全组
用途: Interface VPC Endpoints(ECR、Logs、STS 等)
入站规则:
协议 端口 源 说明
-------------------------------------------------------
TCP 443 sg-eks-nodes 节点访问 AWS 服务
TCP 443 sg-bastion 管理员访问
出站规则:
无需出站规则
创建脚本:
#!/bin/bash
# create-vpce-sg.sh
source vpc-config.sh
source sg-config.sh
SG_NAME="prod-vpce-sg"
DESCRIPTION="Security group for VPC Endpoints"
# 创建安全组
VPCE_SG_ID=$(aws ec2 create-security-group \
--group-name $SG_NAME \
--description "$DESCRIPTION" \
--vpc-id $VPC_ID \
--region us-east-1 \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=$SG_NAME},
{Key=Environment,Value=production}
]" \
--query 'GroupId' \
--output text)
echo "VPC Endpoint 安全组 ID: $VPCE_SG_ID"
# 添加入站规则
echo "添加 EKS 节点访问规则..."
aws ec2 authorize-security-group-ingress \
--group-id $VPCE_SG_ID \
--protocol tcp \
--port 443 \
--source-group $EKS_NODES_SG_ID \
--region us-east-1
# Bastion 访问
if [ -n "$BASTION_SG_ID" ]; then
echo "添加 Bastion 访问规则..."
aws ec2 authorize-security-group-ingress \
--group-id $VPCE_SG_ID \
--protocol tcp \
--port 443 \
--source-group $BASTION_SG_ID \
--region us-east-1
fi
# 移除默认出站规则
DEFAULT_EGRESS_RULE=$(aws ec2 describe-security-groups \
--group-ids $VPCE_SG_ID \
--query 'SecurityGroups[0].IpPermissionsEgress[0]' \
--region us-east-1)
aws ec2 revoke-security-group-egress \
--group-id $VPCE_SG_ID \
--ip-permissions "$DEFAULT_EGRESS_RULE" \
--region us-east-1
echo "export VPCE_SG_ID=$VPCE_SG_ID" >> sg-config.sh
7. Bastion Host 安全组
用途: 跳板机(SSH 堡垒机)
入站规则:
协议 端口 源 说明
-------------------------------------------------------
TCP 22 办公室IP/32 SSH 访问
出站规则:
协议 端口 目标 说明
-------------------------------------------------------
TCP 22 sg-eks-nodes SSH 到节点
TCP 5432 sg-rds 访问数据库
TCP 443 0.0.0.0/0 访问 AWS API
创建脚本:
#!/bin/bash
# create-bastion-sg.sh
source vpc-config.sh
SG_NAME="prod-bastion-sg"
DESCRIPTION="Security group for Bastion Host"
OFFICE_IP="203.0.113.0/24" # 替换为实际办公室 IP
# 创建安全组
BASTION_SG_ID=$(aws ec2 create-security-group \
--group-name $SG_NAME \
--description "$DESCRIPTION" \
--vpc-id $VPC_ID \
--region us-east-1 \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=$SG_NAME},
{Key=Environment,Value=production}
]" \
--query 'GroupId' \
--output text)
echo "Bastion 安全组 ID: $BASTION_SG_ID"
# 添加入站规则
echo "添加办公室 SSH 访问规则..."
aws ec2 authorize-security-group-ingress \
--group-id $BASTION_SG_ID \
--protocol tcp \
--port 22 \
--cidr $OFFICE_IP \
--region us-east-1
echo "export BASTION_SG_ID=$BASTION_SG_ID" >> sg-config.sh
echo "⚠️ 注意:Bastion 的出站规则将在其他安全组创建后配置"
完整创建脚本
一键创建所有安全组:
#!/bin/bash
# create-all-security-groups.sh
# 按依赖顺序创建所有安全组
set -e
echo "================================================"
echo "创建所有生产环境安全组"
echo "================================================"
# 检查前置条件
if [ ! -f vpc-config.sh ]; then
echo "错误:未找到 vpc-config.sh"
echo "请先运行 create-production-vpc.sh"
exit 1
fi
source vpc-config.sh
# 清空之前的 sg-config.sh
> sg-config.sh
REGION="us-east-1"
OFFICE_IP="203.0.113.0/24" # ⚠️ 替换为实际办公室 IP
# 第一批:独立安全组(无依赖)
echo ""
echo "第一批:创建独立安全组..."
# 1. ALB 安全组
echo ""
echo "1/7 创建 ALB 安全组..."
ALB_SG_ID=$(aws ec2 create-security-group \
--group-name "prod-alb-public-sg" \
--description "Security group for production ALB" \
--vpc-id $VPC_ID \
--region $REGION \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=prod-alb-public-sg},
{Key=Environment,Value=production},
{Key=Layer,Value=loadbalancer}
]" \
--query 'GroupId' \
--output text)
echo " ALB SG: $ALB_SG_ID"
echo "export ALB_SG_ID=$ALB_SG_ID" >> sg-config.sh
# 添加 ALB 入站规则
aws ec2 authorize-security-group-ingress \
--group-id $ALB_SG_ID \
--ip-permissions \
IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges='[{CidrIp=0.0.0.0/0,Description="HTTPS from Internet"}]' \
IpProtocol=tcp,FromPort=80,ToPort=80,IpRanges='[{CidrIp=0.0.0.0/0,Description="HTTP from Internet"}]' \
--region $REGION
# 2. EKS 控制平面安全组
echo ""
echo "2/7 创建 EKS 控制平面安全组..."
EKS_CP_SG_ID=$(aws ec2 create-security-group \
--group-name "prod-eks-control-plane-sg" \
--description "Security group for EKS control plane" \
--vpc-id $VPC_ID \
--region $REGION \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=prod-eks-control-plane-sg},
{Key=Environment,Value=production}
]" \
--query 'GroupId' \
--output text)
echo " EKS CP SG: $EKS_CP_SG_ID"
echo "export EKS_CP_SG_ID=$EKS_CP_SG_ID" >> sg-config.sh
# 添加办公室访问
aws ec2 authorize-security-group-ingress \
--group-id $EKS_CP_SG_ID \
--protocol tcp \
--port 443 \
--cidr $OFFICE_IP \
--region $REGION
# 3. Bastion 安全组
echo ""
echo "3/7 创建 Bastion 安全组..."
BASTION_SG_ID=$(aws ec2 create-security-group \
--group-name "prod-bastion-sg" \
--description "Security group for Bastion Host" \
--vpc-id $VPC_ID \
--region $REGION \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=prod-bastion-sg},
{Key=Environment,Value=production}
]" \
--query 'GroupId' \
--output text)
echo " Bastion SG: $BASTION_SG_ID"
echo "export BASTION_SG_ID=$BASTION_SG_ID" >> sg-config.sh
# 添加 SSH 访问
aws ec2 authorize-security-group-ingress \
--group-id $BASTION_SG_ID \
--protocol tcp \
--port 22 \
--cidr $OFFICE_IP \
--region $REGION
# 第二批:依赖第一批的安全组
echo ""
echo "第二批:创建依赖安全组..."
# 4. EKS Nodes 安全组
echo ""
echo "4/7 创建 EKS Nodes 安全组..."
EKS_NODES_SG_ID=$(aws ec2 create-security-group \
--group-name "prod-eks-nodes-sg" \
--description "Security group for EKS worker nodes" \
--vpc-id $VPC_ID \
--region $REGION \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=prod-eks-nodes-sg},
{Key=Environment,Value=production},
{Key=kubernetes.io/cluster/production-eks-cluster,Value=owned}
]" \
--query 'GroupId' \
--output text)
echo " EKS Nodes SG: $EKS_NODES_SG_ID"
echo "export EKS_NODES_SG_ID=$EKS_NODES_SG_ID" >> sg-config.sh
# 添加节点入站规则
echo " 配置节点入站规则..."
# 节点间通信
aws ec2 authorize-security-group-ingress \
--group-id $EKS_NODES_SG_ID \
--protocol -1 \
--source-group $EKS_NODES_SG_ID \
--region $REGION
# 控制平面到节点
aws ec2 authorize-security-group-ingress \
--group-id $EKS_NODES_SG_ID \
--protocol -1 \
--source-group $EKS_CP_SG_ID \
--region $REGION
# ALB 到节点(NodePort 范围)
aws ec2 authorize-security-group-ingress \
--group-id $EKS_NODES_SG_ID \
--protocol tcp \
--port 30000-32767 \
--source-group $ALB_SG_ID \
--region $REGION
# Bastion SSH
aws ec2 authorize-security-group-ingress \
--group-id $EKS_NODES_SG_ID \
--protocol tcp \
--port 22 \
--source-group $BASTION_SG_ID \
--region $REGION
# 更新控制平面和 ALB 规则
echo " 更新控制平面和 ALB 规则..."
# 控制平面入站:节点访问
aws ec2 authorize-security-group-ingress \
--group-id $EKS_CP_SG_ID \
--protocol tcp \
--port 443 \
--source-group $EKS_NODES_SG_ID \
--region $REGION
# 控制平面出站:到节点
aws ec2 authorize-security-group-egress \
--group-id $EKS_CP_SG_ID \
--protocol -1 \
--destination-security-group $EKS_NODES_SG_ID \
--region $REGION
# 移除控制平面默认出站规则
DEFAULT_CP_EGRESS=$(aws ec2 describe-security-groups \
--group-ids $EKS_CP_SG_ID \
--query 'SecurityGroups[0].IpPermissionsEgress[?CidrIp==`0.0.0.0/0`]' \
--region $REGION)
if [ "$DEFAULT_CP_EGRESS" != "[]" ]; then
aws ec2 revoke-security-group-egress \
--group-id $EKS_CP_SG_ID \
--protocol -1 \
--cidr 0.0.0.0/0 \
--region $REGION 2>/dev/null || true
fi
# ALB 出站:到节点
aws ec2 authorize-security-group-egress \
--group-id $ALB_SG_ID \
--protocol tcp \
--port 30000-32767 \
--destination-security-group $EKS_NODES_SG_ID \
--region $REGION
# 移除 ALB 默认出站规则
aws ec2 revoke-security-group-egress \
--group-id $ALB_SG_ID \
--protocol -1 \
--cidr 0.0.0.0/0 \
--region $REGION 2>/dev/null || true
# 5. RDS 安全组
echo ""
echo "5/7 创建 RDS 安全组..."
RDS_SG_ID=$(aws ec2 create-security-group \
--group-name "prod-rds-postgres-sg" \
--description "Security group for RDS PostgreSQL" \
--vpc-id $VPC_ID \
--region $REGION \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=prod-rds-postgres-sg},
{Key=Environment,Value=production},
{Key=Service,Value=rds}
]" \
--query 'GroupId' \
--output text)
echo " RDS SG: $RDS_SG_ID"
echo "export RDS_SG_ID=$RDS_SG_ID" >> sg-config.sh
# RDS 入站规则
aws ec2 authorize-security-group-ingress \
--group-id $RDS_SG_ID \
--ip-permissions \
IpProtocol=tcp,FromPort=5432,ToPort=5432,UserIdGroupPairs="[{GroupId=$EKS_NODES_SG_ID,Description='From EKS nodes'}]" \
IpProtocol=tcp,FromPort=5432,ToPort=5432,UserIdGroupPairs="[{GroupId=$BASTION_SG_ID,Description='From Bastion'}]" \
--region $REGION
# 移除 RDS 默认出站规则
aws ec2 revoke-security-group-egress \
--group-id $RDS_SG_ID \
--protocol -1 \
--cidr 0.0.0.0/0 \
--region $REGION
# 6. Redis 安全组
echo ""
echo "6/7 创建 Redis 安全组..."
REDIS_SG_ID=$(aws ec2 create-security-group \
--group-name "prod-redis-cluster-sg" \
--description "Security group for Redis cluster" \
--vpc-id $VPC_ID \
--region $REGION \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=prod-redis-cluster-sg},
{Key=Environment,Value=production},
{Key=Service,Value=redis}
]" \
--query 'GroupId' \
--output text)
echo " Redis SG: $REDIS_SG_ID"
echo "export REDIS_SG_ID=$REDIS_SG_ID" >> sg-config.sh
# Redis 入站规则
aws ec2 authorize-security-group-ingress \
--group-id $REDIS_SG_ID \
--protocol tcp \
--port 6379 \
--source-group $EKS_NODES_SG_ID \
--region $REGION
# 移除 Redis 默认出站规则
aws ec2 revoke-security-group-egress \
--group-id $REDIS_SG_ID \
--protocol -1 \
--cidr 0.0.0.0/0 \
--region $REGION
# 7. VPC Endpoint 安全组
echo ""
echo "7/7 创建 VPC Endpoint 安全组..."
VPCE_SG_ID=$(aws ec2 create-security-group \
--group-name "prod-vpce-sg" \
--description "Security group for VPC Endpoints" \
--vpc-id $VPC_ID \
--region $REGION \
--tag-specifications "ResourceType=security-group,Tags=[
{Key=Name,Value=prod-vpce-sg},
{Key=Environment,Value=production}
]" \
--query 'GroupId' \
--output text)
echo " VPCE SG: $VPCE_SG_ID"
echo "export VPCE_SG_ID=$VPCE_SG_ID" >> sg-config.sh
# VPCE 入站规则
aws ec2 authorize-security-group-ingress \
--group-id $VPCE_SG_ID \
--ip-permissions \
IpProtocol=tcp,FromPort=443,ToPort=443,UserIdGroupPairs="[{GroupId=$EKS_NODES_SG_ID,Description='From EKS nodes'}]" \
IpProtocol=tcp,FromPort=443,ToPort=443,UserIdGroupPairs="[{GroupId=$BASTION_SG_ID,Description='From Bastion'}]" \
--region $REGION
# 移除 VPCE 默认出站规则
aws ec2 revoke-security-group-egress \
--group-id $VPCE_SG_ID \
--protocol -1 \
--cidr 0.0.0.0/0 \
--region $REGION
# 配置 Bastion 出站规则
echo ""
echo "配置 Bastion 出站规则..."
aws ec2 authorize-security-group-egress \
--group-id $BASTION_SG_ID \
--ip-permissions \
IpProtocol=tcp,FromPort=22,ToPort=22,UserIdGroupPairs="[{GroupId=$EKS_NODES_SG_ID}]" \
IpProtocol=tcp,FromPort=5432,ToPort=5432,UserIdGroupPairs="[{GroupId=$RDS_SG_ID}]" \
IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges='[{CidrIp=0.0.0.0/0}]' \
--region $REGION
# 移除 Bastion 默认出站规则
aws ec2 revoke-security-group-egress \
--group-id $BASTION_SG_ID \
--protocol -1 \
--cidr 0.0.0.0/0 \
--region $REGION 2>/dev/null || true
# 输出摘要
echo ""
echo "================================================"
echo "所有安全组创建完成!"
echo "================================================"
echo ""
echo "安全组 ID:"
echo " ALB: $ALB_SG_ID"
echo " EKS Control Plane: $EKS_CP_SG_ID"
echo " EKS Nodes: $EKS_NODES_SG_ID"
echo " RDS PostgreSQL: $RDS_SG_ID"
echo " Redis: $REDIS_SG_ID"
echo " VPC Endpoint: $VPCE_SG_ID"
echo " Bastion: $BASTION_SG_ID"
echo ""
echo "配置已保存到 sg-config.sh"
echo ""
echo "⚠️ 重要提醒:"
echo "1. 请将 OFFICE_IP 替换为实际办公室 IP 地址"
echo "2. 定期审计安全组规则"
echo "3. 遵循最小权限原则"
echo "================================================"
安全组验证
验证脚本
#!/bin/bash
# verify-security-groups.sh
# 验证安全组配置
source sg-config.sh
echo "================================================"
echo "安全组配置验证"
echo "================================================"
# 1. 检查所有安全组是否存在
echo ""
echo "1. 检查安全组存在性..."
SG_IDS=("$ALB_SG_ID" "$EKS_CP_SG_ID" "$EKS_NODES_SG_ID" "$RDS_SG_ID" "$REDIS_SG_ID" "$VPCE_SG_ID" "$BASTION_SG_ID")
SG_NAMES=("ALB" "EKS_CP" "EKS_Nodes" "RDS" "Redis" "VPCE" "Bastion")
for i in "${!SG_IDS[@]}"; do
SG_ID="${SG_IDS[$i]}"
SG_NAME="${SG_NAMES[$i]}"
STATUS=$(aws ec2 describe-security-groups \
--group-ids $SG_ID \
--query 'SecurityGroups[0].GroupId' \
--output text 2>/dev/null)
if [ "$STATUS" == "$SG_ID" ]; then
echo " ✓ $SG_NAME: $SG_ID"
else
echo " ✗ $SG_NAME: 不存在或无权限"
fi
done
# 2. 验证点对点引用
echo ""
echo "2. 验证点对点安全组引用..."
# 检查 RDS 是否允许来自 EKS Nodes
RDS_RULES=$(aws ec2 describe-security-groups \
--group-ids $RDS_SG_ID \
--query "SecurityGroups[0].IpPermissions[?contains(UserIdGroupPairs[].GroupId, '$EKS_NODES_SG_ID')]" \
--output json)
if [ "$RDS_RULES" != "[]" ]; then
echo " ✓ RDS 允许来自 EKS Nodes"
else
echo " ✗ RDS 未配置 EKS Nodes 访问"
fi
# 检查 Redis 是否允许来自 EKS Nodes
REDIS_RULES=$(aws ec2 describe-security-groups \
--group-ids $REDIS_SG_ID \
--query "SecurityGroups[0].IpPermissions[?contains(UserIdGroupPairs[].GroupId, '$EKS_NODES_SG_ID')]" \
--output json)
if [ "$REDIS_RULES" != "[]" ]; then
echo " ✓ Redis 允许来自 EKS Nodes"
else
echo " ✗ Redis 未配置 EKS Nodes 访问"
fi
# 3. 检查过度开放的规则
echo ""
echo "3. 检查安全风险..."
# 检查是否有 0.0.0.0/0 的入站规则(除了 ALB)
for i in "${!SG_IDS[@]}"; do
SG_ID="${SG_IDS[$i]}"
SG_NAME="${SG_NAMES[$i]}"
if [ "$SG_NAME" != "ALB" ]; then
OPEN_RULES=$(aws ec2 describe-security-groups \
--group-ids $SG_ID \
--query "SecurityGroups[0].IpPermissions[?contains(IpRanges[].CidrIp, '0.0.0.0/0')]" \
--output json)
if [ "$OPEN_RULES" != "[]" ]; then
echo " ⚠️ $SG_NAME 有对互联网开放的规则"
fi
fi
done
# 4. 生成访问关系图
echo ""
echo "4. 访问关系图:"
echo ""
echo " 互联网"
echo " ↓ (443, 80)"
echo " ALB ($ALB_SG_ID)"
echo " ↓ (30000-32767)"
echo " EKS Nodes ($EKS_NODES_SG_ID)"
echo " ↓ (5432) ↓ (6379)"
echo " RDS ($RDS_SG_ID) Redis ($REDIS_SG_ID)"
echo ""
echo " 办公室 IP"
echo " ↓ (22)"
echo " Bastion ($BASTION_SG_ID)"
echo " ↓ (SSH, psql)"
echo " 节点/数据库"
echo ""
echo "================================================"
echo "验证完成"
echo "================================================"
最佳实践总结
1. 使用安全组引用而非 IP
✓ 逻辑清晰
✓ 易于维护
✓ 自动更新
✓ 避免错误
2. 最小权限原则
✓ 仅开放必需的端口
✓ 使用最窄的源范围
✓ 定期审计和清理
✓ 移除默认出站规则
3. 分层防御
✓ 网络层:安全组 + NACL
✓ 应用层:认证 + 授权
✓ 数据层:加密 + 访问控制
✓ 审计层:日志 + 监控
4. 文档化和审计
✓ 清晰的命名规范
✓ 详细的规则说明
✓ 定期安全审计
✓ 变更记录和审批
5. 自动化管理
✓ 使用脚本创建
✓ 版本控制
✓ CI/CD 集成
✓ 自动化测试
下一步: 继续学习 EKS 集群创建和配置 章节。