Created
July 28, 2021 16:17
-
-
Save assimilat/ec254c71953e82826cf269f999124c8d to your computer and use it in GitHub Desktop.
duo_proxy_fargate_template
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
# SPDX-License-Identifier: MIT-0 | |
# AWS Customer Agreement - https://aws.amazon.com/agreement | |
AWSTemplateFormatVersion: 2010-09-09 | |
Description: > | |
Configures Duo RADIUS ECS services using Fargate for use in Directory Service MFA (can be used | |
for AWS SSO, WorkSpaces, and other SAML service providers) | |
Metadata: | |
QuickStartDocumentation: | |
EntrypointName: "Parameters for deploying into an existing VPC" | |
Order: "2" | |
AWS::CloudFormation::Interface: | |
ParameterGroups: | |
- Label: | |
default: Duo account settings | |
Parameters: | |
- DuoIntegrationKey | |
- DuoSecretKey | |
- DuoApiHostName | |
- Label: | |
default: RADIUS proxy configuration settings | |
Parameters: | |
- DirectoryServiceId | |
- RadiusProxyServerCount | |
- RadiusPortNumber | |
- DuoFailMode | |
- DuoMaxCapacity | |
- NotificationEmail | |
- Label: | |
default: Directory Sync configuration settings | |
Parameters: | |
- AdSync | |
- DirectoryIntegrationKey | |
- DirectorySecretKey | |
- adReadOnlyUser | |
- adReadOnlyPassword | |
- Label: | |
default: Duo Authentication Proxy ECR configuration | |
Parameters: | |
- EcrImageRetention | |
- EcrRepoName | |
- Label: | |
default: CodeCommit configuration | |
Parameters: | |
- CodeCommitRepoName | |
- CodeCommitBranchName | |
# - CodeCommitS3Bucket | |
# - CodeCommitS3BucketKey | |
- Label: | |
default: CodePipeline configuration | |
Parameters: | |
- EcrCronExpression | |
- Label: | |
default: AWS KMS configuration | |
Parameters: | |
- AdminArn | |
- Label: | |
default: Quick Start configuration | |
Parameters: | |
- QSS3BucketName | |
- QSS3BucketRegion | |
- QSS3KeyPrefix | |
ParameterLabels: | |
AdminArn: | |
default: AWS KMS administrator role ARN | |
# CodeCommitS3Bucket: | |
# default: CodeCommit S3 Bucket | |
# CodeCommitS3BucketKey: | |
# default: CodeCommit S3 Bucket Key | |
EcrRepoName: | |
default: ECR repo name | |
EcrImageRetention: | |
default: ECR retention period | |
CodeCommitRepoName: | |
default: CodeCommit repo name | |
CodeCommitBranchName: | |
default: CodeCommit branch name | |
EcrCronExpression: | |
default: ECR rebuild cron expression | |
NotificationEmail: | |
default: Duo administrator email | |
DuoMaxCapacity: | |
default: Duo maximum tasks | |
AdSync: | |
default: Sync Active Directory | |
adReadOnlyUser: | |
default: Active Directory read-only user | |
adReadOnlyPassword: | |
default: Active Directory read-only password | |
DirectoryIntegrationKey: | |
default: Duo directory integration key | |
DirectorySecretKey: | |
default: Duo directory secret key | |
DuoIntegrationKey: | |
default: Duo integration key | |
DuoSecretKey: | |
default: Duo secret key | |
DuoApiHostName: | |
default: Duo API hostname | |
DirectoryServiceId: | |
default: Directory Service ID | |
RadiusProxyServerCount: | |
default: RADIUS proxy server count | |
RadiusPortNumber: | |
default: RADIUS port number | |
DuoFailMode: | |
default: Duo fail mode | |
QSS3BucketName: | |
default: Quick Start S3 bucket name | |
QSS3BucketRegion: | |
default: Quick Start S3 bucket Region | |
QSS3KeyPrefix: | |
default: Quick Start S3 key prefix | |
#----------------------------------------------------------- | |
# Parameters | |
#----------------------------------------------------------- | |
Parameters: | |
AdminArn: | |
Type: String | |
Description: IAM Amazon Resource Name that has administrator rights to the AWS KMS key. If you keep this box blank, KMS key policy will not have an administrator role to administer it. | |
Default: '' | |
# CodeCommitS3Bucket: | |
# Type: String | |
# Description: S3 bucket name where the code for Duo AuthProxy is located | |
# AllowedPattern: "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$" | |
# ConstraintDescription: "S3 Bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-)." | |
# CodeCommitS3BucketKey: | |
# Type: String | |
# Description: S3 bucket key location where the code for Duo AuthProxy | |
# AllowedPattern: "^[0-9a-zA-Z-/.]*$" | |
# ConstraintDescription: "S3 Bucket key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/)." | |
EcrRepoName: | |
Type: String | |
Description: Name of the Duo Authentication proxy ECR repo. | |
Default: duo-authproxy | |
AllowedPattern: "(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*" | |
MinLength: 2 | |
MaxLength: 256 | |
EcrImageRetention: | |
Type: Number | |
Description: Number of days to retain the ECR image. | |
ConstraintDescription: 'Must be in the range [30-600].' | |
Default: 30 | |
MinValue: 30 | |
MaxValue: 600 | |
CodeCommitRepoName: | |
Type: String | |
Description: Name of the CodeCommit repo that which will manage all the code base. | |
Default: duo-authproxy | |
AllowedPattern: "^[0-9a-zA-Z-/]*$" | |
CodeCommitBranchName: | |
Type: String | |
Description: Name of the CodeCommit branch where all the code base is located. This branch starts actions in CodePipeline. | |
Default: ecr | |
AllowedPattern: "^[0-9a-zA-Z-/]*$" | |
EcrCronExpression: | |
Type: String | |
Description: Cron expression trigger. By default, it's set at 0000 UTC every Saturday. See https://docs.aws.amazon.com/eventbridge/latest/userguide/scheduled-events.html. | |
Default: '0 0 ? * SAT *' | |
NotificationEmail: | |
Type: String | |
Description: Email address of Duo administrators to notify when the pipeline fails or when an update to the directory fails. | |
AllowedPattern: '^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$' | |
ConstraintDescription: Provide a valid email address. | |
AdSync: | |
Type: String | |
Description: If Active Directory synchronization is not required, choose "no." | |
Default: 'yes' | |
AllowedValues: | |
- 'yes' | |
- 'no' | |
adReadOnlyUser: | |
Type: String | |
Description: > | |
Name of Active Directory user with read-only access to the directory for Duo's Directory Sync configuration. | |
Default: '' | |
adReadOnlyPassword: | |
Type: String | |
NoEcho: true | |
Description: > | |
Password for Active Directory user with read-only access to the directory for Duo's Directory Sync configuration. | |
Default: '' | |
DirectoryIntegrationKey: | |
Type: String | |
Description: > | |
Integration key retrieved from Duo's Directory Sync configuration. | |
Default: '' | |
DirectorySecretKey: | |
Type: String | |
NoEcho: true | |
Description: > | |
Secret key retrieved from Duo's Directory Sync configuration. | |
Default: '' | |
DuoIntegrationKey: | |
Type: String | |
Description: > | |
Integration key retrieved from the Duo RADIUS application configuration. | |
DuoSecretKey: | |
Type: String | |
NoEcho: true | |
Description: > | |
Secret key retrieved from the Duo RADIUS application configuration. | |
DuoApiHostName: | |
Type: String | |
Description: > | |
API hostname retrieved from the Duo RADIUS application configuration. | |
# AllowedPattern: ^api\-[a-zA-Z0-9]*.duofederal.com$ | |
# ConstraintDescription: > | |
# API hostname must match pattern api-12345678.duosecurity.com | |
DirectoryServiceId: | |
Type: String | |
Description: > | |
ID of existing Directory Service (d-xxxxxxxxxx). | |
AllowedPattern: ^d\-[a-zA-Z0-9]{10,}$ | |
ConstraintDescription: > | |
Directory Service ID must match the pattern d-0123456789. | |
RadiusProxyServerCount: | |
Type: Number | |
Default: 2 | |
AllowedValues: | |
- 1 | |
- 2 | |
- 3 | |
- 4 | |
Description: > | |
Number of RADIUS proxy Fargate servers to create. | |
RadiusPortNumber: | |
Type: Number | |
Description: > | |
Port on which to listen for incoming RADIUS access requests. | |
Default: 1812 | |
ConstraintDescription: 'Must be in the range [1150-65535].' | |
MinValue: 1150 | |
MaxValue: 65535 | |
DuoFailMode: | |
Type: String | |
Description: > | |
After primary authentication succeeds, safe mode allows authentication attempts | |
if the Duo service cannot be contacted. Secure mode rejects authentication attempts | |
if the Duo service cannot be contacted. | |
AllowedValues: | |
- "safe" | |
- "secure" | |
Default: "safe" | |
DuoMaxCapacity: | |
Type: String | |
Description: > | |
Maximum number of tasks that can be launched by ECS Application Auto Scaling. | |
Default: 4 | |
AllowedValues: | |
- 4 | |
- 5 | |
- 6 | |
- 7 | |
- 8 | |
- 9 | |
- 10 | |
QSS3BucketName: | |
AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ | |
ConstraintDescription: The Quick Start bucket name can include numbers, lowercase | |
letters, uppercase letters, and hyphens (-). It cannot start or end with a | |
hyphen (-). | |
Default: aws-quickstart | |
Description: Name of the S3 bucket for your copy of the Quick Start assets. | |
Keep the default name unless you are customizing the template. | |
Changing the name updates code references to point to a new Quick | |
Start location. This name can include numbers, lowercase letters, | |
uppercase letters, and hyphens, but do not start or end with a hyphen (-). | |
See https://aws-quickstart.github.io/option1.html. | |
Type: String | |
QSS3BucketRegion: | |
Default: 'us-east-1' | |
Description: 'AWS Region where the Quick Start S3 bucket (QSS3BucketName) is | |
hosted. Keep the default Region unless you are customizing the template. | |
Changing this Region updates code references to point to a new Quick Start location. | |
When using your own bucket, specify the Region. | |
See https://aws-quickstart.github.io/option1.html.' | |
Type: String | |
QSS3KeyPrefix: | |
AllowedPattern: ^[0-9a-zA-Z-/]*$ | |
ConstraintDescription: The Quick Start S3 key prefix can include numbers, lowercase letters, | |
uppercase letters, hyphens (-), and forward slashes (/). The prefix should | |
end with a forward slash (/). | |
Default: quickstart-duo-mfa/ | |
Description: S3 key prefix that is used to simulate a directory for your copy of the | |
Quick Start assets. Keep the default prefix unless you are customizing | |
the template. Changing this prefix updates code references to point to | |
a new Quick Start location. This prefix can include numbers, lowercase | |
letters, uppercase letters, hyphens (-), and forward slashes (/). End with | |
a forward slash. See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html | |
and https://aws-quickstart.github.io/option1.html. | |
Type: String | |
#----------------------------------------------------------- | |
# Conditions | |
#----------------------------------------------------------- | |
Conditions: | |
NoKmsAdmin: !Equals | |
- !Ref AdminArn | |
- '' | |
#----------------------------------------------------------- | |
# Resources | |
#----------------------------------------------------------- | |
Resources: | |
#-------------------------------------------------- | |
# IAM role used by the bootstrapping Lambda function | |
# to retrieve the ID of the directory service. | |
#-------------------------------------------------- | |
GetDirectoryServiceMfaSettingsRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: !Sub GetDirectoryServiceMfaSettingsRole-${DirectoryServiceId} | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: [lambda.amazonaws.com] | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' | |
- !Ref DuoKmsIamPolicy | |
Policies: | |
- PolicyName: DescribeDirectoryServices | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Action: | |
- ds:DescribeDirectories | |
Resource: "*" | |
#-------------------------------------------------- | |
# This custom Lambda function will retrieve the | |
# details of the directory service. | |
#-------------------------------------------------- | |
GetDirectoryServiceFunction: | |
Type: AWS::Lambda::Function | |
Properties: | |
Description: Look up Directory Service | |
Handler: index.lambda_handler | |
KmsKeyArn: !GetAtt DuoKmsKey.Arn | |
Role: !GetAtt GetDirectoryServiceMfaSettingsRole.Arn | |
Runtime: python3.7 | |
Timeout: 60 | |
Tags: | |
- Key: duo:DirectoryServiceId | |
Value: !Ref DirectoryServiceId | |
Code: | |
ZipFile: | | |
import boto3 | |
import json | |
import cfnresponse | |
def lambda_handler(event, context): | |
print (json.dumps(event)) | |
if 'RequestType' in event and 'Delete' in event['RequestType']: | |
cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, '') | |
elif (event['RequestType'] == 'Create') or (event['RequestType'] == 'Update'): | |
print(event['RequestType'] + ' event proceeding to get ips') | |
try: | |
directory_id = event['ResourceProperties']['directory_id'] | |
directories = boto3.client('ds').describe_directories(DirectoryIds = [directory_id])['DirectoryDescriptions'] | |
directory = directories[0] | |
network = '' | |
if directory['Type'] == 'ADConnector': | |
network = 'ConnectSettings' | |
ips = directory['ConnectSettings']['ConnectIps'] | |
elif directory['Type'] == 'MicrosoftAD': | |
network = 'VpcSettings' | |
ips = directory['DnsIpAddrs'] | |
responseData = {} | |
responseData['VpcId'] = directory[network]['VpcId'] | |
responseData['SecurityGroupId'] = directory[network]['SecurityGroupId'] | |
responseData['SubnetId1'] = directory[network]['SubnetIds'][0] | |
responseData['SubnetId2'] = directory[network]['SubnetIds'][1] | |
responseData['DsIp1'] = ips[0] | |
responseData['DsIp2'] = ips[1] | |
# print(reponseData) | |
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, '') | |
except Exception as e: | |
print(e) | |
cfnresponse.send(event, context, cfnresponse.FAILED, responseData, '') | |
#-------------------------------------------------- | |
# CloudFormation uses this custom resource to invoke | |
# the Lambda function to look up the ID of the | |
# directory service. | |
#------------------------------------------------ | |
GetDirectoryServiceDetails: | |
Type: Custom::GetDirectoryService | |
Properties: | |
ServiceToken: !GetAtt GetDirectoryServiceFunction.Arn | |
directory_id: !Ref DirectoryServiceId | |
toggler: '2' | |
#------------------------------------------------ | |
# Stores the Duo configuration data as a Secrets | |
# Manager secret value and schedules periodic | |
# shares secret rotation. | |
#------------------------------------------------ | |
DuoConfigurationSettingsSecret: | |
Type: AWS::SecretsManager::Secret | |
Properties: | |
Name: !Sub DuoConfigurationSettings-${DirectoryServiceId} | |
Description: Duo configuration settings | |
KmsKeyId: !GetAtt DuoKmsKey.Arn | |
GenerateSecretString: | |
SecretStringTemplate: !Sub | | |
{ | |
"DuoSecretKey":"${DuoSecretKey}", | |
"DuoIntegrationKey":"${DuoIntegrationKey}", | |
"DuoApiHostName":"${DuoApiHostName}", | |
"DirectoryIntegrationKey":"${DirectoryIntegrationKey}", | |
"DirectorySecretKey":"${DirectorySecretKey}", | |
"adReadOnlyUser":"${adReadOnlyUser}", | |
"adReadOnlyPassword":"${adReadOnlyPassword}" | |
} | |
GenerateStringKey: RadiusSharedSecret | |
PasswordLength: 25 | |
# Do not include the following characters. | |
ExcludeCharacters: '"=,' | |
Tags: | |
- Key: duo:DirectoryServiceId | |
Value: !Ref DirectoryServiceId | |
#------------------------------------------------ | |
# Automatically rotate Secrets Manager secret | |
# every 7 days | |
#------------------------------------------------ | |
DuoConfigurationSettingsSecretRotationSchedule: | |
Type: AWS::SecretsManager::RotationSchedule | |
DependsOn: DuoConfigurationSettingsSecretRotationLambdaInvokePermission | |
Properties: | |
SecretId: !Ref DuoConfigurationSettingsSecret | |
RotationLambdaARN: !GetAtt RotateRadiusSharedSecretFunction.Arn | |
RotationRules: | |
AutomaticallyAfterDays: 7 | |
#------------------------------------------------ | |
# Allow Lambda to rotate secrets | |
#------------------------------------------------ | |
DuoConfigurationSettingsSecretRotationLambdaInvokePermission: | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: !Ref RotateRadiusSharedSecretFunction | |
Action: lambda:InvokeFunction | |
Principal: secretsmanager.amazonaws.com | |
#------------------------------------------------- | |
# Create the CloudWatch Log resource for logging | |
# by the Duo RADIUS service | |
#------------------------------------------------- | |
RadiusProxyCloudWatchLogsGroup: | |
Type: AWS::Logs::LogGroup | |
Properties: | |
LogGroupName: !Sub RadiusProxyLogs-${DirectoryServiceId}/authproxy.log | |
RetentionInDays: 30 | |
#------------------------------------------------- | |
# Create the Elastic Container Service Fargate | |
# Cluster used by the Duo RADIUS service | |
#------------------------------------------------- | |
DuoCluster: | |
Type: AWS::ECS::Cluster | |
Properties: | |
ClusterSettings: | |
- Name: containerInsights | |
Value: enabled | |
#------------------------------------------------- | |
# Create the Elastic Container Service Fargate | |
# Task Definition used by the Duo RADIUS service | |
#------------------------------------------------- | |
DuoTaskDefinition: | |
DependsOn: DuoSnsCustomResource | |
Type: AWS::ECS::TaskDefinition | |
Properties: | |
NetworkMode: awsvpc | |
RequiresCompatibilities: | |
- FARGATE | |
Cpu: '1024' | |
Memory: 2GB | |
Family: !Sub '${AWS::StackName}' | |
ExecutionRoleArn: !GetAtt DuoTaskRoleArn.Arn | |
TaskRoleArn: !GetAtt DuoTaskRoleArn.Arn | |
ContainerDefinitions: | |
- Name: DuoAuthProxy | |
Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepoName}' | |
PortMappings: | |
- ContainerPort: !Ref RadiusPortNumber | |
HostPort: !Ref RadiusPortNumber | |
Protocol: udp | |
- ContainerPort: 80 | |
HostPort: 80 | |
Protocol: tcp | |
# Set plain text environment variables for the container | |
Environment: | |
- Name: DIRECTORY_IP1 | |
Value: !GetAtt GetDirectoryServiceDetails.DsIp1 | |
- Name: DIRECTORY_IP2 | |
Value: !GetAtt GetDirectoryServiceDetails.DsIp2 | |
- Name: DUO_FAIL_MODE | |
Value: !Ref DuoFailMode | |
- Name: RADIUS_PORT_NUMBER | |
Value: !Ref RadiusPortNumber | |
- Name: AD_SYNC | |
Value: !Ref AdSync | |
# Set secret environment variable for the container | |
Secrets: | |
- Name: DuoSecret | |
ValueFrom: !Ref DuoConfigurationSettingsSecret | |
LogConfiguration: | |
LogDriver: awslogs | |
Options: | |
awslogs-region: !Ref AWS::Region | |
awslogs-group: !Ref RadiusProxyCloudWatchLogsGroup | |
awslogs-stream-prefix: ecs | |
#------------------------------------------------- | |
# Create the Elastic Container Service Fargate | |
# IAM role used by the Duo RADIUS tasks | |
#------------------------------------------------- | |
DuoTaskRoleArn: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: ecs-tasks.amazonaws.com | |
Action: 'sts:AssumeRole' | |
ManagedPolicyArns: | |
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy' | |
- !Ref DuoKmsIamPolicy | |
Policies: | |
- PolicyName: GetSecrets | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Action: | |
- secretsmanager:GetSecretValue | |
Resource: !Ref DuoConfigurationSettingsSecret | |
#------------------------------------------------- | |
# Create the Elastic Container Service Fargate | |
# Service used by the Duo RADIUS | |
#------------------------------------------------- | |
DuoService: | |
Type: AWS::ECS::Service | |
Properties: | |
Cluster: !Ref DuoCluster | |
DeploymentConfiguration: | |
MaximumPercent: 400 | |
MinimumHealthyPercent: 100 | |
DesiredCount: !Ref RadiusProxyServerCount | |
LaunchType: FARGATE | |
NetworkConfiguration: | |
AwsvpcConfiguration: | |
SecurityGroups: | |
- !Ref DuoServiceSg | |
Subnets: | |
- !GetAtt GetDirectoryServiceDetails.SubnetId1 | |
- !GetAtt GetDirectoryServiceDetails.SubnetId2 | |
TaskDefinition: !Ref DuoTaskDefinition | |
#------------------------------------------------- | |
# Create the Security Group used by Fargate | |
# for Duo ECS tasks | |
#------------------------------------------------- | |
DuoServiceSg: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
# GroupName: Duo_RADIUS_proxies | |
GroupDescription: Duo RADIUS proxies | |
VpcId: !GetAtt GetDirectoryServiceDetails.VpcId | |
SecurityGroupIngress: | |
- IpProtocol: udp | |
FromPort: !Ref RadiusPortNumber | |
ToPort: !Ref RadiusPortNumber | |
SourceSecurityGroupId: !GetAtt GetDirectoryServiceDetails.SecurityGroupId | |
Description: Allows UDP from Directory Service domain controllers | |
SecurityGroupEgress: | |
- IpProtocol: "-1" | |
FromPort: 0 | |
ToPort: 65535 | |
CidrIp: 0.0.0.0/0 | |
Description: Allow all outbound traffic | |
#------------------------------------------------- | |
# Create the Security Group Egress used by Directory | |
# to talk back to Duo ECS service | |
#------------------------------------------------- | |
DirectoryEgressRule: | |
Type: AWS::EC2::SecurityGroupEgress | |
Properties: | |
IpProtocol: udp | |
FromPort: !Ref RadiusPortNumber | |
ToPort: !Ref RadiusPortNumber | |
DestinationSecurityGroupId: !GetAtt DuoServiceSg.GroupId | |
GroupId: !GetAtt GetDirectoryServiceDetails.SecurityGroupId | |
#------------------------------------------------- | |
# Create the CloudWatch Event Rule for | |
# Duo ECS Service | |
#------------------------------------------------- | |
DuoServiceEvents: | |
Type: AWS::Events::Rule | |
Properties: | |
Description: "EventRule" | |
EventPattern: | |
source: | |
- "aws.ecs" | |
detail-type: | |
- "ECS Service Action" | |
detail: | |
clusterArn: | |
- !GetAtt DuoCluster.Arn | |
State: "ENABLED" | |
Targets: | |
- | |
Arn: !GetAtt ProcessDuoServiceFunction.Arn | |
Id: "TargetFunctionV1" | |
#------------------------------------------------- | |
# Allow the CloudWatch Event Rule for | |
# Duo ECS Service to trigger Lambda | |
#------------------------------------------------- | |
PermissionForEventsToInvokeLambda: | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: !Ref ProcessDuoServiceFunction | |
Action: "lambda:InvokeFunction" | |
Principal: "events.amazonaws.com" | |
SourceArn: !GetAtt DuoServiceEvents.Arn | |
#------------------------------------------------- | |
# Create an empty Systems Manager Parameter | |
# for Duo ECS tasks IP adresses | |
#------------------------------------------------- | |
DuoServiceIps: | |
Type: AWS::SSM::Parameter | |
Properties: | |
Type: String | |
Value: default | |
#------------------------------------------------- | |
# Create an IAM role for processing events from | |
# Duo ECS Service | |
#------------------------------------------------- | |
ProcessDuoServiceEventsRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: [lambda.amazonaws.com] | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' | |
- !Ref DuoKmsIamPolicy | |
Policies: | |
- PolicyName: DuoEcsServiceDetails | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Action: | |
- ecs:ListTasks | |
- ecs:DescribeTasks | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- ssm:PutParameter | |
- ssm:GetParameter* | |
Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${DuoServiceIps}' | |
#-------------------------------------------------- | |
# This custom Lambda function will processing events | |
# from Duo ECS Service | |
#-------------------------------------------------- | |
ProcessDuoServiceFunction: | |
Type: AWS::Lambda::Function | |
Properties: | |
Description: | | |
Rotates RADIUS shared secret and updates running instances and directory | |
Handler: index.lambda_handler | |
KmsKeyArn: !GetAtt DuoKmsKey.Arn | |
Role: !GetAtt ProcessDuoServiceEventsRole.Arn | |
Runtime: python3.7 | |
Timeout: 900 | |
Environment: | |
Variables: | |
DuoService: !Ref DuoService | |
DuoCluster: !GetAtt DuoCluster.Arn | |
DuoSsm: !Ref DuoServiceIps | |
Tags: | |
- Key: duo:DirectoryServiceId | |
Value: !Ref DirectoryServiceId | |
Code: | |
ZipFile: | | |
import boto3 | |
import os | |
import json | |
ecs = boto3.client('ecs') | |
ssm = boto3.client('ssm') | |
os_srv = os.environ['DuoService'] | |
os_cluster = os.environ['DuoCluster'] | |
def lambda_handler(event, context): | |
duo_cluster = event['detail']['clusterArn'] | |
duo_service = event['resources'][0] | |
old_ips = ssm.get_parameter(Name=os.environ['DuoSsm'])['Parameter']['Value'] | |
if os_srv == duo_service: | |
if os_cluster == duo_cluster: | |
print('Cluster and service names match') | |
r = ecs.list_tasks(cluster=duo_cluster, serviceName=duo_service, desiredStatus='RUNNING', launchType='FARGATE') | |
s = ecs.describe_tasks(cluster=duo_cluster,tasks=r['taskArns']) | |
ip=[] | |
for task in s['tasks']: | |
for con in task['containers']: | |
for net in con['networkInterfaces']: | |
ip.append(net['privateIpv4Address']) | |
print('Task IP: '+ net['privateIpv4Address']) | |
ip_string = ','.join([str(elem) for elem in ip]) | |
if old_ips == ip_string: | |
print('There has been no change in the IP address. No action will be taken') | |
else: | |
print('Fargate task IP addresses have changed.') | |
ssm.put_parameter(Name=os.environ['DuoSsm'],Value=ip_string,Type='String',Overwrite=True) | |
else: | |
print('Cluster name does not match. Given: ' + os_cluster + ' observed: ' + duo_cluster) | |
else: | |
print('Service names do not match. Given: ' + os_srv + ' observed: ' + duo_service) | |
#-------------------------------------------------- | |
# Create an IAM role for | |
# Radius Shared Secret Rotation | |
#-------------------------------------------------- | |
RadiusSharedSecretRotationRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: [lambda.amazonaws.com] | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' | |
- !Ref DuoKmsIamPolicy | |
Policies: | |
- PolicyName: RotateDuoConfigurationSettingsSecret | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Action: | |
- ecs:UpdateService | |
Resource: !Sub | |
- 'arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:service/${DuoCluster}/${DuoServiceName}' | |
- DuoServiceName: !GetAtt DuoService.Name | |
- Effect: Allow | |
Action: | |
- secretsmanager:DescribeSecret | |
- secretsmanager:GetSecretValue | |
- secretsmanager:PutSecretValue | |
- secretsmanager:UpdateSecretVersionStage | |
Resource: !Ref DuoConfigurationSettingsSecret | |
- Effect: Allow | |
Action: | |
- secretsmanager:GetRandomPassword | |
Resource: "*" | |
#-------------------------------------------------- | |
# This custom Lambda function will rotate the | |
# radius shared secret | |
#-------------------------------------------------- | |
RotateRadiusSharedSecretFunction: | |
Type: AWS::Lambda::Function | |
Properties: | |
Description: | | |
Rotates RADIUS shared secret and updates running instances and directory | |
Handler: index.lambda_handler | |
KmsKeyArn: !GetAtt DuoKmsKey.Arn | |
Role: !GetAtt RadiusSharedSecretRotationRole.Arn | |
Runtime: python3.7 | |
Timeout: 900 | |
Environment: | |
Variables: | |
PasswordLength: 25 | |
ExcludeCharacters: '"=,' | |
RunDocumentTagName: tag:duo:DirectoryServiceId | |
RunDocumentTagValue: !Ref DirectoryServiceId | |
# func_arn: !GetAtt UpdateDirectoryServiceMfaSettings.Arn | |
DuoService: !GetAtt DuoService.Name | |
DuoCluster: !Ref DuoCluster | |
Tags: | |
- Key: duo:DirectoryServiceId | |
Value: !Ref DirectoryServiceId | |
Code: | |
ZipFile: | | |
import boto3 | |
import os | |
import json | |
import time | |
secretsmanager_client = boto3.client('secretsmanager') | |
ssm_client = boto3.client('ssm') | |
lambda_client = boto3.client('lambda') | |
ecs = boto3.client('ecs') | |
srv = os.environ['DuoService'] | |
duo_cluster = os.environ['DuoCluster'] | |
# func = os.environ['func_arn'] | |
def lambda_handler(event, context): | |
arn = event['SecretId'] | |
token = event['ClientRequestToken'] | |
step = event['Step'] | |
if step == "createSecret": | |
create_secret(secretsmanager_client, arn, token) | |
elif step == "finishSecret": | |
finish_secret(secretsmanager_client, arn, token) | |
ecs.update_service(cluster=duo_cluster,service=srv, forceNewDeployment=True) | |
def create_secret(secretsmanager_client, arn, token): | |
# Get the secret | |
secret = json.loads(secretsmanager_client.get_secret_value(SecretId=arn, VersionStage="AWSCURRENT")['SecretString']) | |
# Generate a random password | |
r = secretsmanager_client.get_random_password(PasswordLength=int(os.environ['PasswordLength']),ExcludeCharacters=os.environ['ExcludeCharacters']) | |
secret['RadiusSharedSecret'] = r['RandomPassword'] | |
# Put the secret | |
secretsmanager_client.put_secret_value(SecretId=arn, ClientRequestToken=token, SecretString=json.dumps(secret), VersionStages=['AWSPENDING']) | |
print("createSecret: Successfully put secret for ARN %s and version %s." % (arn, token)) | |
def finish_secret(secretsmanager_client, arn, token): | |
# First describe the secret to get the current version | |
metadata = secretsmanager_client.describe_secret(SecretId=arn) | |
current_version = None | |
for version in metadata["VersionIdsToStages"]: | |
if "AWSCURRENT" in metadata["VersionIdsToStages"][version]: | |
if version == token: | |
print("finishSecret: Version %s already marked as AWSCURRENT for %s" % (version, arn)) | |
return | |
current_version = version | |
break | |
# Finalize by staging the secret version current | |
secretsmanager_client.update_secret_version_stage(SecretId=arn, VersionStage="AWSCURRENT", MoveToVersionId=token, RemoveFromVersionId=current_version) | |
print("finishSecret: Successfully set AWSCURRENT stage to version %s for secret %s." % (version, arn)) | |
#-------------------------------------------------- | |
# Create CloudWatch Event Rule to trigger when | |
# System Manager Parameter changes | |
#-------------------------------------------------- | |
UpdateDirectoryServiceEvent: | |
Type: AWS::Events::Rule | |
Properties: | |
EventPattern: | |
source: | |
- "aws.ssm" | |
detail-type: | |
- "Parameter Store Change" | |
detail: | |
name: | |
- !Ref DuoServiceIps | |
State: "ENABLED" | |
Targets: | |
- | |
Arn: !GetAtt UpdateDirectoryServiceMfaSettings.Arn | |
Id: "TargetFunctionV1" | |
#-------------------------------------------------- | |
# Allow CloudWatch Event Rule to trigger Lambda | |
# System Manager Parameter changes | |
#-------------------------------------------------- | |
UpdateDirectoryPermissionForEventsToInvokeLambda: | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: !Ref UpdateDirectoryServiceMfaSettings | |
Action: "lambda:InvokeFunction" | |
Principal: "events.amazonaws.com" | |
SourceArn: !GetAtt UpdateDirectoryServiceEvent.Arn | |
#-------------------------------------------------- | |
# IAM role used by the Lambda function to update the | |
# Directory Service MFA settings. | |
#-------------------------------------------------- | |
UpdateDirectoryServiceMfaSettingsRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: [lambda.amazonaws.com] | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' | |
- !Ref DuoKmsIamPolicy | |
Policies: | |
- PolicyName: UpdateDirectoryServiceMfaSettings | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Action: | |
- ds:DescribeDirectories | |
- ds:DisableRadius | |
- ds:EnableRadius | |
- ds:UpdateRadius | |
Resource: "*" | |
- Effect: Allow | |
Action: | |
- secretsmanager:GetSecretValue | |
Resource: !Ref DuoConfigurationSettingsSecret | |
- Effect: Allow | |
Action: | |
- ssm:GetParameter | |
Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${DuoServiceIps}' | |
- Effect: Allow | |
Action: | |
- sns:Publish | |
Resource: !Ref DuoNotification | |
#-------------------------------------------------- | |
# Create a notification for Duo related events | |
#-------------------------------------------------- | |
DuoNotification: | |
Type: AWS::SNS::Topic | |
Properties: | |
KmsMasterKeyId: !GetAtt DuoKmsKey.Arn | |
Subscription: | |
- Endpoint: !Ref NotificationEmail | |
Protocol: email | |
#-------------------------------------------------- | |
# This Lambda function will update the directory | |
# service MFA settings. | |
#-------------------------------------------------- | |
UpdateDirectoryServiceMfaSettings: | |
Type: AWS::Lambda::Function | |
Properties: | |
Description: Update the Directory Service MFA settings. | |
Handler: index.lambda_handler | |
KmsKeyArn: !GetAtt DuoKmsKey.Arn | |
Role: !GetAtt UpdateDirectoryServiceMfaSettingsRole.Arn | |
Runtime: python3.7 | |
Timeout: 900 | |
Environment: | |
Variables: | |
# The ARN of the Secrets Manager secret containing the RADIUS shared | |
# secret is passed to this function by the Secrets Manager rotation | |
# feature. | |
ds_id: !Ref DirectoryServiceId | |
proxy_port: !Ref RadiusPortNumber | |
rs_arn: !Ref DuoConfigurationSettingsSecret | |
DuoSsm: !Ref DuoServiceIps | |
Topic: !Ref DuoNotification | |
Tags: | |
- Key: duo:DirectoryServiceId | |
Value: !Ref DirectoryServiceId | |
Code: | |
ZipFile: | | |
import os | |
import boto3 | |
import json | |
import time | |
from enum import Enum | |
class RadiusStatus(Enum): | |
Creating = 1 | |
Completed = 2 | |
Failed = 3 | |
NotConfigured = 4 | |
RADIUS_TIMEOUT = 5 | |
RADIUS_RETRIES = 2 | |
RADIUS_AUTHENTICATION_PROTOCOL = 'PAP' | |
ds_client = boto3.client('ds') | |
sc = boto3.client('secretsmanager') | |
ssm =boto3.client('ssm') | |
sns = boto3.client('sns') | |
ds_id = os.environ['ds_id'] | |
ips = ssm.get_parameter(Name=os.environ['DuoSsm'])['Parameter']['Value'] | |
def lambda_handler(event, context): | |
print(json.dumps(event)) | |
print('Directory Service Id: {}'.format(ds_id)) | |
print('Fargate IP Addresses : {}'.format(ips)) | |
enable_radius(ds_id, ips) | |
def enable_radius(ds_id, nlb): | |
port = int(os.environ['proxy_port']) | |
rs = get_rs(os.environ['rs_arn']) | |
radius_settings = { | |
"RadiusServers": [nlb], | |
"RadiusPort": port, | |
"RadiusTimeout": RADIUS_TIMEOUT, | |
"RadiusRetries": RADIUS_RETRIES, | |
"SharedSecret": rs, | |
"AuthenticationProtocol": RADIUS_AUTHENTICATION_PROTOCOL, | |
"DisplayLabel": "Duo MFA" | |
} | |
# Determine whether RADIUS has been configured. | |
radius_current = radius_status(ds_id) | |
print('Current RADIUS status: {}.'.format(radius_current)) | |
# Enable RADIUS. | |
if radius_current in [RadiusStatus.NotConfigured, RadiusStatus.Failed]: | |
# Enable the RADIUS settings for this directory. | |
print('Enabling RADIUS configuration...') | |
r = ds_client.enable_radius( | |
DirectoryId = ds_id, | |
RadiusSettings = radius_settings | |
) | |
# Update RADIUS. | |
elif radius_current == RadiusStatus.Completed: | |
# Update the RADIUS settings for this directory. | |
print('Updating RADIUS configuration...') | |
r = ds_client.update_radius( | |
DirectoryId = ds_id, | |
RadiusSettings = radius_settings | |
) | |
# Now get the status; updating the directory service is asynchronous. | |
MAX_ATTEMPTS = 40 | |
SLEEP_TIME = 15 | |
attempt_number = 1 | |
while attempt_number <= MAX_ATTEMPTS: | |
r = ds_client.describe_directories(DirectoryIds=[ds_id])['DirectoryDescriptions'][0] | |
print("** ATTEMPT {}: {}".format(attempt_number, r['RadiusStatus'])) | |
if r['RadiusStatus'] == 'Completed': | |
print('Radius updated successfully') | |
break | |
elif r['RadiusStatus'] == 'Failed': | |
print('Radius creation failed') | |
break | |
else: | |
time.sleep(SLEEP_TIME) | |
attempt_number +=1 | |
if attempt_number == MAX_ATTEMPTS: | |
print('Radius create/update timed out') | |
msg = 'Duo MFA update failed on '+ ds_id +'. Please check.' | |
sns.publish(TopicArn=os.environ['Topic'], Message=msg, Subject='Failed to update MFA') | |
def radius_status(ds_id): | |
return_value = -1 | |
r = ds_client.describe_directories(DirectoryIds=[ds_id])['DirectoryDescriptions'][0] | |
if 'RadiusStatus' not in r: | |
return_value = RadiusStatus.NotConfigured | |
elif r['RadiusStatus'] == 'Completed': | |
return_value = RadiusStatus.Completed | |
elif r['RadiusStatus'] == 'Failed': | |
return_value = RadiusStatus.Failed | |
elif r['RadiusStatus'] == 'Creating': | |
return_value = RadiusStatus.Creating | |
return return_value | |
def get_rs(rs_arn): | |
rs = '' | |
r = sc.get_secret_value( | |
SecretId = rs_arn | |
) | |
if 'SecretString' in r: | |
rs = json.loads(r['SecretString'])['RadiusSharedSecret'] | |
return rs | |
#-------------------------------------------------- | |
# Create an Application Auto Scaling scalable target | |
# for Duo ECS service | |
#-------------------------------------------------- | |
DuoEcsScalableTarget: | |
Type: AWS::ApplicationAutoScaling::ScalableTarget | |
Properties: | |
MaxCapacity: !Ref DuoMaxCapacity | |
MinCapacity: !Ref RadiusProxyServerCount | |
ResourceId: !Sub | |
- 'service/${DuoCluster}/${DuoServiceName}' | |
- DuoServiceName: !GetAtt DuoService.Name | |
RoleARN: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService' | |
ScalableDimension: ecs:service:DesiredCount | |
ServiceNamespace: ecs | |
#-------------------------------------------------- | |
# Create an Application Auto Scaling policy | |
# for Duo ECS service based on CPU | |
#-------------------------------------------------- | |
DuoServiceScalingPolicyCpu: | |
Type: AWS::ApplicationAutoScaling::ScalingPolicy | |
Properties: | |
PolicyName: !Sub ${AWS::StackName}-target-tracking-cpu70 | |
PolicyType: TargetTrackingScaling | |
ScalingTargetId: !Ref DuoEcsScalableTarget | |
TargetTrackingScalingPolicyConfiguration: | |
TargetValue: 70.0 | |
ScaleInCooldown: 180 | |
ScaleOutCooldown: 60 | |
PredefinedMetricSpecification: | |
PredefinedMetricType: ECSServiceAverageCPUUtilization | |
#-------------------------------------------------- | |
# Create an Application Auto Scaling policy | |
# for Duo ECS service based on Memory (RAM) | |
#-------------------------------------------------- | |
DuoServiceScalingPolicyMem: | |
Type: AWS::ApplicationAutoScaling::ScalingPolicy | |
Properties: | |
PolicyName: !Sub ${AWS::StackName}-target-tracking-mem80 | |
PolicyType: TargetTrackingScaling | |
ScalingTargetId: !Ref DuoEcsScalableTarget | |
TargetTrackingScalingPolicyConfiguration: | |
TargetValue: 80.0 | |
ScaleInCooldown: 180 | |
ScaleOutCooldown: 60 | |
PredefinedMetricSpecification: | |
PredefinedMetricType: ECSServiceAverageMemoryUtilization | |
#-------------------------------------------------- | |
# Create an alarm for Duo ECS service CPU | |
# will send email when CPU > 90 | |
#-------------------------------------------------- | |
DuoServiceAlarmCpu: | |
Type: AWS::CloudWatch::Alarm | |
Properties: | |
AlarmDescription: CPU alarm for DuoService | |
AlarmActions: | |
- !Ref DuoNotification | |
MetricName: CPUUtilization | |
Namespace: AWS/ECS | |
Statistic: Average | |
Period: 60 | |
EvaluationPeriods: 3 | |
Threshold: 90.0 | |
ComparisonOperator: GreaterThanThreshold | |
Dimensions: | |
- Name: ServiceName | |
Value: !GetAtt DuoService.Name | |
- Name: ClusterName | |
Value: !Ref DuoCluster | |
#-------------------------------------------------- | |
# Create an alarm for Duo ECS service CPU | |
# will send email when Memory > 90 | |
#-------------------------------------------------- | |
DuoServiceAlarmMemory: | |
Type: AWS::CloudWatch::Alarm | |
Properties: | |
AlarmDescription: Memory alarm for DuoService | |
AlarmActions: | |
- !Ref DuoNotification | |
MetricName: MemoryUtilization | |
Namespace: AWS/ECS | |
Statistic: Average | |
Period: 60 | |
EvaluationPeriods: 3 | |
Threshold: 90.0 | |
ComparisonOperator: GreaterThanThreshold | |
Dimensions: | |
- Name: ServiceName | |
Value: !GetAtt DuoService.Name | |
- Name: ClusterName | |
Value: !Ref DuoCluster | |
#-------------------------------------------------- | |
# Create S3 Bucket for CodePipeline Artifacts | |
#-------------------------------------------------- | |
ArtifactBucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
BucketEncryption: | |
ServerSideEncryptionConfiguration: | |
- ServerSideEncryptionByDefault: | |
SSEAlgorithm: 'aws:kms' | |
KMSMasterKeyID: !GetAtt DuoKmsKey.Arn | |
#-------------------------------------------------- | |
# Create an ECR repository for DuoAuthProxy | |
# Set a lifecycle policy to retain based on input | |
#-------------------------------------------------- | |
DuoEcrRepository: | |
Type: AWS::ECR::Repository | |
Properties: | |
RepositoryName: !Ref EcrRepoName | |
ImageScanningConfiguration: | |
ScanOnPush: "true" | |
LifecyclePolicy: | |
LifecyclePolicyText: !Sub | | |
{ | |
"rules": [{ | |
"rulePriority": 1, | |
"description": "remove older images", | |
"selection": { | |
"tagStatus": "any", | |
"countType": "sinceImagePushed", | |
"countUnit": "days", | |
"countNumber": ${EcrImageRetention} | |
}, | |
"action": { | |
"type": "expire" | |
} | |
}] | |
} | |
#-------------------------------------------------- | |
# Create CloudWatch Event on Duo Pipeline | |
# will send an email to admins if Pipeline fails | |
#-------------------------------------------------- | |
DuoEcrPipelineEvents: | |
Type: 'AWS::Events::Rule' | |
Properties: | |
Description: EventRule | |
EventPattern: | |
source: | |
- aws.codepipeline | |
detail-type: | |
- CodePipeline Pipeline Execution State Change | |
detail: | |
state: | |
- FAILED | |
pipeline: | |
- !Ref DuoEcrPipeline | |
State: ENABLED | |
Targets: | |
- Arn: !Ref DuoNotification | |
Id: DuoEcrPipeline | |
#-------------------------------------------------- | |
# Allow CloudWatch permissions to send emails | |
#-------------------------------------------------- | |
DuoNotificationPolicy: | |
Type: 'AWS::SNS::TopicPolicy' | |
Properties: | |
PolicyDocument: | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: events.amazonaws.com | |
Action: 'sns:Publish' | |
Resource: '*' | |
Topics: | |
- !Ref DuoNotification | |
#check | |
DuoEcrCodeCommitRepo: | |
Type: AWS::CodeCommit::Repository | |
DependsOn: DuoCleanupCustomResource | |
Properties: | |
RepositoryName: !Ref CodeCommitRepoName | |
RepositoryDescription: CodeCommit Repo with code for building DuoAuthentication Proxy ECR images | |
Code: | |
BranchName: !Ref CodeCommitBranchName | |
S3: | |
Bucket: !Ref ArtifactBucket | |
Key: !Sub ${QSS3KeyPrefix}scripts/packages/code_commit.zip | |
#-------------------------------------------------- | |
# Create an IAM role for Duo CodePipeline | |
# grant access to CodeCommit. CodeBuild and KMS | |
#-------------------------------------------------- | |
DuoCodePipelineRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
Effect: Allow | |
Principal: | |
Service: codepipeline.amazonaws.com | |
Action: sts:AssumeRole | |
Path: / | |
ManagedPolicyArns: | |
- !Sub "arn:${AWS::Partition}:iam::aws:policy/AWSCodeCommitReadOnly" | |
- !Ref DuoCodeBuildPolicy | |
- !Ref DuoKmsIamPolicy | |
#-------------------------------------------------- | |
# Create an IAM policy for CodeBuild | |
#-------------------------------------------------- | |
DuoCodeBuildPolicy: | |
Type: AWS::IAM::ManagedPolicy | |
Properties: | |
ManagedPolicyName: !Sub '${AWS::StackName}-${AWS::Region}-codebuild' | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- codebuild:StartBuild | |
- codebuild:BatchGetBuilds | |
Effect: Allow | |
Resource: | |
- !Sub 'arn:${AWS::Partition}:codebuild:${AWS::Region}:${AWS::AccountId}:project/${AWS::StackName}' | |
- Action: | |
- codecommit:UploadArchive | |
Effect: Allow | |
Resource: | |
- !GetAtt DuoEcrCodeCommitRepo.Arn | |
- Action: | |
- s3:GetObject | |
- s3:PutObject | |
Effect: Allow | |
Resource: !Sub 'arn:${AWS::Partition}:s3:::${ArtifactBucket}/*' | |
- Effect: Allow | |
Resource: | |
- !Sub 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*' | |
- !Sub 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*:*' | |
Action: | |
- logs:CreateLogGroup | |
- logs:CreateLogStream | |
- logs:PutLogEvents | |
#-------------------------------------------------- | |
# Create an IAM Role for CodeBuild | |
#-------------------------------------------------- | |
DuoCodeBuildRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
Effect: Allow | |
Principal: | |
Service: codebuild.amazonaws.com | |
Action: sts:AssumeRole | |
Path: / | |
ManagedPolicyArns: | |
- !Ref DuoCodeBuildPolicy | |
- !Ref DuoKmsIamPolicy | |
Policies: | |
- PolicyName: GetEcrAccess | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Action: | |
- ecr:GetAuthorizationToken | |
Resource: "*" | |
- Effect: Allow | |
Action: | |
- ecr:GetDownloadUrlForLayer | |
- ecr:BatchGetImage | |
- ecr:UploadLayerPart | |
- ecr:InitiateLayerUpload | |
- ecr:BatchCheckLayerAvailability | |
- ecr:PutImage | |
- ecr:CompleteLayerUpload | |
- ecr:DescribeImageScanFindings | |
Resource: !GetAtt DuoEcrRepository.Arn | |
DuoCodeBuildEcr: | |
Type: 'AWS::CodeBuild::Project' | |
Properties: | |
Artifacts: | |
Type: CODEPIPELINE | |
Description: Build and deploy Duo AuthProxy containers | |
EncryptionKey: !GetAtt DuoKmsKey.Arn | |
Environment: | |
ComputeType: BUILD_GENERAL1_SMALL | |
EnvironmentVariables: | |
- Name: ECR_REPO_NAME | |
Type: PLAINTEXT | |
Value: !Ref DuoEcrRepository | |
- Name: AWS_ACCOUNT_NUMBER | |
Type: PLAINTEXT | |
Value: !Ref 'AWS::AccountId' | |
Image: 'aws/codebuild/standard:3.0' | |
Type: LINUX_CONTAINER | |
PrivilegedMode: true | |
Name: !Sub '${AWS::StackName}' | |
ServiceRole: !GetAtt DuoCodeBuildRole.Arn | |
Source: | |
BuildSpec: buildspec.yaml | |
Type: CODEPIPELINE | |
TimeoutInMinutes: 480 | |
#-------------------------------------------------- | |
# Create CodeBuild Project | |
# for building AuthProxy ECR | |
#-------------------------------------------------- | |
DuoEcrPipeline: | |
Type: 'AWS::CodePipeline::Pipeline' | |
DependsOn: | |
- DuoCodeBuildPolicy | |
- DuoKmsIamPolicy | |
Properties: | |
ArtifactStore: | |
Location: !Ref ArtifactBucket | |
Type: S3 | |
RoleArn: !GetAtt DuoCodePipelineRole.Arn | |
Stages: | |
- Actions: | |
- ActionTypeId: | |
Category: Source | |
Owner: AWS | |
Provider: CodeCommit | |
Version: '1' | |
Configuration: | |
BranchName: !Ref CodeCommitBranchName | |
PollForSourceChanges: 'false' | |
RepositoryName: !GetAtt DuoEcrCodeCommitRepo.Name | |
Name: SourceAction | |
OutputArtifacts: | |
- Name: AppSource | |
RunOrder: 1 | |
Name: Source | |
- Actions: | |
- ActionTypeId: | |
Category: Build | |
Owner: AWS | |
Provider: CodeBuild | |
Version: '1' | |
Configuration: | |
ProjectName: !Ref DuoCodeBuildEcr | |
InputArtifacts: | |
- Name: AppSource | |
Name: BuildDuoAuthProxyEcr | |
RunOrder: 1 | |
Name: Build | |
#-------------------------------------------------- | |
# Create CloudWatch Event which will | |
# start Pipeline on commit to specific branch | |
# in CodeCommit | |
#-------------------------------------------------- | |
DuoEcrPipelineCloudWatchEventRole: | |
Type: 'AWS::IAM::Role' | |
Properties: | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: 'sts:AssumeRole' | |
Effect: Allow | |
Principal: | |
Service: | |
- events.amazonaws.com | |
Version: 2012-10-17 | |
Path: / | |
Policies: | |
- PolicyDocument: | |
Statement: | |
- Action: 'codepipeline:StartPipelineExecution' | |
Effect: Allow | |
Resource: !Sub >- | |
arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${DuoEcrPipeline} | |
Version: 2012-10-17 | |
PolicyName: 'duo-ecr-pipeline-trigger' | |
#-------------------------------------------------- | |
# Create CodeBuild Project | |
# for building AuthProxy ECR | |
#-------------------------------------------------- | |
DuoEcrPipelineCloudWatchEventRule: | |
Type: 'AWS::Events::Rule' | |
Properties: | |
Description: !Sub 'Amazon CloudWatch Rule which triggers the build for DuoAuthProxy ECR when the branch ${CodeCommitBranchName} is updated' | |
EventPattern: | |
detail: | |
event: | |
- referenceCreated | |
- referenceUpdated | |
referenceName: | |
- !Ref CodeCommitBranchName | |
referenceType: | |
- branch | |
detail-type: | |
- CodeCommit Repository State Change | |
resources: | |
- !GetAtt DuoEcrCodeCommitRepo.Arn | |
source: | |
- aws.codecommit | |
Targets: | |
- Arn: !Sub >- | |
arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${DuoEcrPipeline} | |
Id: !Ref DuoEcrPipeline | |
RoleArn: !GetAtt DuoEcrPipelineCloudWatchEventRole.Arn | |
#-------------------------------------------------- | |
# Create a weekly CloudWatch trigger | |
# for building AuthProxy ECR | |
#-------------------------------------------------- | |
DuoEcrPipelineWeeklyTrigger: | |
Type: AWS::Events::Rule | |
Properties: | |
Description: Amazon CloudWatch Rule which triggers the build for DuoAuthProxy ECR on a weekly basis | |
ScheduleExpression: !Sub 'cron(${EcrCronExpression})' | |
Targets: | |
- Arn: !Sub >- | |
arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${DuoEcrPipeline} | |
Id: !Ref DuoEcrPipeline | |
RoleArn: !GetAtt DuoEcrPipelineCloudWatchEventRole.Arn | |
#-------------------------------------------------- | |
# Create an SSM parameter | |
# to store Custom Resource Event | |
#-------------------------------------------------- | |
DuoCustomResourceEvent: | |
Type: AWS::SSM::Parameter | |
Properties: | |
Description: Duo SNS event response for Custom Resource backed by SNS | |
Type: String | |
Value: default | |
#-------------------------------------------------- | |
# Create an SNS topic for Custom Resource | |
#-------------------------------------------------- | |
DuoSnsCustomResourceTopic: | |
Type: AWS::SNS::Topic | |
Properties: | |
KmsMasterKeyId: !GetAtt DuoKmsKey.Arn | |
#-------------------------------------------------- | |
# Create CloudWatch Event on success/failure | |
# for SNS backed Custom Resource | |
#-------------------------------------------------- | |
DuoPipelineSnsEvents: | |
Type: 'AWS::Events::Rule' | |
Properties: | |
Description: EventRule | |
EventPattern: | |
source: | |
- aws.codepipeline | |
detail-type: | |
- CodePipeline Pipeline Execution State Change | |
detail: | |
state: | |
- FAILED | |
- SUCCEEDED | |
- CANCELED | |
pipeline: | |
- !Ref DuoEcrPipeline | |
State: ENABLED | |
Targets: | |
- Arn: !Ref DuoSnsCustomResourceTopic | |
Id: DuoSnsCustomResourceTopic | |
- Arn: !GetAtt DuoSnsCustomResourceLambda.Arn | |
Id: DuoSnsCustomResourceLambda | |
#-------------------------------------------------- | |
# Create SNS Subscription to Lambda | |
# from SNS backed Custom Resource | |
#-------------------------------------------------- | |
DuoSnsCustomResourceTopicSubscriptionLambda: | |
Type: AWS::SNS::Subscription | |
Properties: | |
Endpoint: !GetAtt DuoSnsCustomResourceLambda.Arn | |
Protocol: lambda | |
TopicArn: !Ref DuoSnsCustomResourceTopic | |
#-------------------------------------------------- | |
# Create SNS backed Custom Resource | |
#-------------------------------------------------- | |
DuoSnsCustomResource: | |
DependsOn: | |
- DuoSnsCustomResourcePermissionLambda | |
- DuoSnsCustomResourceTopicSubscriptionLambda | |
- DuoPipelineSnsEvents | |
Type: Custom::Poller | |
Properties: | |
ServiceToken: !Ref DuoSnsCustomResourceTopic | |
Pipeline: !Ref DuoEcrPipeline | |
#-------------------------------------------------- | |
# Allow SNS Custom Resource to invoke Lambda | |
#-------------------------------------------------- | |
DuoSnsCustomResourcePermissionLambda: | |
DependsOn: DuoSnsCustomResourceTopicSubscriptionLambda | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: !GetAtt DuoSnsCustomResourceLambda.Arn | |
Action: lambda:InvokeFunction | |
Principal: sns.amazonaws.com | |
SourceArn: !Ref DuoSnsCustomResourceTopic | |
#-------------------------------------------------- | |
# Allow CodePipeline events to invoke Lambda | |
#-------------------------------------------------- | |
DuoSnsCustomResourceEventsToInvokeLambda: | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: !GetAtt DuoSnsCustomResourceLambda.Arn | |
Action: lambda:InvokeFunction | |
Principal: events.amazonaws.com | |
SourceArn: !GetAtt DuoPipelineSnsEvents.Arn | |
#-------------------------------------------------- | |
# Create IAM role for Lambda | |
#-------------------------------------------------- | |
DuoSnsCustomResourceLambdaRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: [lambda.amazonaws.com] | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' | |
- !Ref DuoKmsIamPolicy | |
Policies: | |
- PolicyName: DuoSnsCustomResourceEvent | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Action: | |
- ssm:PutParameter | |
- ssm:GetParameter* | |
Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${DuoCustomResourceEvent}' | |
#-------------------------------------------------- | |
# Create Lambda function | |
# to parse events from SNS and CodePipeline | |
#-------------------------------------------------- | |
DuoSnsCustomResourceLambda: | |
Type: AWS::Lambda::Function | |
Properties: | |
Description: Gathers event stream from Custom Resource and CodePipeline | |
Handler: index.lambda_handler | |
KmsKeyArn: !GetAtt DuoKmsKey.Arn | |
Role: !GetAtt DuoSnsCustomResourceLambdaRole.Arn | |
Runtime: python3.7 | |
Timeout: 900 | |
Environment: | |
Variables: | |
SnsEvent: !Ref DuoCustomResourceEvent | |
Tags: | |
- Key: duo:DirectoryServiceId | |
Value: !Ref DirectoryServiceId | |
Code: | |
ZipFile: | | |
import json | |
import cfnresponse | |
import boto3 | |
import os | |
ssm = boto3.client('ssm') | |
def lambda_handler(event, context): | |
print(json.dumps(event)) | |
if 'Records' in event: | |
print('Message from SNS') | |
cfn_event = event['Records'][0]['Sns']['Message'] | |
cfn_json = json.loads(cfn_event) | |
try: | |
if cfn_json['RequestType'] == 'Create': | |
print('Create event, will place the event in SSM') | |
ssm.put_parameter(Name=os.environ['SnsEvent'],Value=cfn_event,Type='String',Overwrite=True) | |
else: | |
print('This is Update or Delete event, will send success') | |
cfnresponse.send(cfn_json, context, cfnresponse.SUCCESS, {}, '') | |
except: | |
cfnresponse.send(cfn_json, context, cfnresponse.FAILED, {}, '') | |
if 'source' in event: | |
if event['source'] == 'aws.codepipeline': | |
print('CodePipeline event, checkint the detail') | |
cfn_event = ssm.get_parameter(Name=os.environ['SnsEvent'])['Parameter']['Value'] | |
cfn_json = json.loads(cfn_event) | |
if event['detail']['state'] == 'SUCCEEDED': | |
print('Pipeline is succeded') | |
cfnresponse.send(cfn_json, context, cfnresponse.SUCCESS, {}, '') | |
else: | |
print('CodePipeline is failed or cancelled') | |
cfnresponse.send(cfn_json, context, cfnresponse.FAILED, {}, '') | |
#-------------------------------------------------- | |
# Create KMS Key for Duo AuthProxy | |
#-------------------------------------------------- | |
DuoKmsKey: | |
Type: AWS::KMS::Key | |
Properties: | |
Description: KMS key to encrypt all of Duo AuthProxy related resources | |
EnableKeyRotation: true | |
KeyPolicy: | |
Version: '2012-10-17' | |
Id: key-default-1 | |
Statement: | |
- Sid: Allow full access to key metadata to the root account | |
Effect: Allow | |
Principal: | |
AWS: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:root' | |
Action: | |
- kms:* | |
Resource: "*" | |
- !If | |
- NoKmsAdmin | |
- !Ref 'AWS::NoValue' | |
- | |
Sid: Allow administration of the key | |
Effect: Allow | |
Principal: | |
AWS: !Ref AdminArn | |
Action: | |
- kms:Create* | |
- kms:Describe* | |
- kms:Enable* | |
- kms:List* | |
- kms:Put* | |
- kms:Update* | |
- kms:Revoke* | |
- kms:Disable* | |
- kms:Get* | |
- kms:Delete* | |
- kms:ScheduleKeyDeletion | |
- kms:CancelKeyDeletion | |
Resource: '*' | |
- Sid: Allow AWS Services to use the key | |
Effect: Allow | |
Principal: | |
AWS: "*" | |
Action: | |
- kms:Encrypt | |
- kms:Decrypt | |
- kms:ReEncrypt* | |
- kms:GenerateDataKey* | |
- kms:CreateGrant | |
- kms:DescribeKey | |
Resource: "*" | |
Condition: | |
StringEquals: | |
kms:CallerAccount: !Ref 'AWS::AccountId' | |
kms:ViaService: | |
- !Sub 'lambda.${AWS::Region}.amazonaws.com' | |
- !Sub 'sns.${AWS::Region}.amazonaws.com' | |
- !Sub 's3.${AWS::Region}.amazonaws.com' | |
- !Sub 'secretsmanager.${AWS::Region}.amazonaws.com' | |
#-------------------------------------------------- | |
# Create a key alias for Duo AuthProxy | |
#-------------------------------------------------- | |
DuoKmsKeyAlias: | |
Type: AWS::KMS::Alias | |
Properties: | |
AliasName: alias/duoAuthProxy | |
TargetKeyId: !Ref DuoKmsKey | |
# Based on: https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html#key-policy-users-crypto | |
#-------------------------------------------------- | |
# Create an IAM policy that allows the use of KMS key | |
#-------------------------------------------------- | |
DuoKmsIamPolicy: | |
Type: AWS::IAM::ManagedPolicy | |
Properties: | |
# ManagedPolicyName: !Sub '${AWS::StackName}-${AWS::Region}-kms' | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- kms:Encrypt | |
- kms:Decrypt | |
- kms:ReEncrypt* | |
- kms:DescribeKey | |
- kms:GetPublicKey | |
Effect: Allow | |
Resource: !GetAtt DuoKmsKey.Arn | |
#-------------------------------------------------- | |
# Create a IAM role for Cleanup | |
#-------------------------------------------------- | |
DuoCleanupServiceRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: [lambda.amazonaws.com] | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' | |
- !Ref DuoKmsIamPolicy | |
Policies: | |
- PolicyName: DuoEcrCleanup | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Action: | |
- 'ecr:DescribeImages' | |
- 'ecr:BatchDeleteImage' | |
Resource: !GetAtt DuoEcrRepository.Arn | |
- PolicyName: s3-access | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Action: | |
- 's3:PutObject' | |
- 's3:GetObject' | |
- 's3:DeleteObject' | |
- 's3:ListBucketVersion*' | |
- 's3:ListBucket' | |
- 's3:DeleteObjectVersion' | |
- 's3:GetObjectVersion*' | |
Resource: | |
- !Sub 'arn:${AWS::Partition}:s3:::${ArtifactBucket}' | |
- !Sub 'arn:${AWS::Partition}:s3:::${ArtifactBucket}/*' | |
- !Sub 'arn:${AWS::Partition}:s3:::${QSS3BucketName}' | |
- !Sub 'arn:${AWS::Partition}:s3:::${QSS3BucketName}/*' | |
#-------------------------------------------------- | |
# Create a Lambda function for Cleanup | |
#-------------------------------------------------- | |
DuoCleanupFunction: | |
Type: AWS::Lambda::Function | |
Properties: | |
Description: Cleanup ECR images and S3 artifact bucket on delete of stack | |
Handler: index.lambda_handler | |
KmsKeyArn: !GetAtt DuoKmsKey.Arn | |
Role: !GetAtt DuoCleanupServiceRole.Arn | |
Runtime: python3.7 | |
Timeout: 900 | |
Environment: | |
Variables: | |
DuoEcr: !Ref DuoEcrRepository | |
DuoBucket: !Ref ArtifactBucket | |
Code: | |
ZipFile: | | |
import json | |
import cfnresponse | |
import boto3 | |
import os | |
import time | |
ecr = boto3.client('ecr') | |
s3 = boto3.resource('s3') | |
ecr_repo = os.environ['DuoEcr'] | |
artifact_bucket = os.environ['DuoBucket'] | |
bucket = s3.Bucket(artifact_bucket) | |
def lambda_handler(event, context): | |
print(json.dumps(event)) | |
source_bucket = event['ResourceProperties']['Source'] | |
source_bucket_prefix = event['ResourceProperties']['Prefix'] | |
objects = event['ResourceProperties']['Objects'] | |
try: | |
if event['RequestType'] == 'Delete': | |
print('Delete event, will clean up ECR and S3') | |
time.sleep(120) | |
delete_all_ecr_images(ecr_repo) | |
bucket.object_versions.all().delete() | |
elif event['RequestType'] == 'Create': | |
print('Create event, will populate S3') | |
s3 = boto3.client('s3') | |
for o in objects: | |
key = source_bucket_prefix + o | |
copy_source={ | |
'Bucket': source_bucket, | |
'Key': key | |
} | |
s3.copy_object(CopySource=copy_source, Bucket=artifact_bucket, Key=key) | |
else: | |
print('This is Update event, will send success') | |
cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, '') | |
except: | |
cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, '') | |
def delete_all_ecr_images(ecr_repo): | |
len_images = ecr.describe_images(repositoryName=ecr_repo)['imageDetails'] | |
while len(len_images) > 0: | |
for image in len_images: | |
ecr.batch_delete_image(repositoryName=ecr_repo,imageIds = [{'imageDigest': image['imageDigest']}]) | |
len_images = ecr.describe_images(repositoryName=ecr_repo)['imageDetails'] | |
#-------------------------------------------------- | |
# Create a Custom Resource for Cleanup | |
#-------------------------------------------------- | |
DuoCleanupCustomResource: | |
Type: Custom::DuoCleanup | |
Properties: | |
ServiceToken: !GetAtt DuoCleanupFunction.Arn | |
DuoEcr: !Ref DuoEcrRepository | |
DuoBucket: !Ref ArtifactBucket | |
Source: !Ref QSS3BucketName | |
Prefix: !Ref QSS3KeyPrefix | |
SourceRegion: !Ref QSS3BucketRegion | |
Objects: | |
- scripts/packages/code_commit.zip | |
#-------------------------------------------------- | |
# Outputs | |
#-------------------------------------------------- | |
Outputs: | |
DuoRadiusProxyVpc: | |
Value: !GetAtt GetDirectoryServiceDetails.VpcId | |
Description: VPC ID of directory | |
ArtifactBucket: | |
Value: !Ref ArtifactBucket | |
Description: S3 bucket to store CodePipeline Artifacts | |
DuoCluster: | |
Value: !Ref DuoCluster | |
Description: ECS Fargate Cluster for Duo AuthProxy | |
DuoCodeBuildEcr: | |
Value: !Ref DuoCodeBuildEcr | |
Description: CodeBuild Project that builds the ECR image for Duo AuthProxy | |
DuoCodeBuildPolicy: | |
Value: !Ref DuoCodeBuildPolicy | |
Description: IAM policy to allow CodeBuild to build ECR images | |
DuoCodeBuildRole: | |
Value: !Ref DuoCodeBuildRole | |
Description: IAM role for CodeBuild | |
DuoCodePipelineRole: | |
Value: !Ref DuoCodePipelineRole | |
Description: IAM Role for CodePipeline | |
DuoConfigurationSettingsSecret: | |
Value: !Ref DuoConfigurationSettingsSecret | |
Description: Secrets Manager secret that stores the Duo AuthProxy information | |
DuoConfigurationSettingsSecretRotationLambdaInvokePermission: | |
Value: !Ref DuoConfigurationSettingsSecretRotationLambdaInvokePermission | |
Description: Lambda permission to allow Secrets Manager | |
DuoConfigurationSettingsSecretRotationSchedule: | |
Value: !Ref DuoConfigurationSettingsSecretRotationSchedule | |
Description: Secrets Manager Rotation Schedule | |
DuoCustomResourceEvent: | |
Value: !Ref DuoCustomResourceEvent | |
Description: CloudWatch event for CodePipeline status for Custom Resource | |
DuoEcrCodeCommitRepo: | |
Value: !Ref DuoEcrCodeCommitRepo | |
Description: CodeCommit repo for Duo AuthProxy | |
DuoEcrPipeline: | |
Value: !Ref DuoEcrPipeline | |
Description: CodePipeline for building Duo AuthProxy images | |
DuoEcrPipelineCloudWatchEventRole: | |
Value: !Ref DuoEcrPipelineCloudWatchEventRole | |
Description: IAM role to trigger CodePipeline based on CodeCommit | |
DuoEcrPipelineCloudWatchEventRule: | |
Value: !Ref DuoEcrPipelineCloudWatchEventRule | |
Description: Event based trigger on CodePipeline | |
DuoEcrPipelineEvents: | |
Value: !Ref DuoEcrPipelineEvents | |
Description: Events on Duo AuthProxy CodePipeline | |
DuoEcrPipelineWeeklyTrigger: | |
Value: !Ref DuoEcrPipelineWeeklyTrigger | |
Description: Weekly trigger for building Duo AuthProxy images | |
DuoEcrRepository: | |
Value: !Ref DuoEcrRepository | |
Description: Elastic Container Repository for Duo AuthProxy | |
DuoEcsScalableTarget: | |
Value: !Ref DuoEcsScalableTarget | |
Description: ECS scalable target for Duo AuthProxy | |
DuoKmsIamPolicy: | |
Value: !Ref DuoKmsIamPolicy | |
Description: IAM policy for KMS key | |
DuoKmsKey: | |
Value: !Ref DuoKmsKey | |
Description: KMS Key for Duo AuthProxy | |
DuoKmsKeyAlias: | |
Value: !Ref DuoKmsKeyAlias | |
Description: KMS Key alias for Duo AuthProxy | |
DuoNotification: | |
Value: !Ref DuoNotification | |
Description: Email notification to Duo Admins | |
DuoNotificationPolicy: | |
Value: !Ref DuoNotificationPolicy | |
Description: Allow other AWS services to send email incase of failures to Duo Admins | |
DuoPipelineSnsEvents: | |
Value: !Ref DuoPipelineSnsEvents | |
Description: CodePipeline events for Custom Resource on Create | |
DuoService: | |
Value: !Ref DuoService | |
Description: ECS service for Duo AuthProxy | |
DuoServiceAlarmCpu: | |
Value: !Ref DuoServiceAlarmCpu | |
Description: Alarm to notify admins for ECS Service CPU | |
DuoServiceAlarmMemory: | |
Value: !Ref DuoServiceAlarmMemory | |
Description: Alarm to notify admins for ECS Service Memory | |
DuoServiceEvents: | |
Value: !Ref DuoServiceEvents | |
Description: Event stream for Duo ECS service | |
DuoServiceIps: | |
Value: !Ref DuoServiceIps | |
Description: SSM parameter store for Fargate IPs | |
DuoServiceScalingPolicyCpu: | |
Value: !Ref DuoServiceScalingPolicyCpu | |
Description: Duo ECS service Application Auto Scaling for CPU | |
DuoServiceScalingPolicyMem: | |
Value: !Ref DuoServiceScalingPolicyMem | |
Description: Duo ECS service Application Auto Scaling for memory | |
DuoServiceSg: | |
Value: !Ref DuoServiceSg | |
Description: EC2 Security group for Duo Fargate Service | |
DuoSnsCustomResource: | |
Value: !Ref DuoSnsCustomResource | |
Description: SNS backed custom resource for ECR image | |
DuoSnsCustomResourceEventsToInvokeLambda: | |
Value: !Ref DuoSnsCustomResourceEventsToInvokeLambda | |
Description: Allow Custom Resource SNS to invoke Lambda | |
DuoSnsCustomResourceLambda: | |
Value: !Ref DuoSnsCustomResourceLambda | |
Description: Lambda function to process custom resource events | |
DuoSnsCustomResourceLambdaRole: | |
Value: !Ref DuoSnsCustomResourceLambdaRole | |
Description: IAM role for lambda to process custom resource events | |
DuoSnsCustomResourcePermissionLambda: | |
Value: !Ref DuoSnsCustomResourcePermissionLambda | |
Description: Allow Events to invoke lambda | |
DuoSnsCustomResourceTopic: | |
Value: !Ref DuoSnsCustomResourceTopic | |
Description: SNS topic for Custom Resource | |
DuoSnsCustomResourceTopicSubscriptionLambda: | |
Value: !Ref DuoSnsCustomResourceTopicSubscriptionLambda | |
Description: SNS Subscription from Custom Resource to lambda | |
DuoTaskDefinition: | |
Value: !Ref DuoTaskDefinition | |
Description: ECS Task Definition for Duo AuthProxy | |
DuoTaskRoleArn: | |
Value: !Ref DuoTaskRoleArn | |
Description: IAM role for ECS Fargate tasks | |
GetDirectoryServiceDetails: | |
Value: !Ref GetDirectoryServiceDetails | |
Description: Custom Resource to get Directory Service Details | |
GetDirectoryServiceFunction: | |
Value: !Ref GetDirectoryServiceFunction | |
Description: Lambda function to get Directory Service details | |
GetDirectoryServiceMfaSettingsRole: | |
Value: !Ref GetDirectoryServiceMfaSettingsRole | |
Description: IAM role to get Directory Service details | |
PermissionForEventsToInvokeLambda: | |
Value: !Ref PermissionForEventsToInvokeLambda | |
Description: Allow Duo ECS Service events to trigger Lambda | |
ProcessDuoServiceEventsRole: | |
Value: !Ref ProcessDuoServiceEventsRole | |
Description: IAM role to process ECS service events | |
ProcessDuoServiceFunction: | |
Value: !Ref ProcessDuoServiceFunction | |
Description: Lambda function to process ECS Service events | |
RadiusProxyCloudWatchLogsGroup: | |
Value: !Ref RadiusProxyCloudWatchLogsGroup | |
Description: CloudWatch log group for Duo ECS Service | |
RadiusSharedSecretRotationRole: | |
Value: !Ref RadiusSharedSecretRotationRole | |
Description: IAM role to allow Secrets Manager Rotation | |
RotateRadiusSharedSecretFunction: | |
Value: !Ref RotateRadiusSharedSecretFunction | |
Description: Lambda function to rotate secrets | |
UpdateDirectoryPermissionForEventsToInvokeLambda: | |
Value: !Ref UpdateDirectoryPermissionForEventsToInvokeLambda | |
Description: Allow change in Fargate IPs to trigger lambda via SSM | |
UpdateDirectoryServiceEvent: | |
Value: !Ref UpdateDirectoryServiceEvent | |
Description: Fargate IP change event | |
UpdateDirectoryServiceMfaSettings: | |
Value: !Ref UpdateDirectoryServiceMfaSettings | |
Description: Lambda function to update MFA of directory | |
UpdateDirectoryServiceMfaSettingsRole: | |
Value: !Ref UpdateDirectoryServiceMfaSettingsRole | |
Description: IAM role to allow update MFA of Directory | |
Postdeployment: | |
Value: https://fwd.aws/n7wKB? | |
Description: See the deployment guide for post-deployment steps. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment