# Following example shows how to create AWS Gateway Load Balancer (GWLB) # Inspection VPC in distributed model using AWS CloudFormation. # Template uses Amazon Linux 2 instances as appliances behind the GWLB. # Configures iptables on instances for hairpin setup. The hairpin setup # allows traffic coming from GWLB on appliance to be sent back to GWLB. # Template when deployed creates: # - 1 VPC # - 1 IGW # - 2 NGWs # - 2 Public subnets in unique availability zone # - 2 Private subnets in unique availability zone # - 1 Public route table # - 2 public subnets associated with this route table # - Next hope for 0.0.0.0/0 set to IGW # - 2 Private route tables # - private subnets associated with these route tables # - Next hope for 0.0.0.0/0 set to respective NGWs # - 1 bastion security group # - port 22, port 80 and port 443 access # - All TCP, UDP and ICMP from VPC CIDR # - 1 appliance security group # - port 22, port 80, port 443 and ICMP access from bastion security group # - All TCP, UDP and ICMP from VPC CIDR # - Conditionally creates 1 bastion host in public subnet # - 2 Amazon Linux 2 instances acting as appliance in private subnet. # Registered as targets behind a GWLB. Configures iptables on # instances for hairpin setup. # - 1 GWLB # - 1 Target group for GWLB # - 1 Listner for GWLB # - 1 VPC endpoint service # # iptables configuration is for sample purpose only. It allows all the traffic! Use it for GWLB POC only** AWSTemplateFormatVersion: "2010-09-09" Description: >- AWS CloudFormation sample template For Inspection VPC setup for Gateway Load Balancer (GWLB) across 2 Availability Zones (AZ) in distributed architecture. Template creates Amazon Linux 2 EC2 instances acting as virtual appliance. EC2 intances are added as targets behind GWLB. EC2 instances, using iptables, are configured in a hairpin mode to send traffic back to GWLB. **WARNING** This template creates NAT gateways, one or more Amazon EC2 instances, GWLB and interface endpoitns. You will be billed for the AWS resources used if you create a stack from this template. Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Network Configuration Parameters: - VpcCidr - AvailabilityZone1 - PublicSubnet1Cidr - ApplianceSubnet1Cidr - AvailabilityZone2 - PublicSubnet2Cidr - ApplianceSubnet2Cidr - Label: default: Appliance Configuration Parameters: - ApplianceInstanceType - ApplianceAmiId - ApplianceInstanceDiskSize - KeyPairName - AccessLocation - Label: default: Gateway Load Balancer Configuration Parameters: - GwlbName - TargetGroupName - HealthPort - HealthProtocol - Label: default: VPC Endpoint Service Configuration Parameters: - ConnectionAcceptance - AwsAccountToWhitelist - Label: Bastion Host Condition - CreateBastionHost ParameterLabels: VpcCidr: default: Network CIDR block for new VPC AvailabilityZone1: default: Availability Zone 1 PublicSubnet1Cidr: default: Network CIDR for Public Subnet 1 ApplianceSubnet1Cidr: default: Network CIDR for Appliance Subnet 1 AvailabilityZone2: default: Availability Zone 2 PublicSubnet2Cidr: default: Network CIDR for Public Subnet 2 ApplianceSubnet2Cidr: default: Network CIDR for Appliance Subnet 2 ApplianceInstanceType: default: Appliance Instance Type ApplianceAmiId: default: Latest AMI ID for appliance (ec2 instance) ApplianceInstanceDiskSize: default: Appliance Instance Size in GB KeyPairName: default: KeyPair required for accessing Appliance instance AccessLocation: default: Network CIDR to access Appliance instance GwlbName: default: Gateway Load Balancer Name TargetGroupName: default: Target Group Name HealthPort: default: Health Check Port HealthProtocol: default: Health Check Protocol ConnectionAcceptance: default: VPC Endpoint Service Acceptance Required Attribute AwsAccountToWhitelist: default: AWS Account to Whitelist for the Service CreateBastionHost: default: Condition to create bastion host Parameters: VpcCidr: AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" Default: 192.168.1.0/24 Description: CIDR block for the VPC Type: String ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/y # AZ1: AvailabilityZone1: Description: Availability Zone to use for the Public Subnet 1 in the VPC Type: AWS::EC2::AvailabilityZone::Name ConstraintDescription: Valid Availability Zone Id PublicSubnet1Cidr: AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" Default: 192.168.1.0/28 Description: CIDR block for the Public Subnet 1 located in Availability Zone 1 Type: String ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 ApplianceSubnet1Cidr: AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" Default: 192.168.1.16/28 Description: CIDR block for the Appliance Subnet 1 located in Availability Zone 1 Type: String ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 # AZ2: AvailabilityZone2: Description: Availability Zone to use for the Public Subnet 2 in the VPC Type: AWS::EC2::AvailabilityZone::Name ConstraintDescription: Valid Availability Zone Id PublicSubnet2Cidr: AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" Default: 192.168.1.32/28 Description: CIDR block for the Public Subnet 2 located in Availability Zone 2 Type: String ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 ApplianceSubnet2Cidr: AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$" Default: 192.168.1.48/28 Description: CIDR block for the Appliance Subnet 2 located in Availability Zone 2 Type: String ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 # EC2 Instance ApplianceInstanceType: Description: Select EC2 instance type for Appliance instance. Default is set to t2.micro Default: t2.micro AllowedValues: - t2.micro Type: String ApplianceAmiId: Type: AWS::SSM::Parameter::Value Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' ApplianceInstanceDiskSize: Description: Appliance instance disk size in GB. Default is set to 8GB Default: 8 AllowedValues: [8] Type: Number ConstraintDescription: Should be a valid instance size in GB KeyPairName: Description: EC2 KeyPair required for accessing EC2 instance Type: AWS::EC2::KeyPair::KeyName ConstraintDescription: Must be the name of an existing EC2 KeyPair AccessLocation: Description: >- Enter desired Network CIDR to allow traffic to appliance. Default is set to access from anywhere and it is not recommended AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})" MinLength: "9" MaxLength: "18" Default: 0.0.0.0/0 Type: String ConstraintDescription: Must be a valid Network CIDR of the form x.x.x.x/y # GWLB: GwlbName: Description: >- Appliance gateway name. This name must be unique within your AWS account and can have a maximum of 32 alphanumeric characters and hyphens. A name cannot begin or end with a hyphen. Type: String Default: gwlb1 ConstraintDescription: Must be a valid GWLB Name TargetGroupName: Description: Target Group Name Type: String Default: tg1 ConstraintDescription: Must be a valid target group name HealthProtocol: Description: >- The protocol the appliane gateway uses when performing health checks on targets. For Application Load Balancers, the default is HTTP. Type: String Default: HTTP AllowedValues: ['TCP', 'HTTP', 'HTTPS'] ConstraintDescription: Must be a valid health check protocol HealthPort: Description: >- The port the load balancer uses when performing health checks on targets. The default is traffic-port, which is the port on which each target receives traffic from the load balancer. Type: String Default: '80' ConstraintDescription: Must be a valid health check port ConnectionAcceptance: Description: >- Acceptance required for endpoint connection or not. Select true or false to either acceptance required or acceptance not required default is set to false: acceptance not required Default: "false" AllowedValues: ["true", "false"] Type: String ConstraintDescription: Must be true or false AwsAccountToWhitelist: Description: >- Enter ARN of one or more prinicapls: IAM user, IAM roles and AWS accounts. To grant permissions to all principals, specify an asterisk (*). exmaple: arn:aws:iam::112233445566:user1 Default: '*' Type: String ConstraintDescription: Must be a valid AWS ARN of one or more principals # Condition: CreateBastionHost: Description: "Create bastion host? Allowed values: Yes/No, default set to Yes" Default: "Yes" AllowedValues: ["Yes", "No"] Type: String ConstraintDescription: Value must be either Yes or No Conditions: BastionHostCreate: !Equals [!Ref CreateBastionHost, "Yes"] Resources: # Inspection/Appliance/Security VPC: Vpc: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VpcCidr EnableDnsSupport: true EnableDnsHostnames: true InstanceTenancy: default Tags: - Key: Name Value: !Sub "${AWS::StackName}-inspection-vpc" # IGW associated with Appliance VPC: InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub "${AWS::StackName}-igw" AttachInternetGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref Vpc InternetGatewayId: !Ref InternetGateway # Subnets: # AZ1: PublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Ref AvailabilityZone1 CidrBlock: !Ref PublicSubnet1Cidr VpcId: !Ref Vpc MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub "${AWS::StackName}-public-subnet-1" ApplianceSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Ref AvailabilityZone1 CidrBlock: !Ref ApplianceSubnet1Cidr VpcId: !Ref Vpc MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub "${AWS::StackName}-appliance-subnet-1" # AZ2: PublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Ref AvailabilityZone2 CidrBlock: !Ref PublicSubnet2Cidr VpcId: !Ref Vpc MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub "${AWS::StackName}-public-subnet-2" ApplianceSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Ref AvailabilityZone2 CidrBlock: !Ref ApplianceSubnet2Cidr VpcId: !Ref Vpc MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub "${AWS::StackName}-appliance-subnet-1" # Elastic IP and NAT GW: # AZ1: NATGW1EIP: Type: AWS::EC2::EIP Properties: Domain: vpc NATGW1: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NATGW1EIP.AllocationId SubnetId: !Ref PublicSubnet1 Tags: - Key: Name Value: !Sub "${AWS::StackName}-natgw-1" # AZ 2: NATGW2EIP: Type: AWS::EC2::EIP Properties: Domain: vpc NATGW2: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NATGW2EIP.AllocationId SubnetId: !Ref PublicSubnet2 Tags: - Key: Name Value: !Sub "${AWS::StackName}-natgw-2" # Route Table: # Public: PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub "${AWS::StackName}-public-rtb1" PublicRoute: Type: AWS::EC2::Route DependsOn: AttachInternetGateway Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway RouteTableId: !Ref PublicRouteTable # Subnet to Route Table association: PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable # Private/Appliance: ApplianceRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub "${AWS::StackName}-appliance-rtb1" ApplianceRtb1Route: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NATGW1 RouteTableId: !Ref ApplianceRouteTable1 ApplianceRouteTable2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub "${AWS::StackName}-appliance-rtb2" ApplianceRtb2Route: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NATGW2 RouteTableId: !Ref ApplianceRouteTable2 # Subnet to Route Table association: ApplianceSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref ApplianceSubnet1 RouteTableId: !Ref ApplianceRouteTable1 ApplianceSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref ApplianceSubnet2 RouteTableId: !Ref ApplianceRouteTable2 # Security Group: BastionSg: Type: AWS::EC2::SecurityGroup Properties: VpcId: !Ref Vpc GroupName: !Sub "${AWS::StackName}-bastion-sg" GroupDescription: >- Access to bastion instance: allow SSH, HTTP, HTTPS and ICMP access from appropriate location. Allow all traffic from VPC CIDR SecurityGroupIngress: - CidrIp: !Ref AccessLocation IpProtocol: tcp FromPort: 22 ToPort: 22 - CidrIp: !Ref AccessLocation IpProtocol: tcp FromPort: 80 ToPort: 80 - CidrIp: !Ref AccessLocation IpProtocol: tcp FromPort: 443 ToPort: 443 - CidrIp: !Ref AccessLocation IpProtocol: ICMP FromPort: -1 ToPort: -1 - CidrIp: !Ref VpcCidr IpProtocol: "-1" FromPort: -1 ToPort: -1 SecurityGroupEgress: - CidrIp: 0.0.0.0/0 IpProtocol: "-1" FromPort: -1 ToPort: -1 Tags: - Key: Name Value: !Sub "${AWS::StackName}-bastion-sg" ApplianceSg: Type: AWS::EC2::SecurityGroup Properties: VpcId: !Ref Vpc GroupName: !Sub "${AWS::StackName}-appliance-sg" GroupDescription: >- Access to Appliance instance: allow SSH and ICMP access from desired CIDR. Allow all traffic from VPC CIDR SecurityGroupIngress: - CidrIp: !Ref VpcCidr IpProtocol: "-1" FromPort: -1 ToPort: -1 SecurityGroupEgress: - CidrIp: 0.0.0.0/0 IpProtocol: "-1" FromPort: -1 ToPort: -1 Tags: - Key: Name Value: !Sub "${AWS::StackName}-appliance-sg" # Gateway Load Balancer (GWLB), Target Group, Listener # GWLB: Gwlb: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Name: !Ref GwlbName Type: gateway Subnets: - !Ref ApplianceSubnet1 - !Ref ApplianceSubnet2 Tags: - Key: Name Value: !Sub "${AWS::StackName}-gwlb-1" # Target Group: TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: !Ref TargetGroupName Port: 6081 Protocol: GENEVE TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: "20" VpcId: !Ref Vpc HealthCheckPort: !Ref HealthPort HealthCheckProtocol: !Ref HealthProtocol TargetType: instance Targets: - Id: !Ref Appliance1 - Id: !Ref Appliance2 Tags: - Key: Name Value: !Sub "${AWS::StackName}-tg-1" # Listener: Listener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref TargetGroup LoadBalancerArn: !Ref Gwlb # IAM Instance Role and Profile: ApplianceRole: Type: AWS::IAM::Role Properties: RoleName: !Sub "${AWS::StackName}-appliance-role" ManagedPolicyArns: - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' Path: / AppliancePolicy: Type: AWS::IAM::Policy Properties: PolicyName: AppServer PolicyDocument: Statement: - Effect: Allow Action: - ec2:DescribeNetworkInterfaces Resource: '*' Roles: - !Ref ApplianceRole ApplianceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref ApplianceRole # EC2 Instances (Appliances acting as target for GWLB): Appliance1: DependsOn: Gwlb Type: AWS::EC2::Instance Properties: ImageId: !Ref ApplianceAmiId KeyName: !Ref KeyPairName InstanceType: !Ref ApplianceInstanceType IamInstanceProfile: !Ref ApplianceProfile SecurityGroupIds: - !Ref ApplianceSg SubnetId: !Ref ApplianceSubnet1 BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: !Ref ApplianceInstanceDiskSize Tags: - Key: Name Value: !Sub "${AWS::StackName}-appliance-1" UserData: Fn::Base64: | #!/bin/bash -ex # Install packages: yum update -y; yum install jq -y; yum install httpd -y; yum install htop -y; sudo yum install iptables-services -y; # Enable IP Forwarding: echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.d/00-defaults.conf; sysctl -p /etc/sysctl.d/00-defaults.conf; # Configure hostname: hostnamectl set-hostname gwlb-target-1; # Configure SSH client alive interval for ssh session timeout: echo 'ClientAliveInterval 60' | sudo tee --append /etc/ssh/sshd_config; service sshd restart; # Set dark background for vim: touch /home/ec2-user/.vimrc; echo "set background=dark" >> /home/ec2-user/.vimrc; # Define variables: curl --silent http://169.254.169.254/latest/dynamic/instance-identity/document > /home/ec2-user/iid; export instance_interface=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/); export instance_vpcid=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/$instance_interface/vpc-id); export instance_az=$(cat /home/ec2-user/iid |grep 'availability' | awk -F': ' '{print $2}' | awk -F',' '{print $1}'); export instance_ip=$(cat /home/ec2-user/iid |grep 'privateIp' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); export instance_region=$(cat /home/ec2-user/iid |grep 'region' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); export gwlb_ip=$(aws --region $instance_region ec2 describe-network-interfaces --filters Name=vpc-id,Values=$instance_vpcid | jq ' .NetworkInterfaces[] | select(.AvailabilityZone=='$instance_az') | select(.InterfaceType=="gateway_load_balancer") |.PrivateIpAddress' -r); # Start http and configure index.html: systemctl enable httpd; systemctl start httpd; touch /var/www/html/index.html; echo "" >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo " Gateway Load Balancer POC" >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo "

Welcome to Gateway Load Balancer POC:

" >> /var/www/html/index.html echo "

This is appliance running in $instance_az. Happy testing!

" >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo "" >> /var/www/html/index.html # Start and configure iptables: systemctl enable iptables; systemctl start iptables; # Configuration below allows allows all traffic: # Set the default policies for each of the built-in chains to ACCEPT: iptables -P INPUT ACCEPT; iptables -P FORWARD ACCEPT; iptables -P OUTPUT ACCEPT; # Flush the nat and mangle tables, flush all chains (-F), and delete all non-default chains (-X): iptables -t nat -F; iptables -t mangle -F; iptables -F; iptables -X; # Configure nat table to hairpin traffic back to GWLB: iptables -t nat -A PREROUTING -p udp -s $gwlb_ip -d $instance_ip -i eth0 -j DNAT --to-destination $gwlb_ip:6081; iptables -t nat -A POSTROUTING -p udp --dport 6081 -s $gwlb_ip -d $gwlb_ip -o eth0 -j MASQUERADE; # Save iptables: service iptables save; Appliance2: Type: AWS::EC2::Instance Properties: ImageId: !Ref ApplianceAmiId KeyName: !Ref KeyPairName InstanceType: !Ref ApplianceInstanceType IamInstanceProfile: !Ref ApplianceProfile SecurityGroupIds: - !Ref ApplianceSg SubnetId: !Ref ApplianceSubnet2 BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: !Ref ApplianceInstanceDiskSize Tags: - Key: Name Value: !Sub "${AWS::StackName}-appliance-2" UserData: Fn::Base64: | #!/bin/bash -ex # Install packages: yum update -y; yum install jq -y; yum install httpd -y; yum install htop -y; sudo yum install iptables-services -y; # Enable IP Forwarding: echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.d/00-defaults.conf; sysctl -p /etc/sysctl.d/00-defaults.conf; # Configure hostname: hostnamectl set-hostname gwlb-target-2; # Configure SSH client alive interval for ssh session timeout: echo 'ClientAliveInterval 60' | sudo tee --append /etc/ssh/sshd_config; service sshd restart; # Set dark background for vim: touch /home/ec2-user/.vimrc; echo "set background=dark" >> /home/ec2-user/.vimrc; # Define variables: curl --silent http://169.254.169.254/latest/dynamic/instance-identity/document > /home/ec2-user/iid; export instance_interface=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/); export instance_vpcid=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/$instance_interface/vpc-id); export instance_az=$(cat /home/ec2-user/iid |grep 'availability' | awk -F': ' '{print $2}' | awk -F',' '{print $1}'); export instance_ip=$(cat /home/ec2-user/iid |grep 'privateIp' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); export instance_region=$(cat /home/ec2-user/iid |grep 'region' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); export gwlb_ip=$(aws --region $instance_region ec2 describe-network-interfaces --filters Name=vpc-id,Values=$instance_vpcid | jq ' .NetworkInterfaces[] | select(.AvailabilityZone=='$instance_az') | select(.InterfaceType=="gateway_load_balancer") |.PrivateIpAddress' -r); # Start httpd and configure index.html: systemctl enable httpd; systemctl start httpd; touch /var/www/html/index.html; echo "" >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo " Gateway Load Balancer POC" >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo "

Welcome to Gateway Load Balancer POC:

" >> /var/www/html/index.html echo "

This is appliance running in $instance_az. Happy testing!

" >> /var/www/html/index.html echo " " >> /var/www/html/index.html echo "" >> /var/www/html/index.html # Start and configure iptables: systemctl enable iptables; systemctl start iptables; # Configuration below allows allows all traffic: # Set the default policies for each of the built-in chains to ACCEPT: iptables -P INPUT ACCEPT; iptables -P FORWARD ACCEPT; iptables -P OUTPUT ACCEPT; # Flush the nat and mangle tables, flush all chains (-F), and delete all non-default chains (-X): iptables -t nat -F; iptables -t mangle -F; iptables -F; iptables -X; # Configure nat table to hairpin traffic back to GWLB: iptables -t nat -A PREROUTING -p udp -s $gwlb_ip -d $instance_ip -i eth0 -j DNAT --to-destination $gwlb_ip:6081; iptables -t nat -A POSTROUTING -p udp --dport 6081 -s $gwlb_ip -d $gwlb_ip -o eth0 -j MASQUERADE; # Save iptables: service iptables save; BastionHost1: Type: AWS::EC2::Instance Condition: BastionHostCreate Properties: ImageId: !Ref ApplianceAmiId KeyName: !Ref KeyPairName InstanceType: !Ref ApplianceInstanceType IamInstanceProfile: !Ref ApplianceProfile SecurityGroupIds: - !Ref BastionSg SubnetId: !Ref PublicSubnet1 BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: !Ref ApplianceInstanceDiskSize Tags: - Key: Name Value: !Sub "${AWS::StackName}-bastion-host-1" UserData: Fn::Base64: | #!/bin/bash -ex # Install packages: yum update -y; yum install htop -y; # Configure hostname: hostnamectl set-hostname inspection-vpc-bh; # Configure SSH client alive interval for ssh session timeout: echo 'ClientAliveInterval 60' | sudo tee --append /etc/ssh/sshd_config; service sshd restart; # Set dark background for vim: touch /home/ec2-user/.vimrc; echo "set background=dark" >> /home/ec2-user/.vimrc; # VPC Endpoint Service using GWLB ARN: VpcEndpointService: Type: AWS::EC2::VPCEndpointService Properties: GatewayLoadBalancerArns: - !Ref Gwlb AcceptanceRequired: !Ref ConnectionAcceptance VpcEndpointServicePermissions: Type: AWS::EC2::VPCEndpointServicePermissions Properties: AllowedPrincipals: - !Ref AwsAccountToWhitelist ServiceId: !Ref VpcEndpointService # Outputs Outputs: InspectionVpcCidr: Description: Inspection VPC CIDR Value: !Ref VpcCidr InspectionVpcId: Description: Inspection VPC ID Value: !Ref Vpc PublicSubnet1Id: Description: Inspection VPC Public Subnet 1 ID Value: !Ref PublicSubnet1 ApplianceSubnet1Id: Description: Inspection VPC Appliance Subnet 1 ID Value: !Ref ApplianceSubnet1 PublicSubnet2Id: Description: Inspection VPC Public Subnet 2 ID Value: !Ref PublicSubnet2 ApplianceSubnet2Id: Description: Inspection VPC Appliance Subnet 2 ID Value: !Ref ApplianceSubnet2 BastionSgId: Description: Inspection VPC Security Group ID Value: !Ref BastionSg ApplianceSgId: Description: Inspection VPC Security Group ID Value: !Ref ApplianceSg Appliance1PrivateIp: Description: Inspection VPC Appliance1's private IP Value: !GetAtt Appliance1.PrivateIp Appliance2PrivateIp: Description: Appliance VPC Appliance2's private IP Value: !GetAtt Appliance2.PrivateIp BastionHost1PublicIp: Condition: BastionHostCreate Description: Inspection VPC bastion host1's public IP Value: !GetAtt BastionHost1.PublicIp ApplianceGwlbArn: Description: Appliance VPC GWLB ARN Value: !Ref Gwlb ApplianceVpcEndpointServiceId: Description: Appliance VPC Endpoint Service ID Value: !Ref VpcEndpointService ApplianceVpcEndpointServiceName: Description: Appliance VPC Endpoint Service Name. Required to create GWLB Endpoint Value: !Sub "com.amazonaws.vpce.${AWS::Region}.${VpcEndpointService}"