Open dtnmatt opened 2 years ago
Hello, Yes, the Launch Stack button is non-functional. I have found some workaround, so that we can create it via AWS CLI. For authentication token I have used aws sso login using the profile, you can use various methods to login using aws-cli.
I have used 3 files
1. common-script.py (this script is resides under cloudformation-custom-resources/ folder
import boto3 import sys import os
PROFILE=sys.argv[1] region = sys.argv[2]
boto3 = boto3.session.Session(profile_name=PROFILE)
print("Working on region: " + region)
bucket_prefix = "aws-waf-dashboards-" bucket_name = bucket_prefix + region
s3 = boto3.client('s3', region_name=region) if (region != 'us-east-1'): s3.create_bucket(Bucket=bucket_name, CreateBucketConfiguration={'LocationConstraint': region}) else: s3.create_bucket(Bucket=bucket_name)
cmd_1 = 'cd domain-setter-lambda; zip -r ../domain-setter-lambda.zip ; cd ..' cmd_2 = 'cd es-cognito-auth-lambda; zip -r ../es-cognito-auth-lambda.zip ; cd ..' cmd_3 = 'cd kibana-customizer-lambda; zip -r ../kibana-customizer-lambda.zip *; cd ..' os.system(cmd_1) os.system(cmd_2) os.system(cmd_3)
print("Working on region: " + region);
print("-> Uploading zip files to Bucket: " + bucket_name);
boto3.resource('s3').Bucket(bucket_name).upload_file("domain-setter-lambda.zip", 'domain-setter-lambda.zip') boto3.client('s3', region_name=region).put_object_acl(ACL='public-read',Bucket=bucket_name,Key='domain-setter-lambda.zip');
boto3.resource('s3').Bucket(bucket_name).upload_file("es-cognito-auth-lambda.zip", 'es-cognito-auth-lambda.zip') boto3.client('s3', region_name=region).put_object_acl(ACL='public-read',Bucket=bucket_name,Key='es-cognito-auth-lambda.zip');
boto3.resource('s3').Bucket(bucket_name).upload_file("kibana-customizer-lambda.zip", 'kibana-customizer-lambda.zip') boto3.client('s3', region_name=region).put_object_acl(ACL='public-read',Bucket=bucket_name,Key='kibana-customizer-lambda.zip');
cmd_1 = 'rm domain-setter-lambda.zip' cmd_2 = 'rm es-cognito-auth-lambda.zip' cmd_3 = 'rm kibana-customizer-lambda.zip'
os.system(cmd_1) os.system(cmd_2) os.system(cmd_3)
2. deploy_core(this is under the root of repository)
SVC_NAME=my-waf-dashboard DataNodeEBSVolumeSize=100 ElasticSerchDomainName=waf-dashboards NodeType=m5.large.elasticsearch Email=abc@gmail.com
function deploy_core_stack { PROFILE=${1} REGION=${2} TOKEN_PREFIX=${3} # optional
cd cloudformation-custom-resources python3 common-script.py ${PROFILE} ${REGION} cd ..
aws cloudformation deploy --stack-name ${SVC_NAME} --template-file waf-dashboard.yaml.yaml \ --capabilities CAPABILITY_NAMED_IAM --no-fail-on-empty-changeset \ --parameter-overrides \ DataNodeEBSVolumeSize=${DataNodeEBSVolumeSize} \ ElasticSerchDomainName=${ElasticSerchDomainName} \ NodeType=${NodeType} \ UserEmail=${Email} \ --region ${REGION} \ --profile ${PROFILE}
aws cloudformation update-termination-protection --stack-name ${SVC_NAME} \ --enable-termination-protection \ --region ${REGION} \ --profile ${PROFILE} > /dev/null }
DEPLOY_TYPE=${1} DEPLOY_PROFILE=${2} DEPLOY_REGION=${3} DEPLOY_PREFIX=${4}
if [[ "${DEPLOY_TYPE}" == "solo" ]]; then
deploy_core_stack ${DEPLOY_PROFILE} ${DEPLOY_REGION:-us-east-1} ${DEPLOY_PREFIX:-dev}
elif [[ "${DEPLOY_TYPE}" == "nonprd" ]]; then
deploy_core_stack
fi
3. waf-dashboard.yaml.yaml
AWSTemplateFormatVersion: "2010-09-09" Description: Sample AWS WAF Dashboard build on Amazon Elasticsearch Service.
Parameters: DataNodeEBSVolumeSize: Type: Number Default: 100 Description: Elasticsearch volume disk size
NodeType: Type: String Default: m5.large.elasticsearch Description: Elasticsearch Node Type
ElasticSerchDomainName: Type: String Default: 'waf-dashboards' AllowedPattern: "[a-z\-]*" Description: Elasticsearch domain name
UserEmail: Type: String Default: 'your@email.com' Description: Dashboard user e-mail address
Resources:
UserPoolDomainSetterLambda: Type: "AWS::Lambda::Function" Properties: Handler: "lambda_function.handler" Role: !GetAtt UserPoolDomainSetterLambdaRole.Arn Code: S3Bucket: !Join
ESCognitoAuthSetterLambda: Type: "AWS::Lambda::Function" Properties: Handler: "lambda_function.handler" Role: !GetAtt ESCognitoAuthSetterLambdaRole.Arn Code: S3Bucket: !Join
KibanaCustomizerLambda: Type: "AWS::Lambda::Function" Properties: Handler: "lambda_function.handler" Role: !GetAtt KibanaCustomizerLambdaRole.Arn Code: S3Bucket: !Join
UserPoolDomainSetterLambdaRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement:
Effect: Allow Principal: Service: lambda.amazonaws.com Action: "sts:AssumeRole" Policies:
ESCognitoAuthSetterLambdaRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement:
Effect: Allow Principal: Service: lambda.amazonaws.com Action: "sts:AssumeRole" Policies:
KibanaCustomizerLambdaRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement:
Effect: Allow Principal: Service: lambda.amazonaws.com Action: "sts:AssumeRole" Policies:
KinesisFirehoseS3Bucket: Type: AWS::S3::Bucket DeletionPolicy: Retain
UserPool: Type: AWS::Cognito::UserPool Properties: UserPoolName: 'WAFKibanaUsers' AdminCreateUserConfig: AllowAdminCreateUserOnly: True UsernameAttributes:
IdentityPool: Type: AWS::Cognito::IdentityPool Properties: IdentityPoolName: "WAFKibanaIdentityPool" AllowUnauthenticatedIdentities: true
AuthenticatedPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement:
Effect: "Allow" Action:
AuthenticatedRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement:
Effect: Allow Action: "sts:AssumeRoleWithWebIdentity" Principal: Federated: cognito-identity.amazonaws.com Condition: StringEquals: "cognito-identity.amazonaws.com:aud": !Ref IdentityPool ForAnyValue:StringLike: "cognito-identity.amazonaws.com:amr": authenticated ManagedPolicyArns:
CognitoAccessForAmazonESRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement:
Effect: Allow Principal: Service: es.amazonaws.com Action: "sts:AssumeRole" ManagedPolicyArns:
RoleAttachment: Type: AWS::Cognito::IdentityPoolRoleAttachment Properties: IdentityPoolId: !Ref IdentityPool Roles: authenticated: !GetAtt AuthenticatedRole.Arn
CognitoPoolUser: Type: AWS::Cognito::UserPoolUser Properties: Username: !Ref UserEmail UserPoolId: !Ref UserPool
ElasticsearchDomain: Type: AWS::Elasticsearch::Domain Properties: DomainName: !Ref ElasticSerchDomainName ElasticsearchVersion: '6.7' ElasticsearchClusterConfig: DedicatedMasterEnabled: 'false' InstanceCount: '1' InstanceType: !Ref NodeType EBSOptions: EBSEnabled: true Iops: 0 VolumeSize: !Ref DataNodeEBSVolumeSize VolumeType: 'gp2' SnapshotOptions: AutomatedSnapshotStartHour: '0' AccessPolicies: Version: "2012-10-17" Statement:
Effect: "Allow" Principal: AWS: !GetAtt KibanaCustomizerLambdaRole.Arn Action: "es:" Resource: !Sub "arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${ElasticSerchDomainName}/"
KinesisFirehoseDeliveryRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement:
Effect: Allow Action: 'sts:AssumeRole' Principal: Service:
KinesisFirehoseDeliveryPolicy: Type: 'AWS::IAM::Policy' Properties: PolicyName: 'WAFDeliveryPolicy' Roles:
kinesis:GetRecords Resource: !Sub 'arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/%FIREHOSE_STREAM_NAME%'
KinesisFirehoseDeliveryStream: Type: AWS::KinesisFirehose::DeliveryStream Properties: DeliveryStreamName: !Sub 'aws-waf-logs-${UserPool}' DeliveryStreamType: "DirectPut" ElasticsearchDestinationConfiguration: BufferingHints: IntervalInSeconds: "60" SizeInMBs: 5 CloudWatchLoggingOptions: Enabled: true LogGroupName: 'deliverystream' LogStreamName: 'elasticsearchDelivery' DomainARN: !GetAtt ElasticsearchDomain.Arn IndexName: 'awswaf' IndexRotationPeriod: "OneDay" TypeName: 'waflog' RetryOptions: DurationInSeconds: "60" RoleARN: !GetAtt KinesisFirehoseDeliveryRole.Arn S3BackupMode: "AllDocuments" S3Configuration: BucketARN: !Sub 'arn:aws:s3:::${KinesisFirehoseS3Bucket}' BufferingHints: IntervalInSeconds: "60" SizeInMBs: "50" CompressionFormat: "UNCOMPRESSED" Prefix: 'log/' RoleARN: !GetAtt KinesisFirehoseDeliveryRole.Arn CloudWatchLoggingOptions: Enabled: true LogGroupName: "deliverystream" LogStreamName: "s3Backup"
UserPoolDomainSetter: Type: Custom::UserPoolDomainSetterLambda DependsOn: ElasticsearchDomain Properties: ServiceToken: !GetAtt UserPoolDomainSetterLambda.Arn StackName: !Ref "AWS::StackName" UserPoolId: !Ref UserPool
ESCognitoAuthSetter: Type: Custom::ESCognitoAuthSetter DependsOn: UserPoolDomainSetter Properties: ServiceToken: !GetAtt ESCognitoAuthSetterLambda.Arn StackName: !Ref "AWS::StackName" UserPoolId: !Ref UserPool IdentityPoolId: !Ref IdentityPool RoleArn: !GetAtt CognitoAccessForAmazonESRole.Arn DomainName: !Ref ElasticSerchDomainName
KibanaCustomizer: Type: Custom::KibanaCustomizer DependsOn: UserPoolDomainSetter Properties: ServiceToken: !GetAtt KibanaCustomizerLambda.Arn StackName: !Ref "AWS::StackName" Region: !Ref "AWS::Region" Host: !GetAtt ElasticsearchDomain.DomainEndpoint AccountID: !Ref "AWS::AccountId"
KibanaUpdate: Type: "AWS::Lambda::Function" Properties: Handler: "lambda_function.update_kibana" Role: !GetAtt KibanaCustomizerLambdaRole.Arn Code: S3Bucket: !Join
WAFv2Modification: Type: AWS::Events::Rule Properties: Description: WAF Dashboard - detects new WebACL and rules for WAFv2. EventPattern: source:
"AWS API Call via CloudTrail" detail: eventSource:
Arn: !GetAtt KibanaUpdate.Arn Id: "1"
WAFGlobalModification: Type: AWS::Events::Rule Properties: Description: WAF Dashboard - detects new WebACL and rules for WAF Global. EventPattern: source:
"AWS API Call via CloudTrail" detail: eventSource:
Arn: !GetAtt KibanaUpdate.Arn Id: "1"
WAFRegionalModification: Type: AWS::Events::Rule Properties: Description: WAF Dashboard - detects new WebACL and rules for WAF Regional. EventPattern: source:
Arn: !GetAtt KibanaUpdate.Arn Id: "1"
KibanaUpdateWAFGlobalPermission: Type: AWS::Lambda::Permission Properties: Action: 'lambda:InvokeFunction' FunctionName: !Ref KibanaUpdate Principal: "events.amazonaws.com" SourceArn: !GetAtt WAFGlobalModification.Arn
KibanaUpdateWAFv2Permission: Type: AWS::Lambda::Permission Properties: Action: 'lambda:InvokeFunction' FunctionName: !Ref KibanaUpdate Principal: "events.amazonaws.com" SourceArn: !GetAtt WAFv2Modification.Arn
KibanaUpdateWAFRegionalPermission: Type: AWS::Lambda::Permission Properties: Action: 'lambda:InvokeFunction' FunctionName: !Ref KibanaUpdate Principal: "events.amazonaws.com" SourceArn: !GetAtt WAFRegionalModification.Arn
Outputs:
DashboardLinkOutput: Description: Link to WAF Dashboard Value: !Join
To run this script be in the root of the repository and run ./deploy_core nonprd
still getting 403
➜ ~ wget https://waf-dashboards.s3.amazonaws.com/dashboard.yaml
--2022-05-26 21:21:44-- https://waf-dashboards.s3.amazonaws.com/dashboard.yaml
Resolving waf-dashboards.s3.amazonaws.com (waf-dashboards.s3.amazonaws.com)... 52.217.111.52
Connecting to waf-dashboards.s3.amazonaws.com (waf-dashboards.s3.amazonaws.com)|52.217.111.52|:443... connected.
HTTP request sent, awaiting response... 403 Forbidden
2022-05-26 21:21:44 ERROR 403: Forbidden.
The launch stack button is non-functional as it appears the bucket no longer allows access or does not exist.