

# 使用 CloudFormation 管理安全组
<a name="quickref-ec2-sg"></a>

以下代码段演示了如何使用 CloudFormation 管理安全组和 Amazon EC2 实例，从而控制对 AWS 资源的访问权限。

**Topics**
+ [将 Amazon EC2 实例与安全组关联](#quickref-ec2-instances-associate-security-group)
+ [使用入口规则创建安全组](#quickref-ec2-instances-ingress)
+ [使用安全组入口规则创建弹性负载均衡器](#scenario-ec2-security-group-elbingress)

## 将 Amazon EC2 实例与安全组关联
<a name="quickref-ec2-instances-associate-security-group"></a>

以下示例代码段演示了如何使用 CloudFormation 将 Amazon EC2 实例与默认的 Amazon VPC 安全组关联起来。

**Topics**
+ [将 Amazon EC2 实例与默认 VPC 安全组关联](#using-cfn-getatt-default-values)
+ [创建带有附加卷和安全组的 Amazon EC2 实例](#scenario-ec2-volumeattachment)

### 将 Amazon EC2 实例与默认 VPC 安全组关联
<a name="using-cfn-getatt-default-values"></a>

以下代码段创建了一个 Amazon VPC、一个 VPC 内的子网和一个 Amazon EC2 实例。VPC 是使用 [AWS::EC2::VPC](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-vpc.html) 资源创建的。VPC 的 IP 地址范围在较大的模板中定义并由 `MyVPCCIDRRange` 参数引用。

子网是使用 [AWS::EC2:: Subnet](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-subnet.html) 资源在 VPC 中创建的。子网与 VPC 关联，后者被引用为 `MyVPC`。

使用 [AWS::EC2::Instance](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-instance.html) 资源在 VPC 和子网内启动 EC2 实例。此资源指定用于启动实例的亚马逊机器映像（AMI）、实例将在其中运行的子网以及要与该实例关联的安全组。`ImageId` 使用 Systems Manager 参数动态检索最新的 Amazon Linux 2 AMI。

安全组 ID 是使用 `Fn::GetAtt` 函数获取的，该函数从 `MyVPC` 资源中检索默认安全组。

该实例位于代码段中定义的 `MySubnet` 资源中。

使用 CloudFormation 创建 VPC 时，AWS 会在 VPC 中自动创建默认资源，包括默认安全组。但是，当您在 CloudFormation 模板中定义 VPC 时，可能无法在创建模板时访问这些默认资源的 ID。要访问和使用模板中指定的默认资源，您可以使用内置函数，例如 `Fn::GetAtt`。此函数允许您使用由 CloudFormation 自动创建的默认资源。

#### JSON
<a name="quickref-ec2-example-15.json"></a>

```
"MyVPC": {
    "Type": "AWS::EC2::VPC",
    "Properties": {
        "CidrBlock": {
            "Ref": "MyVPCCIDRRange"
        },
        "EnableDnsSupport": false,
        "EnableDnsHostnames": false,
        "InstanceTenancy": "default"
    }
},
"MySubnet": {
    "Type": "AWS::EC2::Subnet",
    "Properties": {
        "CidrBlock": {
            "Ref": "MyVPCCIDRRange"
        },
        "VpcId": {
            "Ref": "MyVPC"
        }
    }
},
"MyInstance": {
    "Type": "AWS::EC2::Instance",
    "Properties": {
        "ImageId": "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}",
        "SecurityGroupIds": [
            {
                "Fn::GetAtt": [
                    "MyVPC",
                    "DefaultSecurityGroup"
                ]
            }
        ],
        "SubnetId": {
            "Ref": "MySubnet"
        }
    }
}
```

#### YAML
<a name="quickref-ec2-example-15.yaml"></a>

```
MyVPC:
  Type: AWS::EC2::VPC
  Properties:
    CidrBlock:
      Ref: MyVPCCIDRRange
    EnableDnsSupport: false
    EnableDnsHostnames: false
    InstanceTenancy: default
MySubnet:
  Type: AWS::EC2::Subnet
  Properties:
    CidrBlock:
      Ref: MyVPCCIDRRange
    VpcId:
      Ref: MyVPC
MyInstance:
  Type: AWS::EC2::Instance
  Properties:
    ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}'
    SecurityGroupIds:
      - Fn::GetAtt:
          - MyVPC
          - DefaultSecurityGroup
    SubnetId:
      Ref: MySubnet
```

### 创建带有附加卷和安全组的 Amazon EC2 实例
<a name="scenario-ec2-volumeattachment"></a>

以下代码段使用从指定 AMI 启动的 [AWS::EC2::Instance](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-instance.html) 资源创建 Amazon EC2 实例。该实例与一个安全组关联，该安全组使用 [AWS::EC2::SecurityGroup](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroup.html) 资源允许来自指定 IP 地址端口 22 上的传入 SSH 流量。它使用 [AWS::EC2::Volume](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-volume.html) 资源创建一个 100GB 的 Amazon EBS 卷。根据 `GetAtt` 函数的指定，这个卷在与实例相同的可用区中创建，并安装到 `/dev/sdh` 设备上的实例。

有关创建 Amazon EBS 卷的更多信息，请参阅[创建 Amazon EBS 卷](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-creating-volume.html)。

#### JSON
<a name="quickref-ec2-example-14.json"></a>

```
 1. "Ec2Instance": {
 2.     "Type": "AWS::EC2::Instance",
 3.     "Properties": {
 4.         "SecurityGroups": [
 5.             {
 6.                 "Ref": "InstanceSecurityGroup"
 7.             }
 8.         ],
 9.         "ImageId": "ami-1234567890abcdef0"
10.     }
11. },
12. "InstanceSecurityGroup": {
13.     "Type": "AWS::EC2::SecurityGroup",
14.     "Properties": {
15.         "GroupDescription": "Enable SSH access via port 22",
16.         "SecurityGroupIngress": [
17.             {
18.                 "IpProtocol": "tcp",
19.                 "FromPort": "22",
20.                 "ToPort": "22",
21.                 "CidrIp": "192.0.2.0/24"
22.             }
23.         ]
24.     }
25. },
26. "NewVolume": {
27.     "Type": "AWS::EC2::Volume",
28.     "Properties": {
29.         "Size": "100",
30.         "AvailabilityZone": {
31.             "Fn::GetAtt": [
32.                 "Ec2Instance",
33.                 "AvailabilityZone"
34.             ]
35.         }
36.     }
37. },
38. "MountPoint": {
39.     "Type": "AWS::EC2::VolumeAttachment",
40.     "Properties": {
41.         "InstanceId": {
42.             "Ref": "Ec2Instance"
43.         },
44.         "VolumeId": {
45.             "Ref": "NewVolume"
46.         },
47.         "Device": "/dev/sdh"
48.     }
49. }
```

#### YAML
<a name="quickref-ec2-example-14.yaml"></a>

```
 1. Ec2Instance:
 2.   Type: AWS::EC2::Instance
 3.   Properties:
 4.     SecurityGroups:
 5.       - !Ref InstanceSecurityGroup
 6.     ImageId: ami-1234567890abcdef0
 7. InstanceSecurityGroup:
 8.   Type: AWS::EC2::SecurityGroup
 9.   Properties:
10.     GroupDescription: Enable SSH access via port 22
11.     SecurityGroupIngress:
12.       - IpProtocol: tcp
13.         FromPort: 22
14.         ToPort: 22
15.         CidrIp: 192.0.2.0/24
16. NewVolume:
17.   Type: AWS::EC2::Volume
18.   Properties:
19.     Size: 100
20.     AvailabilityZone: !GetAtt [Ec2Instance, AvailabilityZone]
21. MountPoint:
22.   Type: AWS::EC2::VolumeAttachment
23.   Properties:
24.     InstanceId: !Ref Ec2Instance
25.     VolumeId: !Ref NewVolume
26.     Device: /dev/sdh
```

## 使用入口规则创建安全组
<a name="quickref-ec2-instances-ingress"></a>

以下示例代码段演示了如何使用 CloudFormation 配置具有特定入口规则的安全组。

**Topics**
+ [使用入口规则创建安全组以允许 SSH 和 HTTP 访问](#scenario-ec2-security-group-rule)
+ [使用入口规则创建安全组以允许来自指定 CIDR 范围的 HTTP 和 SSH 访问](#scenario-ec2-security-group-two-ports)
+ [使用入口规则创建交叉引用安全组](#scenario-ec2-security-group-ingress)

### 使用入口规则创建安全组以允许 SSH 和 HTTP 访问
<a name="scenario-ec2-security-group-rule"></a>

以下代码段描述了两个使用 [AWS::EC2::SecurityGroup](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroup.html) 资源的安全组入口规则。第一个入口规则允许从名为 `MyAdminSecurityGroup` 的现有安全组访问 SSH（端口 22），该安全组由账号为 `1111-2222-3333` 的 AWS 账户所有。第二个入口规则允许从名为 `MySecurityGroupCreatedInCFN` 的另一个安全组访问 HTTP（端口 80），该安全组是在同一个模板中创建的。`Ref` 函数用于引用在同一模板中创建的安全组的逻辑名称。

在第一个入口规则中，必须为 `SourceSecurityGroupName` 和 `SourceSecurityGroupOwnerId` 属性添加一个值。在第二个入口规则中，`MySecurityGroupCreatedInCFNTemplate` 引用了在同一个模板中创建的另一个安全组。验证逻辑名称 `MySecurityGroupCreatedInCFNTemplate` 是否与您在较大模板中指定的安全组资源的实际逻辑名称相匹配。

有关安全组的更多信息，请参阅《Amazon EC2 用户指南》**中的 [Amazon EC2 实例的 Amazon EC2 安全组](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)。

#### JSON
<a name="quickref-ec2-example-10.json"></a>

```
 1. "SecurityGroup": {
 2.     "Type": "AWS::EC2::SecurityGroup",
 3.     "Properties": {
 4.         "GroupDescription": "Allow connections from specified source security group",
 5.         "SecurityGroupIngress": [
 6.             {
 7.                 "IpProtocol": "tcp",
 8.                 "FromPort": "22",
 9.                 "ToPort": "22",
10.                 "SourceSecurityGroupName": "MyAdminSecurityGroup",
11.                 "SourceSecurityGroupOwnerId": "1111-2222-3333"
12.             },
13.             {
14.                 "IpProtocol": "tcp",
15.                 "FromPort": "80",
16.                 "ToPort": "80",
17.                 "SourceSecurityGroupName": {
18.                     "Ref": "MySecurityGroupCreatedInCFNTemplate"
19.                 }
20.             }
21.         ]
22.     }
23. }
```

#### YAML
<a name="quickref-ec2-example-10.yaml"></a>

```
 1. SecurityGroup:
 2.   Type: AWS::EC2::SecurityGroup
 3.   Properties:
 4.     GroupDescription: Allow connections from specified source security group
 5.     SecurityGroupIngress:
 6.       - IpProtocol: tcp
 7.         FromPort: '22'
 8.         ToPort: '22'
 9.         SourceSecurityGroupName: MyAdminSecurityGroup
10.         SourceSecurityGroupOwnerId: '1111-2222-3333'
11.       - IpProtocol: tcp
12.         FromPort: '80'
13.         ToPort: '80'
14.         SourceSecurityGroupName:
15.           Ref: MySecurityGroupCreatedInCFNTemplate
```

### 使用入口规则创建安全组以允许来自指定 CIDR 范围的 HTTP 和 SSH 访问
<a name="scenario-ec2-security-group-two-ports"></a>

以下代码段为具有两个入站规则的 Amazon EC2 实例创建了一个安全组。入站规则允许来自指定 CIDR 范围指定端口上的传入 TCP 流量。[AWS::EC2::SecurityGroup](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroup.html) 资源用于指定规则。您必须为每个规则指定一个协议。对于 TCP，您必须指定端口或端口范围。如果未指定源安全组或 CIDR 范围，堆栈虽会成功启动，但规则不会应用于安全组。

有关安全组的更多信息，请参阅《Amazon EC2 用户指南》**中的 [Amazon EC2 实例的 Amazon EC2 安全组](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)。

#### JSON
<a name="quickref-ec2-example-9.json"></a>

```
 1. "ServerSecurityGroup": {
 2.   "Type": "AWS::EC2::SecurityGroup",
 3.   "Properties": {
 4.     "GroupDescription": "Allow connections from specified CIDR ranges",
 5.     "SecurityGroupIngress": [
 6.       {
 7.         "IpProtocol": "tcp",
 8.         "FromPort": "80",
 9.         "ToPort": "80",
10.         "CidrIp": "192.0.2.0/24"
11.       },
12.       {
13.         "IpProtocol": "tcp",
14.         "FromPort": "22",
15.         "ToPort": "22",
16.         "CidrIp": "192.0.2.0/24"
17.       }
18.     ]
19.   }
20. }
```

#### YAML
<a name="quickref-ec2-example-9.yaml"></a>

```
 1. ServerSecurityGroup:
 2.   Type: AWS::EC2::SecurityGroup
 3.   Properties:
 4.     GroupDescription: Allow connections from specified CIDR ranges
 5.     SecurityGroupIngress:
 6.       - IpProtocol: tcp
 7.         FromPort: 80
 8.         ToPort: 80
 9.         CidrIp: 192.0.2.0/24
10.       - IpProtocol: tcp
11.         FromPort: 22
12.         ToPort: 22
13.         CidrIp: 192.0.2.0/24
```

### 使用入口规则创建交叉引用安全组
<a name="scenario-ec2-security-group-ingress"></a>

以下代码段使用 [AWS::EC2::SecurityGroup](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroup.html) 资源创建两个 Amazon EC2 安全组：`SGroup1` 和 `SGroup2`。允许在两个安全组之间通信的入口规则是使用 [AWS::EC2::SecurityGroupIngress](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroupingress.html) 资源创建的。`SGroup1Ingress` 为 `SGroup1` 建立了一个入口规则，允许来自源安全组 `SGroup2` 端口 80 上的传入 TCP 流量。`SGroup2Ingress` 为 `SGroup2` 建立了一个入口规则，允许来自源安全组 `SGroup1` 端口 80 上的传入 TCP 流量。

#### JSON
<a name="quickref-ec2-example-12.json"></a>

```
 1. "SGroup1": {
 2.     "Type": "AWS::EC2::SecurityGroup",
 3.     "Properties": {
 4.         "GroupDescription": "EC2 instance access"
 5.     }
 6. },
 7. "SGroup2": {
 8.     "Type": "AWS::EC2::SecurityGroup",
 9.     "Properties": {
10.         "GroupDescription": "EC2 instance access"
11.     }
12. },
13. "SGroup1Ingress": {
14.     "Type": "AWS::EC2::SecurityGroupIngress",
15.     "Properties": {
16.         "GroupName": {
17.             "Ref": "SGroup1"
18.         },
19.         "IpProtocol": "tcp",
20.         "ToPort": "80",
21.         "FromPort": "80",
22.         "SourceSecurityGroupName": {
23.             "Ref": "SGroup2"
24.         }
25.     }
26. },
27. "SGroup2Ingress": {
28.     "Type": "AWS::EC2::SecurityGroupIngress",
29.     "Properties": {
30.         "GroupName": {
31.             "Ref": "SGroup2"
32.         },
33.         "IpProtocol": "tcp",
34.         "ToPort": "80",
35.         "FromPort": "80",
36.         "SourceSecurityGroupName": {
37.             "Ref": "SGroup1"
38.         }
39.     }
40. }
```

#### YAML
<a name="quickref-ec2-example-12.yaml"></a>

```
 1. SGroup1:
 2.   Type: AWS::EC2::SecurityGroup
 3.   Properties:
 4.     GroupDescription: EC2 Instance access
 5. SGroup2:
 6.   Type: AWS::EC2::SecurityGroup
 7.   Properties:
 8.     GroupDescription: EC2 Instance access
 9. SGroup1Ingress:
10.   Type: AWS::EC2::SecurityGroupIngress
11.   Properties:
12.     GroupName: !Ref SGroup1
13.     IpProtocol: tcp
14.     ToPort: 80
15.     FromPort: 80
16.     SourceSecurityGroupName: !Ref SGroup2
17. SGroup2Ingress:
18.   Type: AWS::EC2::SecurityGroupIngress
19.   Properties:
20.     GroupName: !Ref SGroup2
21.     IpProtocol: tcp
22.     ToPort: 80
23.     FromPort: 80
24.     SourceSecurityGroupName: !Ref SGroup1
```

## 使用安全组入口规则创建弹性负载均衡器
<a name="scenario-ec2-security-group-elbingress"></a>

以下模板在指定的可用区中创建了 [AWS::ElasticLoadBalancing::LoadBalancer](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancing-loadbalancer.html) 资源。[AWS::ElasticLoadBalancing::LoadBalancer](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancing-loadbalancer.html) 资源配置为在端口 80 上侦听 HTTP 流量，并将请求定向到端口 80 上的实例。弹性负载均衡器负责对实例间传入的 HTTP 流量进行负载均衡。

 此外，此模板将生成一个与负载均衡器关联的 [AWS::EC2::SecurityGroup](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroup.html) 资源。此安全组是使用单个入口规则（描述为 `ELB ingress group`）创建的，该规则允许端口 80 上的传入 TCP 流量。此入口规则的来源是使用从负载均衡器资源检索属性的 `Fn::GetAtt` 函数定义的。`SourceSecurityGroupOwnerId` 使用 `Fn::GetAtt` 来获取负载均衡器源安全组的 `OwnerAlias`。`SourceSecurityGroupName` 使用 `Fn::Getatt` 来获取 ELB 源安全组的 `GroupName`。

此设置可确保 ELB 和实例之间的安全通信。

有关负载均衡的更多信息，请参阅 [Elastic Load Balancing 用户指南](https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/)。

### JSON
<a name="quickref-ec2-example-11.json"></a>

```
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Resources": {
        "MyELB": {
            "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
            "Properties": {
                "AvailabilityZones": [
                    "aa-example-1a"
                ],
                "Listeners": [
                    {
                        "LoadBalancerPort": "80",
                        "InstancePort": "80",
                        "Protocol": "HTTP"
                    }
                ]
            }
        },
        "MyELBIngressGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": "ELB ingress group",
                "SecurityGroupIngress": [
                    {
                        "IpProtocol": "tcp",
                        "FromPort": 80,
                        "ToPort": 80,
                        "SourceSecurityGroupOwnerId": {
                            "Fn::GetAtt": [
                                "MyELB",
                                "SourceSecurityGroup.OwnerAlias"
                            ]
                        },
                        "SourceSecurityGroupName": {
                            "Fn::GetAtt": [
                                "MyELB",
                                "SourceSecurityGroup.GroupName"
                            ]
                        }
                    }
                ]
            }
        }
    }
}
```

### YAML
<a name="quickref-ec2-example-11.yaml"></a>

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyELB:
    Type: AWS::ElasticLoadBalancing::LoadBalancer
    Properties:
      AvailabilityZones:
        - aa-example-1a
      Listeners:
        - LoadBalancerPort: '80'
          InstancePort: '80'
          Protocol: HTTP
  MyELBIngressGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: ELB ingress group
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          SourceSecurityGroupOwnerId:
            Fn::GetAtt:
              - MyELB
              - SourceSecurityGroup.OwnerAlias
          SourceSecurityGroupName:
            Fn::GetAtt:
              - MyELB
              - SourceSecurityGroup.GroupName
```