0%

《AWS 云计算实战》6. 保护系统安全:IAM、安全组和 VPC

保护系统安全

检查安全更新

1⃣️ 在服务器启动时安装安全更新:在用户数据脚本中添加安全更新命令:

# 检查安全更新
yum -security check-update

# 安装所有更新
yum -y update

# 仅安装安全更新
yum -y -security update

2⃣️ 在服务器运行时安装安全更新:使用一个脚本获取所有服务器列表,然后在所有运行的 EC2 实例上安装安全更新:

update.sh 在所有服务器上安装安全更新的脚本文件

#!/bin/bash -ex

PUBLICNAMES=$(aws ec2 describe-instances --filters "Name=instance-state-name,Values=running" --query "Reservations[].Instances[].PublicDnsName" --output text)

for PUBLICNAME in $PUBLICNAMES; do
    ssh -t -o StrictHostKeyChecking=no ec2-user@$PUBLICNAME "sudo yum -y --security update"
done

保护 AWS 账户安全

使用多重身份验证(MFA)来保护你的 root 用户。然后你将停止使用 root 用户,创建一个新用户用于日常操作,然后学会授予一个角色最小权限。

IAM 服务

root 用户、IAM 用户、IAM 角色的区别:

root 用户 IAM 用户 IAM 角色
可以有一个密码 总是
可以有一个访问密钥 是(不推荐)
可以属于一个组
可以与一个 EC2 实例关联
  • 密码:登录管理控制台;
  • 访问密钥:使用 AWS CLI 登录;

策略类型

IAM 用户和 IAM 角色通过策略进行授权。

最小授权原则。

创建用户组、IAM 用户、IAM 角色,并将 IAM 角色与 EC2 实例关联。

控制进出虚拟服务器的网络流量

  • 防火墙设置原则:只打开必要的端口。
  • 默认情况下,所有的入站流量都被拒绝,而所有的出站流量都被允许。

💡💡💡 源和目的地

入站安全组规则筛选基于网络流量的源。源可以是一个 IP 地址也可以是一个安全组。这样就可以只允许从特定源 IP 地址范围来的入站流量。
出站安全组规则筛选基于网路流量的目的地。目的地可以是一个 IP 地址或安全组。可以只允许特定目的 IP 地址范围的出站流量。

源 IP 和 目的 IP 针对的都是 EC2 实例外部的 IP 地址。

设置安全组规则

"Resources": {
    "SecurityGroup": { ## 安全组
        "Type": "AWS::EC2::SecurityGroup", ## 安全组资源类型
        "Properties": {  ## 安全组属性
            "GroupDescription": "My security group", ## 安全组描述
            "VpcId": {"Ref": "VPC"} ## 关联安全组与虚拟网络
        }
    },
    "AllowInboundICMP": { ## 允许 ICPM 规则描述
        "Type": "AWS::EC2::SecurityGroupIngress", ## 入站规则类型
        "Properties": { 
            "GroupId": {"Ref": "SecurityGroup"}, ## 关联入站规则与安全组
            "IpProtocol": "icmp", ## 指定协议
            "FromPort": "-1", ## -1,意味着所有端口
            "ToPort": "-1",
            "CidrIp": "0.0.0.0/0" ## 0.0.0.0/0 表示所有源IP地址都被允许
        }
    },
    "Server": {
        "Type": "AWS::EC2::Instance", ## EC2 实例描述
        "Properties": {
            "ImageId": {"Fn::FindInMap": ["EC2RegionMap", {"Ref": "AWS::Region"}, "AmazonLinuxAMIHVMEBSBacked64bit"]}, 
            "InstanceType": "t2.micro",
            "KeyName": {"Ref": "KeyName"},
            "SecurityGroupIds": [{"Ref": "SecurityGroup"}], ## 将安全组关联到 EC2 实例上
            "SubnetId": {"Ref": "Subnet"}
        }
    }

允许 ICMP 流量

"AllowInboundICMP": { ## 允许 ICPM 规则描述
    "Type": "AWS::EC2::SecurityGroupIngress", ## 入站规则类型
    "Properties": {
        "GroupId": {"Ref": "SecurityGroup"}, ## 关联入站规则与安全组
        "IpProtocol": "icmp", ## 指定协议:ICPMP 协议
        "FromPort": "-1", ## -1,意味着所有端口
        "ToPort": "-1",
        "CidrIp": "0.0.0.0/0" ## 0.0.0.0/0 表示所有源IP地址都被允许
    }
},

允许 SSH 流量

"IpForSSH": { ## 公有 IP 地址参数
    "Description": "Your public IP address to allow SSH access",
    "Type": "String"
}
...
"AllowInboundSSH": { ## 允许 SSH 规则描述
    "Type": "AWS::EC2::SecurityGroupIngress", ## 入站规则类型
    "Properties": {
        "GroupId": {"Ref": "SecurityGroup"}, ## 关联入站规则与安全组
        "IpProtocol": "tcp", ## SSH 基于 TCP 协议
        "FromPort": "22", ## 默认的 SSH 端口号:22
        "ToPort": "22",
        "CidrIp": {"Fn::Join": ["", [{"Ref": "IpForSSH"}, "/32"]]} ## 指定源IP
    }
},

查看你的公有 IP 地址:http://api.ipify.org

在云中创建一个私有网络:虚拟私有云(VPC)

公有子网 & 私有子网

💡

用户应该至少有两个子网,即公有子网和私有子网。公有子网能够路由到互联网,私有子网则不能。

公有子网:用户的网站服务器。

私有子网:用户的数据库服务器。

公有子网和私有子网到唯一区别是,私有子网不能路由到互联网网关( IGW)。


私有子网如何访问互联网?如何下载安装应用源码?

在公有子网中使用一个 NAT 服务器,并且创建一条从用户的私有子网到 NAT 服务器的路由。一台 NAT 服务器就是一台用来处理网络地址转换的虚拟服务器。来自用户的私有子网的互联网流量将从 NAT 服务器的公有 IP 地址访问互联网。

网络安全组 & 网络访问控制列表 ACL

安全组与网络 ACL 的比较

安全组与 ACL 有一个重要的区别:安全组是有状态的,而 ACL 则没有。

如果用户允许在安全组上的一个入站端口,那么该入站端口上的请求对应的出站响应也是被允许的。安全组规则将按用户所期望的方式工作。如果用户在安全组上打开端口 22,就能通过 SSH 连接。

ACL 则不同。如果用户仅仅为子网的 ACL 打开入站端口 22,仍然不能通过 SSH 访问。除此之外,用户还需要允许出站临时端口,因为 sshd(SSH 守护进程)在端口 22 上接受连接,但却使用临时端口与客户端通信。临时端口从范围 1024 至 65535 中选择。
如果用户想从自己的子网发起一个 SSH 连接,就需要打开出站端口 22 且同时打开入站临时端口。如果对这些都不熟悉,应该使用安全组且在 ACL 层允许所有。

AWS 示例

上图含义:来自 Internet 网关的数据流会通过路由表中的路径被路由到合适的子网。与子网相关的网络 ACL 规则控制允许进入子网的数据流。与实例相关的安全组规则控制允许进入实例的数据流。

互联网网关 IGW

IGW (Internet Gateway): 互联网网关,会使用网络地址转换(network address translation, NAT)把虚拟服务器的公有 IP 地址转换成它们的私有 IP 地址。

附:CIDR (无类域间路由)的概念

一个示例:计算 192.168.23.35/21 计算子网的网络 ID、子网掩码、起止 IP 地址?

CIDR 表示法:192.168.23.35/21

IP 地址: 11000000.10101000.00010111.00100011 (192.168.23.35)

子网掩码:11111111.11111111.11111000.00000000 (255.255.248.0)即 21 个 1

网络 ID: 11000000.10101000.00010000.00000000 (192.168.16.0)

起始 IP 地址: 11000000.10101000.00010000.00000001 (192.168.16.1)

结束 IP 地址:11000000.10101000.00010111.11111110 (192.168.23.254)

广播 IP 地址:11000000.10101000.00010111.11111111 (192.168.23.255)主机 ID 全 1。

附:有 4 个子网来保护网站应用安全的 VPC

有4个子网来保护网站应用安全的VPC

CloudFormation 模版文件示例:
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "AWS in Action: chapter 6 (VPC)",
    "Parameters": {
        "KeyName": {
            "Description": "Key Pair name",
            "Type": "AWS::EC2::KeyPair::KeyName",
            "Default": "mykey"
        }
    },
    "Mappings": {
        "EC2RegionMap": {
            "ap-northeast-1": {"AmazonLinuxAMIHVMEBSBacked64bit": "ami-cbf90ecb", "AmazonLinuxNATAMIHVMEBSBacked64bit": "ami-03cf3903"},
            "ap-southeast-1": {"AmazonLinuxAMIHVMEBSBacked64bit": "ami-68d8e93a", "AmazonLinuxNATAMIHVMEBSBacked64bit": "ami-b49dace6"},
            "ap-southeast-2": {"AmazonLinuxAMIHVMEBSBacked64bit": "ami-fd9cecc7", "AmazonLinuxNATAMIHVMEBSBacked64bit": "ami-e7ee9edd"},
            "eu-central-1": {"AmazonLinuxAMIHVMEBSBacked64bit": "ami-a8221fb5", "AmazonLinuxNATAMIHVMEBSBacked64bit": "ami-46073a5b"},
            "eu-west-1": {"AmazonLinuxAMIHVMEBSBacked64bit": "ami-a10897d6", "AmazonLinuxNATAMIHVMEBSBacked64bit": "ami-6975eb1e"},
            "sa-east-1": {"AmazonLinuxAMIHVMEBSBacked64bit": "ami-b52890a8", "AmazonLinuxNATAMIHVMEBSBacked64bit": "ami-fbfa41e6"},
            "us-east-1": {"AmazonLinuxAMIHVMEBSBacked64bit": "ami-1ecae776", "AmazonLinuxNATAMIHVMEBSBacked64bit": "ami-303b1458"},
            "us-west-1": {"AmazonLinuxAMIHVMEBSBacked64bit": "ami-d114f295", "AmazonLinuxNATAMIHVMEBSBacked64bit": "ami-7da94839"},
            "us-west-2": {"AmazonLinuxAMIHVMEBSBacked64bit": "ami-e7527ed7", "AmazonLinuxNATAMIHVMEBSBacked64bit": "ami-69ae8259"}
        }
    },
    "Resources": {
        "SecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": "My security group",
                "VpcId": {"Ref": "VPC"}
            }
        },
        "SecurityGroupIngress": {
            "Type": "AWS::EC2::SecurityGroupIngress",
            "Properties":{
                "IpProtocol": "-1",
                "FromPort": "-1",
                "ToPort": "-1",
                "CidrIp": "0.0.0.0/0",
                "GroupId": {"Ref": "SecurityGroup"}
            }
        },
        "SecurityGroupEgress": {
            "Type": "AWS::EC2::SecurityGroupEgress",
            "Properties":{
                "IpProtocol": "-1",
                "FromPort": "-1",
                "ToPort": "-1",
                "CidrIp": "0.0.0.0/0",
                "GroupId": {"Ref": "SecurityGroup"}
            }
        },
        "VPC": { ## 1.虚拟网络 VPC
            "Type": "AWS::EC2::VPC",
            "Properties": {
                "CidrBlock": "10.0.0.0/16", ## 地址空间
                "EnableDnsHostnames": "true"
            }
        },
        "InternetGateway": {  ## 互联网网关 IGW
            "Type": "AWS::EC2::InternetGateway",
            "Properties": {
            }
        },
        "VPCGatewayAttachment": { ## 将网关关联到VPC
            "Type": "AWS::EC2::VPCGatewayAttachment",
            "Properties": {
                "VpcId": {"Ref": "VPC"},
                "InternetGatewayId": {"Ref": "InternetGateway"}
            }
        },
        "SubnetPublicNAT": { ## 1.1 公有 NAT 子网
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "AvailabilityZone": {"Fn::Select": ["0", {"Fn::GetAZs": ""}]},
                "CidrBlock": "10.0.0.0/24", ## NAT 子网的地址空间
                "VpcId": {"Ref": "VPC"}
            }
        },
        "RouteTablePublicNAT": {
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {"Ref": "VPC"}
            }
        },
        "RouteTableAssociationPublicNAT": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {"Ref": "SubnetPublicNAT"},
                "RouteTableId": {"Ref": "RouteTablePublicNAT"}
            }
        },
        "RoutePublicNATToInternet": { ## NAT 子网是公有的能路由到互联网
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {"Ref": "RouteTablePublicNAT"},
                "DestinationCidrBlock": "0.0.0.0/0",
                "GatewayId": {"Ref": "InternetGateway"}
            },
            "DependsOn": "VPCGatewayAttachment"
        },
        "NetworkAclPublicNAT": {
            "Type": "AWS::EC2::NetworkAcl",
            "Properties": {
                "VpcId": {"Ref": "VPC"}
            }
        },
        "SubnetNetworkAclAssociationPublicNAT": {
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": {"Ref": "SubnetPublicNAT"},
                "NetworkAclId": {"Ref": "NetworkAclPublicNAT"}
            }
        },
        "NetworkAclEntryInPublicNATHTTP": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicNAT"},
                "RuleNumber": "100",
                "Protocol": "6",
                "PortRange": {
                    "From": "80",
                    "To": "80"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "10.0.0.0/16"
            }
        },
        "NetworkAclEntryInPublicNATHTTPS": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicNAT"},
                "RuleNumber": "110",
                "Protocol": "6",
                "PortRange": {
                    "From": "443",
                    "To": "443"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "10.0.0.0/16"
            }
        },
        "NetworkAclEntryInPublicNATEphemeralPorts": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicNAT"},
                "RuleNumber": "200",
                "Protocol": "6",
                "PortRange": {
                    "From": "1024",
                    "To": "65535"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryOutPublicNATHTTP": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicNAT"},
                "RuleNumber": "100",
                "Protocol": "6",
                "PortRange": {
                    "From": "80",
                    "To": "80"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryOutPublicNATHTTPS": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicNAT"},
                "RuleNumber": "110",
                "Protocol": "6",
                "PortRange": {
                    "From": "443",
                    "To": "443"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryOutPublicNATEphemeralPorts": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicNAT"},
                "RuleNumber": "200",
                "Protocol": "6",
                "PortRange": {
                    "From": "1024",
                    "To": "65535"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "SubnetPublicSSHBastion": { ## 1.2 公有堡垒主机子网
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "AvailabilityZone": {"Fn::Select": ["0", {"Fn::GetAZs": ""}]},
                "CidrBlock": "10.0.1.0/24", ## 公有子网地址空间
                "VpcId": {"Ref": "VPC"}
            }
        },
        "RouteTablePublicSSHBastion": { ## 路由表
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {"Ref": "VPC"}
            }
        },
        "RouteTableAssociationPublicSSHBastion": { ## 将路由表关联到子网
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {"Ref": "SubnetPublicSSHBastion"},
                "RouteTableId": {"Ref": "RouteTablePublicSSHBastion"}
            }
        },
        "RoutePublicSSHBastionToInternet": {
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {"Ref": "RouteTablePublicSSHBastion"},
                "DestinationCidrBlock": "0.0.0.0/0", ## 将任何地址路由至 IGW
                "GatewayId": {"Ref": "InternetGateway"}
            },
            "DependsOn": "VPCGatewayAttachment"
        },
        "NetworkAclPublicSSHBastion": { ## 公有子网访问控制列表 ACL
            "Type": "AWS::EC2::NetworkAcl",
            "Properties": {
                "VpcId": {"Ref": "VPC"}
            }
        },
        "SubnetNetworkAclAssociationPublicSSHBastion": { ## 关联
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": {"Ref": "SubnetPublicSSHBastion"},
                "NetworkAclId": {"Ref": "NetworkAclPublicSSHBastion"}
            }
        },
        "NetworkAclEntryInPublicSSHBastionSSH": { ## 允许来自任何地方的入站 SSH 流量
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicSSHBastion"},
                "RuleNumber": "100",
                "Protocol": "6",
                "PortRange": {
                    "From": "22",
                    "To": "22"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryInPublicSSHBastionEphemeralPorts": { ## 用于短 TCP/IP 连接的入站临时端口
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicSSHBastion"},
                "RuleNumber": "200",
                "Protocol": "6",
                "PortRange": {
                    "From": "1024",
                    "To": "65535"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "10.0.0.0/16"
            }
        },
        "NetworkAclEntryOutPublicSSHBastionSSH": { ## 允许从 VPC 出站的 SSH 流量
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicSSHBastion"},
                "RuleNumber": "100",
                "Protocol": "6",
                "PortRange": {
                    "From": "22",
                    "To": "22"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "10.0.0.0/16"
            }
        },
        "NetworkAclEntryOutPublicSSHBastionEphemeralPorts": {  ## 出站临时端口,用于 SSH 出站连接
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicSSHBastion"},
                "RuleNumber": "200",
                "Protocol": "6",
                "PortRange": {
                    "From": "1024",
                    "To": "65535"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "SubnetPublicVarnish": {  ## 1.3 Varnish 网络缓存子网,公有子网
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "AvailabilityZone": {"Fn::Select": ["0", {"Fn::GetAZs": ""}]},
                "CidrBlock": "10.0.2.0/24", ## 地址空间
                "VpcId": {"Ref": "VPC"}
            }
        },
        "RouteTablePublicVarnish": {
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {"Ref": "VPC"}
            }
        },
        "RouteTableAssociationPublicVarnish": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {"Ref": "SubnetPublicVarnish"},
                "RouteTableId": {"Ref": "RouteTablePublicVarnish"}
            }
        },
        "RoutePublicVarnishToInternet": {
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {"Ref": "RouteTablePublicVarnish"},
                "DestinationCidrBlock": "0.0.0.0/0",
                "GatewayId": {"Ref": "InternetGateway"}
            },
            "DependsOn": "VPCGatewayAttachment"
        },
        "NetworkAclPublicVarnish": {
            "Type": "AWS::EC2::NetworkAcl",
            "Properties": {
                "VpcId": {"Ref": "VPC"}
            }
        },
        "SubnetNetworkAclAssociationPublicVarnish": {
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": {"Ref": "SubnetPublicVarnish"},
                "NetworkAclId": {"Ref": "NetworkAclPublicVarnish"}
            }
        },
        "NetworkAclEntryInPublicVarnishSSH": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicVarnish"},
                "RuleNumber": "100",
                "Protocol": "6",
                "PortRange": {
                    "From": "22",
                    "To": "22"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "10.0.1.0/24"
            }
        },
        "NetworkAclEntryInPublicVarnishHTTP": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicVarnish"},
                "RuleNumber": "110",
                "Protocol": "6",
                "PortRange": {
                    "From": "80",
                    "To": "80"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryInPublicVarnishEphemeralPorts": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicVarnish"},
                "RuleNumber": "200",
                "Protocol": "6",
                "PortRange": {
                    "From": "1024",
                    "To": "65535"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryOutPublicVarnishHTTP": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicVarnish"},
                "RuleNumber": "100",
                "Protocol": "6",
                "PortRange": {
                    "From": "80",
                    "To": "80"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryOutPublicVarnishHTTPS": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicVarnish"},
                "RuleNumber": "110",
                "Protocol": "6",
                "PortRange": {
                    "From": "443",
                    "To": "443"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryOutPublicVarnishEphemeralPorts": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPublicVarnish"},
                "RuleNumber": "200",
                "Protocol": "6",
                "PortRange": {
                    "From": "1024",
                    "To": "65535"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "SubnetPrivateApache": { ## 1.4 Apache 网站服务器私有子网
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "AvailabilityZone": {"Fn::Select": ["0", {"Fn::GetAZs": ""}]},
                "CidrBlock": "10.0.3.0/24", ## 私有子网地址空间
                "VpcId": {"Ref": "VPC"}
            }
        },
        # 公有子网和私有子网到唯一区别是,私有子网不能路由到互联网网关( IGW)。
        "RouteTablePrivateApache": { ## Apache 私有子网路由表。没有路由到 IGW。
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {"Ref": "VPC"}
            }
        },
        "RouteTableAssociationPrivateApache": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {"Ref": "SubnetPrivateApache"},
                "RouteTableId": {"Ref": "RouteTablePrivateApache"}
            }
        },
        "RoutePrivateApacheToInternet": {
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {"Ref": "RouteTablePrivateApache"},
                "DestinationCidrBlock": "0.0.0.0/0", 
                "InstanceId": {"Ref": "NatServer"} ## 从 Apache 子网路由到 NAT 实例
            }
        },
        "NetworkAclPrivateApache": { ## Apache 私有子网 ACL 
            "Type": "AWS::EC2::NetworkAcl",
            "Properties": {
                "VpcId": {"Ref": "VPC"}
            }
        },
        "SubnetNetworkAclAssociationPrivateApache": {
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": {"Ref": "SubnetPrivateApache"},
                "NetworkAclId": {"Ref": "NetworkAclPrivateApache"}
            }
        },
        "NetworkAclEntryInPrivateApacheSSH": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPrivateApache"},
                "RuleNumber": "100",
                "Protocol": "6",
                "PortRange": {
                    "From": "22",
                    "To": "22"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "10.0.1.0/24"
            }
        },
        "NetworkAclEntryInPrivateApacheHTTP": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPrivateApache"},
                "RuleNumber": "110",
                "Protocol": "6",
                "PortRange": {
                    "From": "80",
                    "To": "80"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "10.0.2.0/24"
            }
        },
        "NetworkAclEntryInPrivateApacheEphemeralPorts": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPrivateApache"},
                "RuleNumber": "200",
                "Protocol": "6",
                "PortRange": {
                    "From": "1024",
                    "To": "65535"
                },
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryOutPrivateApacheHTTP": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPrivateApache"},
                "RuleNumber": "100",
                "Protocol": "6",
                "PortRange": {
                    "From": "80",
                    "To": "80"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryOutPrivateApacheHTTPS": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPrivateApache"},
                "RuleNumber": "110",
                "Protocol": "6",
                "PortRange": {
                    "From": "443",
                    "To": "443"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0"
            }
        },
        "NetworkAclEntryOutPrivateApacheEphemeralPorts": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": {"Ref": "NetworkAclPrivateApache"},
                "RuleNumber": "200",
                "Protocol": "6",
                "PortRange": {
                    "From": "1024",
                    "To": "65535"
                },
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "10.0.0.0/16"
            }
        },
        "NatServer": { ## NAT 服务器,私有子网的互联网流量将从 NAT 服务器的公有 IP 地址访问互联网。
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "ImageId": {"Fn::FindInMap": ["EC2RegionMap", {"Ref": "AWS::Region"}, "AmazonLinuxNATAMIHVMEBSBacked64bit"]}, ## AWS 提供了配置好的 NAT 实例映像
                "InstanceType": "t2.micro",
                "KeyName": {"Ref": "KeyName"},
                "NetworkInterfaces": [{
                    "AssociatePublicIpAddress": "true", ## 分配公有 IP 地址,所有私有子网到互联网流量的源地址
                    "DeleteOnTermination": "true",
                    "SubnetId": {"Ref": "SubnetPublicNAT"},
                    "DeviceIndex": "0",
                    "GroupSet": [{"Ref": "SecurityGroup"}]
                }],
                "SourceDestCheck": "false", ## 默认情况下,一个实例必须是它发送的网络流量的源或者目的地。 对 NAT 实例禁用此检查。
                "UserData": {"Fn::Base64": {"Fn::Join": ["", [
                    "#!/bin/bash -ex\n",
                    "/opt/aws/bin/cfn-signal --stack ", {"Ref": "AWS::StackName"}, " --resource NatServer --region ", {"Ref": "AWS::Region"}, "\n"
                ]]}}
            },
            "CreationPolicy": {
                "ResourceSignal": {
                    "Timeout": "PT5M"
                }
            },
            "DependsOn": "VPCGatewayAttachment"
        },
        "BastionHost": { ## 堡垒主机
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "ImageId": {"Fn::FindInMap": ["EC2RegionMap", {"Ref": "AWS::Region"}, "AmazonLinuxAMIHVMEBSBacked64bit"]},
                "InstanceType": "t2.micro",
                "KeyName": {"Ref": "KeyName"},
                "NetworkInterfaces": [{
                    "AssociatePublicIpAddress": "true", ## 分配一个公有 IP 地址
                    "DeleteOnTermination": "true",
                    "SubnetId": {"Ref": "SubnetPublicSSHBastion"}, ## 在堡垒主机子网中启动
                    "DeviceIndex": "0",
                    "GroupSet": [{"Ref": "SecurityGroup"}] ## 安全组允许所有流量
                }]
            },
            "DependsOn": "VPCGatewayAttachment"
        },
        "VarnishServer": { ## Varnish 服务器
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "ImageId": {"Fn::FindInMap": ["EC2RegionMap", {"Ref": "AWS::Region"}, "AmazonLinuxAMIHVMEBSBacked64bit"]},
                "InstanceType": "t2.micro",
                "KeyName": {"Ref": "KeyName"},
                "NetworkInterfaces": [{
                    "AssociatePublicIpAddress": "true",
                    "DeleteOnTermination": "true",
                    "SubnetId": {"Ref": "SubnetPublicVarnish"},
                    "DeviceIndex": "0",
                    "GroupSet": [{"Ref": "SecurityGroup"}]
                }],
                "UserData": {"Fn::Base64": {"Fn::Join": ["", [
                    "#!/bin/bash -ex\n",
                    "yum -y install varnish-3.0.7\n",
                    "cat > /etc/varnish/default.vcl << EOF\n",
                    "backend default {\n",
                    "  .host = \"", {"Fn::GetAtt": ["ApacheServer", "PrivateIp"]} ,"\";\n",
                    "  .port = \"80\";\n",
                    "}\n",
                    "EOF\n",
                    "sed -i.bak \"s/^VARNISH_LISTEN_PORT=.*/VARNISH_LISTEN_PORT=80/\" /etc/sysconfig/varnish\n",
                    "service varnish start\n",
                    "/opt/aws/bin/cfn-signal --stack ", {"Ref": "AWS::StackName"}, " --resource VarnishServer --region ", {"Ref": "AWS::Region"}, "\n"
                ]]}}
            },
            "CreationPolicy": {
                "ResourceSignal": {
                    "Timeout": "PT5M"
                }
            },
            "DependsOn": "VPCGatewayAttachment"
        },
        "ApacheServer": { ## Apache 服务器
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "ImageId": {"Fn::FindInMap": ["EC2RegionMap", {"Ref": "AWS::Region"}, "AmazonLinuxAMIHVMEBSBacked64bit"]},
                "InstanceType": "t2.micro",
                "KeyName": {"Ref": "KeyName"},
                "NetworkInterfaces": [{
                    "AssociatePublicIpAddress": "false", ## 不分配公有 IP 地址。
                    "DeleteOnTermination": "true",
                    "SubnetId": {"Ref": "SubnetPrivateApache"}, ## 在私有子网中启动
                    "DeviceIndex": "0",
                    "GroupSet": [{"Ref": "SecurityGroup"}]
                }],
                "UserData": {"Fn::Base64": {"Fn::Join": ["", [
                    "#!/bin/bash -ex\n",
                    "yum -y install httpd\n", ## 从互联网安装 Apache
                    "service httpd start\n",
                    "/opt/aws/bin/cfn-signal --stack ", {"Ref": "AWS::StackName"}, " --resource ApacheServer --region ", {"Ref": "AWS::Region"}, "\n"
                ]]}}
            },
            "CreationPolicy": {
                "ResourceSignal": {
                    "Timeout": "PT10M"
                }
            },
            "DependsOn": "NatServer"
        }
    },
    "Outputs": {
        "BastionHostPublicName": {
            "Value": {"Fn::GetAtt": ["BastionHost", "PublicDnsName"]},
            "Description": "connect via SSH as user ec2-user"
        },
        "VarnishServerPublicName": {
            "Value": {"Fn::GetAtt": ["VarnishServer", "PublicDnsName"]},
            "Description": "handles HTTP requests"
        },
        "VarnishServerPrivateIp": {
            "Value": {"Fn::GetAtt": ["VarnishServer", "PrivateIp"]},
            "Description": "connect via SSH from bastion host"
        },
        "ApacheServerPrivateIp": {
            "Value": {"Fn::GetAtt": ["ApacheServer", "PrivateIp"]},
            "Description": "connect via SSH from bastion host"
        }
    }
}

欢迎关注我的其它发布渠道