安全组和访问控制

点对点安全组策略

为什么使用安全组引用

传统方式(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 集群创建和配置 章节。