AWSTemplateFormatVersion: 2010-09-09 Description: >- Create Cisco Duo network gateway Admin server (qs-1qtp7fcul). Parameters: AdminServerRootVolumeSize: Description: Root volume size Type: Number Default: 40 AdminServerInstanceType: Description: EC2 instance type Type: String AllowedValues: - t3.micro - t3.small - t3.medium - t3.large - m5.large - m5.xlarge - m5.2xlarge - m5.4xlarge - m5.8xlarge - c5.large - c5.xlarge - c5.2xlarge - c5.4xlarge - c5.9xlarge - r5.large - r5.xlarge - r5.2xlarge - r5.4xlarge - r5.8xlarge ConstraintDescription: must be a valid EC2 instance type. ConfigYAMLPath: Description: (Optional) S3 bucket path of the scripted configuration YAML file. (E.g. https://duoconfigbucket.s3.amazonaws.com/scripts/config/dng_config.yml) Type: String QSS3BucketName: AllowedPattern: '^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$' ConstraintDescription: >- Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Description: >- S3 bucket name for the Quick Start assets. Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Type: String #QSS3BucketRegion: # Description: The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value. # Type: String QSS3KeyPrefix: AllowedPattern: '^[0-9a-zA-Z-/]*$' ConstraintDescription: >- Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). Description: >- S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). Type: String KeyPairName: Type: 'AWS::EC2::KeyPair::KeyName' ConstraintDescription: Name of an existing EC2 KeyPair. RemoteAccessCIDR: 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])(\/([0-9]|[1-2][0-9]|3[0-2]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/x Description: Allowed CIDR block for remote access Type: String PrivateSubnet1ID: Description: Private Subnet Id 1 Type: 'AWS::EC2::Subnet::Id' PublicSubnet1ID: Description: Public Subnet Id 1 Type: 'AWS::EC2::Subnet::Id' PublicSubnet2ID: Description: Public Subnet Id 2 Type: 'AWS::EC2::Subnet::Id' VPCID: Description: 'ID of the VPC (e.g., vpc-0343606e)' Type: 'AWS::EC2::VPC::Id' RedisHost: Description: Hostname of your ElastiCache Redis cluster (e.g. dngcluster.abc1.0001.usw2.cache.amazonaws.com) Type: String RedisPort: Description: Redis traffic port number. Type: String RedisAuth: Description: Redis AUTH token Type: String LatestAmiId: Type: 'AWS::SSM::Parameter::Value' Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' DomainName: Type: String Description: Fully qualified domain name for the Duo Network Gateway load balancer. If you don't provide a value for ACMSSLCertificateArn, use the HostedZoneID. MaxLength: 128 Default: "" HostedZoneID: Type: String Description: Route 53-hosted zone ID of the domain name. If you don't provide an ACMSSLCertificateArn value, the Quick Start creates the ACM certificate for you using HostedZoneID in conjunction with DomainName. Default: "" ACMSSLCertificateArn: Description: Amazon Resource Name (ARN) of the load balancer's SSL certificate. If you don't provide values for DomainName and HostedZoneID, provide a value for ACMSSLCertificateArn. Type: String Default: '' QSS3BucketRegion: Default: 'us-east-1' Description: 'The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.' Type: String #Mappings: # AWSAMIRegionMap: # AMI: # US1604HVM: ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-20180405 # ap-northeast-1: # US1604HVM: ami-60a4b21c # ap-northeast-2: # US1604HVM: ami-633d920d # ap-south-1: # US1604HVM: ami-dba580b4 # ap-southeast-1: # US1604HVM: ami-82c9ecfe # ap-southeast-2: # US1604HVM: ami-2b12dc49 # ca-central-1: # US1604HVM: ami-9d7afcf9 # eu-central-1: # US1604HVM: ami-cd491726 # eu-west-1: # US1604HVM: ami-74e6b80d # eu-west-2: # US1604HVM: ami-506e8f37 # sa-east-1: # US1604HVM: ami-5782d43b # us-east-1: # US1604HVM: ami-6dfe5010 # us-east-2: # US1604HVM: ami-e82a1a8d # us-west-1: # US1604HVM: ami-493f2f29 # us-west-2: # US1604HVM: ami-ca89eeb2 Conditions: InstallConfigScript: !Not [ !Equals [ !Ref ConfigYAMLPath, "" ] ] UsingDefaultBucket: !Equals - !Ref QSS3BucketName - 'aws-quickstart' CustomDns: !Not [!Equals [!Ref DomainName, '']] CreateDns: !And - !Not - !Equals - !Ref 'HostedZoneID' - '' - !Not - !Equals - !Ref 'DomainName' - '' Resources: AdminServerInstance: Type: AWS::EC2::Instance Metadata: AWS::CloudFormation::Authentication: S3Auth: buckets: - !Ref QSS3BucketName roleName: !Ref AdminServerInstanceRole type: S3 AWS::CloudFormation::Init: configSets: bootstrap: - export-redis-env-variables #- config-docker - install-docker - config-docker-compose - add-user - config-admin-server - !If - InstallConfigScript - load-scripted-config - !Ref 'AWS::NoValue' export-redis-env-variables: commands: 1_export_redis_host: command: !Sub echo export REDIS_HOST=${RedisHost} >> /etc/profile.d/env.sh cwd: "~" 2_export_redis_port: command: !Sub echo export REDIS_PORT=${RedisPort} >> /etc/profile.d/env.sh cwd: "~" 3_export_redis_auth: command: !Sub echo export REDIS_AUTH=${RedisAuth} >> /etc/profile.d/env.sh cwd: "~" #config-docker: # commands: # add_gpg_key: # command: wget -O- "https://download.docker.com/linux/ubuntu/gpg" | sudo apt-key add - # add_repo: # command: sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" # update_repo: # command: sudo apt-get update install-docker: packages: yum: docker: [] commands: 1_enable_docker_service: command: sudo systemctl enable docker.service 2_start_docker_service: command: sudo systemctl start docker config-docker-compose: commands: 1_download-docker-compose: command: wget -O- "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" > ./docker-compose 2_set-permissions: command: chmod +x ./docker-compose 3_change-docker-compose-location: command: sudo mv ./docker-compose /usr/bin/ add-user: commands: add-user-to-docker-group: command: sudo usermod -aG docker $(whoami) config-admin-server: commands: 1_download_admin_server_image: command: wget --content-disposition https://dl.duosecurity.com/network-gateway-admin-ha-latest.yml cwd: "~" 2_install_portal_server_image: command: docker-compose -p network-gateway -f $(ls network-gateway-*-ha.admin.yml) up -d env: REDIS_HOST: !Sub ${RedisHost} REDIS_PORT: !Sub ${RedisPort} REDIS_AUTH: !Sub ${RedisAuth} cwd: "~" load-scripted-config: files: /usr/local/dng_config.yml: source: !Ref ConfigYAMLPath group: root owner: root mode: '000644' authentication: S3Auth commands: load_config: command: docker exec -i network-gateway-admin set-config < /usr/local/dng_config.yml cwd: "~" Properties: IamInstanceProfile: !Ref AdminServerInstanceProfile ImageId: !Ref LatestAmiId #!FindInMap #- AWSAMIRegionMap #- !Ref 'AWS::Region' #- US1604HVM BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: !Ref AdminServerRootVolumeSize VolumeType: gp2 Encrypted: true DeleteOnTermination: true InstanceType: !Ref AdminServerInstanceType KeyName: !Ref KeyPairName NetworkInterfaces: - Description: Primary boot node network interface GroupSet: - !Ref AdminServerSecurityGroup AssociatePublicIpAddress: true DeviceIndex: "0" DeleteOnTermination: true SubnetId: !Ref PrivateSubnet1ID # SubnetId: !Ref PrivateSubnet1ID UserData: Fn::Base64: !Sub | #!/bin/bash -xe sudo yum install -y git #cfn signaling functions function cfn_fail { cfn-signal -e 1 --stack ${AWS::StackName} --region ${AWS::Region} --resource AdminServerInstance exit 1 } function cfn_success { cfn-signal -e 0 --stack ${AWS::StackName} --region ${AWS::Region} --resource AdminServerInstance exit 0 } until git clone https://github.com/aws-quickstart/quickstart-linux-utilities.git ; do echo "Retrying"; done cd /quickstart-linux-utilities; source quickstart-cfn-tools.source; qs_update-os || qs_err; qs_bootstrap_pip || qs_err qs_aws-cfn-bootstrap || qs_err cfn-init -v --stack '${AWS::StackName}' --resource AdminServerInstance --configsets bootstrap --region ${AWS::Region} || cfn_fail [ $(qs_status) == 0 ] && cfn_success || cfn_fail Tags: - Key: Name Value: !Sub AdminServer-${AWS::StackName} CreationPolicy: ResourceSignal: Timeout: PT5M AdminServerInstanceProfile: DependsOn: AdminServerInstancePolicy Type: 'AWS::IAM::InstanceProfile' Properties: Roles: - !Ref AdminServerInstanceRole Path: / AdminServerInstanceRole: Type: 'AWS::IAM::Role' Properties: Path: / AssumeRolePolicyDocument: Statement: - Action: - 'sts:AssumeRole' Principal: Service: - !Sub 'ec2.${AWS::URLSuffix}' Effect: Allow Version: 2012-10-17 ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy' AdminServerInstancePolicy: Type: 'AWS::IAM::Policy' Properties: PolicyName: AdminServerPolicy PolicyDocument: Version: 2012-10-17 Statement: - Action: - 's3:GetObject' Resource: !Sub - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] Effect: Allow - Action: - 'logs:CreateLogStream' - 'logs:GetLogEvents' - 'logs:PutLogEvents' - 'logs:DescribeLogGroups' - 'logs:DescribeLogStreams' - 'logs:PutRetentionPolicy' - 'logs:PutMetricFilter' - 'logs:CreateLogGroup' Resource: !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${AdminServerMainLogGroup}:*" Effect: Allow - Action: - 'ec2:AssociateAddress' - 'ec2:DescribeAddresses' Resource: '*' Effect: Allow - Action: - 's3:GetObject' Resource: '*' Effect: Allow Roles: - !Ref AdminServerInstanceRole AdminServerMainLogGroup: Type: 'AWS::Logs::LogGroup' AdminServerSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: Enables SSH Access to Admin server VpcId: !Ref VPCID SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref RemoteAccessCIDR - IpProtocol: tcp FromPort: 8443 ToPort: 8443 SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup # Load balancer resources ApplicationLoadBalancer: Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' Properties: Subnets: - !Ref PublicSubnet1ID - !Ref PublicSubnet2ID SecurityGroups: - !Ref LoadBalancerSecurityGroup Tags: - Key: name Value: AdminServer ALB ALBListener: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: Certificates: - CertificateArn: !If [ CreateDns, !GetAtt "ACMCertificate.Outputs.ACMCertificate", !Ref ACMSSLCertificateArn ] DefaultActions: - Type: forward TargetGroupArn: !Ref ALBTargetGroup LoadBalancerArn: !Ref ApplicationLoadBalancer Port: 8443 Protocol: HTTPS ALBTargetGroup: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' Properties: HealthCheckIntervalSeconds: 30 HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 3 HealthCheckPath: /login Matcher: HttpCode: '200' HealthCheckProtocol: HTTPS Port: 8443 Protocol: HTTPS UnhealthyThresholdCount: 5 VpcId: !Ref VPCID TargetType: instance Targets: - Id: !Ref AdminServerInstance Port: 8443 LoadBalancerSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: Enable HTTP and HTTPS from the Admin server App load balancer only SecurityGroupIngress: - IpProtocol: tcp FromPort: 8443 ToPort: 8443 CidrIp: !Ref RemoteAccessCIDR VpcId: !Ref VPCID # ACM Certificate resources ACMCertificate: Metadata: cfn-lint: config: ignore_checks: - W9198 Type: AWS::CloudFormation::Stack Condition: CreateDns Properties: TemplateURL: !Sub - 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}submodules/quickstart-aws-acm-certificate/templates/quickstart-aws-acm-certificate.template.yml' - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] Parameters: QSS3BucketName: !Ref QSS3BucketName QSS3BucketRegion: !Ref QSS3BucketRegion QSS3KeyPrefix: !Sub '${QSS3KeyPrefix}submodules/quickstart-aws-acm-certificate/' DomainName: !Ref DomainName HostedZoneID: !Ref HostedZoneID DNGDNSRecord: Condition: CreateDns Type: AWS::Route53::RecordSet Properties: Type: A Name: !Ref DomainName AliasTarget: HostedZoneId: !GetAtt ApplicationLoadBalancer.CanonicalHostedZoneID DNSName: !GetAtt ApplicationLoadBalancer.DNSName HostedZoneId: !Ref 'HostedZoneID' #-----------------------------------------a # Outputs #-----------------------------------------a Outputs: AdminServerInstance: Description: Admin server instance Value: !Ref AdminServerInstance AdminServerLoadBalancer: Value: !Sub - "https://${DNSAddress}:8443/" - DNSAddress: !If [ CustomDns, !Ref DomainName, !GetAtt "ApplicationLoadBalancer.DNSName" ]