diff --git a/infrastructure/lib/constructs/opensearchNginxProxyCognito.ts b/infrastructure/lib/constructs/opensearchNginxProxyCognito.ts index 5282030..47089f1 100644 --- a/infrastructure/lib/constructs/opensearchNginxProxyCognito.ts +++ b/infrastructure/lib/constructs/opensearchNginxProxyCognito.ts @@ -49,6 +49,9 @@ export interface opensearchDashboardUrlProps { } export class OpenSearchMetricsNginxCognito extends Construct { + + static readonly COGNITO_ALB_ARN: string = 'cognitoAlbArn'; + readonly asg: AutoScalingGroup; constructor(scope: Construct, id: string, props: NginxProps) { @@ -64,28 +67,37 @@ export class OpenSearchMetricsNginxCognito extends Construct { healthCheck: HealthCheck.ec2({ grace: Duration.seconds(90) }), machineImage: MachineImage.latestAmazonLinux2(), // Temp added public subnet and IP, until backed up by ALB - associatePublicIpAddress: true, + associatePublicIpAddress: false, allowAllOutbound: true, desiredCapacity: 1, minCapacity: 1, vpc: props.vpc, vpcSubnets: { - // Temp added public subnet and IP, until backed up by ALB - // subnetType: SubnetType.PUBLIC, - subnetType: SubnetType.PUBLIC + subnetType: SubnetType.PRIVATE_WITH_EGRESS }, role: instanceRole, - // Actually update the existing instance instead of leaving it running. This will build a new ASG - // and then destroy the old one in order to maintain availability updatePolicy: UpdatePolicy.replacingUpdate() }); Tags.of(this.asg).add("Name", "OpenSearchMetricsCognito") if (props.albProps) { + + const albSecurityGroup = new SecurityGroup(this, 'ALBSecurityGroup', { + vpc, + allowAllOutbound: true, + }); + albSecurityGroup.addIngressRule(Peer.prefixList(Project.RESTRICTED_PREFIX), Port.tcp(443)); + const openSearchCognitoApplicationLoadBalancer = new ApplicationLoadBalancer(this, `OpenSearchMetricsCognito-NginxProxyAlb`, { loadBalancerName: "OpenSearchMetricsCognito", vpc: vpc, - internetFacing: true + internetFacing: true, + securityGroup: albSecurityGroup + }); + + new CfnOutput(this, 'cognitoAlbArn', { + value: openSearchCognitoApplicationLoadBalancer.loadBalancerArn, + exportName: OpenSearchMetricsNginxCognito.COGNITO_ALB_ARN, }); const listenerCertificate = ListenerCertificate.fromArn(props.albProps.certificateArn); @@ -99,13 +111,17 @@ export class OpenSearchMetricsNginxCognito extends Construct { listener.addTargets(`OpenSearchMetricsCognito-NginxProxyAlbTarget`, { port: 443, protocol: ApplicationProtocol.HTTPS, + healthCheck: { + port: '80', + path: '/', + }, targets: [this.asg] }); const aRecord = new ARecord(this, "OpenSearchMetricsCognito-DNS", { zone: props.albProps.hostedZone.zone, - recordName: Project.METRICS_HOSTED_ZONE, + recordName: Project.METRICS_COGNITO_HOSTED_ZONE, target: RecordTarget.fromAlias(new LoadBalancerTarget(openSearchCognitoApplicationLoadBalancer)), }); } diff --git a/infrastructure/lib/enums/project.ts b/infrastructure/lib/enums/project.ts index cdcd26f..27f119f 100644 --- a/infrastructure/lib/enums/project.ts +++ b/infrastructure/lib/enums/project.ts @@ -4,7 +4,7 @@ enum Project{ JENKINS_AGENT_ROLE = '', REGION = '', METRICS_HOSTED_ZONE = 'metrics.opensearch.org', - // Temp until the project is public + METRICS_COGNITO_HOSTED_ZONE = 'metrics.login.opensearch.org', RESTRICTED_PREFIX = '', LAMBDA_PACKAGE = 'opensearch-metrics-1.0.zip', } diff --git a/infrastructure/lib/infrastructure-stack.ts b/infrastructure/lib/infrastructure-stack.ts index 0c438a0..5808026 100644 --- a/infrastructure/lib/infrastructure-stack.ts +++ b/infrastructure/lib/infrastructure-stack.ts @@ -1,4 +1,4 @@ -import {App, CfnOutput, Stack, StackProps} from 'aws-cdk-lib'; +import {App, Fn, Stack, StackProps} from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { VpcStack } from "./stacks/vpc"; import {jenkinsAccess, OpenSearchDomainStack} from "./stacks/opensearch"; @@ -7,6 +7,8 @@ import {OpenSearchHealthRoute53} from "./stacks/route53"; import {OpenSearchMetricsWorkflowStack} from "./stacks/metricsWorkflow"; import {OpenSearchMetricsNginxReadonly} from "./stacks/opensearchNginxProxyReadonly"; import {ArnPrincipal, IPrincipal} from "aws-cdk-lib/aws-iam"; +import {OpenSearchWAF} from "./stacks/waf"; +import {OpenSearchMetricsNginxCognito} from "./constructs/opensearchNginxProxyCognito"; // import * as sqs from 'aws-cdk-lib/aws-sqs'; export class InfrastructureStack extends Stack { @@ -62,5 +64,13 @@ export class InfrastructureStack extends Stack { }); openSearchMetricsNginxReadonly.node.addDependency(vpcStack, openSearchDomainStack); + // Create an OpenSearch WAF stack + const openSearchWAF = new OpenSearchWAF(app, "OpenSearchWAF", { + readOnlyLoadBalancerArn: Fn.importValue(`${OpenSearchMetricsNginxReadonly.READONLY_ALB_ARN}`), + cognitoLoadBalancerArn: Fn.importValue(`${OpenSearchMetricsNginxCognito.COGNITO_ALB_ARN}`), + appName: "OpenSearchMetricsWAF" + }); + openSearchWAF.node.addDependency(openSearchDomainStack, openSearchMetricsNginxReadonly); + } } diff --git a/infrastructure/lib/stacks/opensearch.ts b/infrastructure/lib/stacks/opensearch.ts index f9aee90..0c4e52b 100644 --- a/infrastructure/lib/stacks/opensearch.ts +++ b/infrastructure/lib/stacks/opensearch.ts @@ -6,7 +6,11 @@ import * as iam from "aws-cdk-lib/aws-iam"; import { ArnPrincipal, CompositePrincipal, Effect, IRole, ManagedPolicy, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; import { VpcStack } from "./vpc"; import {OpenSearchMetricsCognito} from "../constructs/opensearchCognito"; -import {OpenSearchMetricsNginxCognito} from "../constructs/opensearchNginxProxyCognito";; +import {OpenSearchMetricsNginxCognito} from "../constructs/opensearchNginxProxyCognito"; +import {OpenSearchHealthRoute53} from "./route53"; +import Project from "../enums/project"; + +; export interface OpenSearchStackProps { @@ -176,6 +180,10 @@ export class OpenSearchDomainStack extends Stack { this.domain.node.addDependency(serviceLinkedRole); if(props.enableNginxCognito) { + const metricsHostedZone = new OpenSearchHealthRoute53(this, "OpenSearchMetricsCognito-HostedZone", { + hostedZone: Project.METRICS_COGNITO_HOSTED_ZONE, + appName: "OpenSearchMetricsCognito" + }); new OpenSearchMetricsNginxCognito(this, "OpenSearchMetricsNginx", { region: this.props.region, vpc: props.vpcStack.vpc, @@ -183,7 +191,11 @@ export class OpenSearchDomainStack extends Stack { opensearchDashboardUrlProps: { opensearchDashboardVpcUrl: this.domain.domainEndpoint, cognitoDomain: metricsCognito.userPoolDomain.domain - } + }, + albProps: { + hostedZone: metricsHostedZone, + certificateArn: metricsHostedZone.certificateArn, + }, }); } } diff --git a/infrastructure/lib/stacks/opensearchNginxProxyReadonly.ts b/infrastructure/lib/stacks/opensearchNginxProxyReadonly.ts index 4b0f00d..e5ef306 100644 --- a/infrastructure/lib/stacks/opensearchNginxProxyReadonly.ts +++ b/infrastructure/lib/stacks/opensearchNginxProxyReadonly.ts @@ -12,7 +12,7 @@ import { AmazonLinuxImage, MachineImage } from 'aws-cdk-lib/aws-ec2'; import * as iam from "aws-cdk-lib/aws-iam"; -import {Aspects, Duration, Stack, Tag, Tags} from 'aws-cdk-lib'; +import {Aspects, CfnOutput, Duration, Stack, Tag, Tags} from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { ApplicationLoadBalancer, ApplicationProtocol, @@ -46,6 +46,9 @@ export interface opensearchDashboardUrlProps { } export class OpenSearchMetricsNginxReadonly extends Stack { + + static readonly READONLY_ALB_ARN: string = 'readOnlyAlbArn'; + readonly asg: AutoScalingGroup; constructor(scope: Construct, id: string, props: NginxProps) { @@ -54,8 +57,6 @@ export class OpenSearchMetricsNginxReadonly extends Stack { super(scope, id); const instanceRole = this.createNginxReadonlyInstanceRole(props); - - this.asg = new AutoScalingGroup(this, 'OpenSearchMetricsReadonly-MetricsProxyAsg', { instanceType: InstanceType.of(InstanceClass.M5, InstanceSize.LARGE), blockDevices: [{ deviceName: '/dev/xvda', volume: BlockDeviceVolume.ebs(10) }], // GB @@ -88,11 +89,13 @@ export class OpenSearchMetricsNginxReadonly extends Stack { securityGroup: albSecurityGroup }); - const openSearchWAF = new OpenSearchWAF(this, "OpenSearchWAF", { - loadBalancer: openSearchApplicationLoadBalancer, - appName: "OpenSearchMetricsWAF" + new CfnOutput(this, 'readOnlyAlbArn', { + value: openSearchApplicationLoadBalancer.loadBalancerArn, + exportName: OpenSearchMetricsNginxReadonly.READONLY_ALB_ARN, }); - openSearchWAF.node.addDependency(openSearchApplicationLoadBalancer) + + //const importedArnSecretBucketValue = Fn.importValue(`${CIConfigStack.CERTIFICATE_ARN_SECRET_EXPORT_VALUE}`); + const listenerCertificate = ListenerCertificate.fromArn(props.albProps.certificateArn); diff --git a/infrastructure/lib/stacks/waf.ts b/infrastructure/lib/stacks/waf.ts index 4c5d63c..4d2754f 100644 --- a/infrastructure/lib/stacks/waf.ts +++ b/infrastructure/lib/stacks/waf.ts @@ -105,16 +105,21 @@ export class WebACLAssociation extends CfnWebACLAssociation { } export interface WafProps extends StackProps{ - loadBalancer: ApplicationLoadBalancer, + readOnlyLoadBalancerArn: string, + cognitoLoadBalancerArn: string appName: string } -export class OpenSearchWAF extends Construct { +export class OpenSearchWAF extends Stack { constructor(scope: Construct, id: string, props: WafProps) { super(scope, id); const waf = new WAF(this, `${props.appName}-WAFv2`); - new WebACLAssociation(this, 'wafALBassociation', { - resourceArn: props.loadBalancer.loadBalancerArn, + new WebACLAssociation(this, 'wafReadOnlyALBassociation', { + resourceArn: props.readOnlyLoadBalancerArn, + webAclArn: waf.attrArn, + }); + new WebACLAssociation(this, 'wafCognitoALBassociation', { + resourceArn: props.cognitoLoadBalancerArn, webAclArn: waf.attrArn, }); }