Skip to content

Commit

Permalink
Enable access logging for load balancer (#401)
Browse files Browse the repository at this point in the history
Signed-off-by: Sayali Gaikawad <[email protected]>
  • Loading branch information
gaiksaya authored Mar 19, 2024
1 parent a977ae5 commit 30cb8a3
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 58 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ resources/jenkins.yaml
# CDK asset staging directory
.cdk.staging
cdk.out

cdk.context.json
# excluding intellij Idea files
*.iml
.idea/
Expand Down
1 change: 1 addition & 0 deletions lib/ci-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ export class CIStack extends Stack {
targetInstance: mainJenkinsNode.mainNodeAsg,
listenerCertificate,
useSsl,
accessLogBucket: auditloggingS3Bucket.bucket,
});

const artifactBucket = new Bucket(this, 'BuildBucket');
Expand Down
26 changes: 23 additions & 3 deletions lib/network/ci-external-load-balancer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@
*/

import { CfnOutput, Stack } from 'aws-cdk-lib';
import { Instance, SecurityGroup, Vpc } from 'aws-cdk-lib/aws-ec2';
import { AutoScalingGroup } from 'aws-cdk-lib/aws-autoscaling';
import { SecurityGroup, Vpc } from 'aws-cdk-lib/aws-ec2';
import {
ApplicationListener, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerCertificate, Protocol, SslPolicy,
} from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import { InstanceTarget } from 'aws-cdk-lib/aws-elasticloadbalancingv2-targets';
import { AutoScalingGroup } from 'aws-cdk-lib/aws-autoscaling';
import { PolicyStatement, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { Bucket, BucketPolicy } from 'aws-cdk-lib/aws-s3';

export interface JenkinsExternalLoadBalancerProps {
readonly vpc: Vpc;
readonly sg: SecurityGroup;
readonly targetInstance: AutoScalingGroup;
readonly listenerCertificate: ListenerCertificate;
readonly useSsl: boolean;
readonly accessLogBucket: Bucket;
}

export class JenkinsExternalLoadBalancer {
Expand All @@ -31,6 +33,7 @@ export class JenkinsExternalLoadBalancer {

constructor(stack: Stack, props: JenkinsExternalLoadBalancerProps) {
const accessPort = props.useSsl ? 443 : 80;
const accessLoggingPrefix = 'loadBalancerAccessLogs';

// Using an ALB so it can be part of a security group rather than by whitelisting ip addresses
this.loadBalancer = new ApplicationLoadBalancer(stack, 'JenkinsALB', {
Expand Down Expand Up @@ -64,6 +67,23 @@ export class JenkinsExternalLoadBalancer {
},
});

this.loadBalancer.logAccessLogs(props.accessLogBucket, accessLoggingPrefix);

const bucketPolicy = new BucketPolicy(stack, 'ALBaccessLoggingBucketPolicyPErmission', {
bucket: props.accessLogBucket,
});

bucketPolicy.document.addStatements(
new PolicyStatement({
actions: ['s3:PutObject'],
principals: [
new ServicePrincipal('logdelivery.elasticloadbalancing.amazonaws.com'),
],
resources: [`${props.accessLogBucket.bucketArn}/${accessLoggingPrefix}/*`],

}),
);

new CfnOutput(stack, 'Jenkins External Load Balancer Dns', {
value: this.loadBalancer.loadBalancerDnsName,
});
Expand Down
91 changes: 85 additions & 6 deletions test/ci-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ test('CI Stack Basic Resources', () => {
// WHEN
const stack = new CIStack(app, 'TestStack', {
dataRetention: true,
env: { account: 'test-account', region: 'us-east-1' },
});
const template = Template.fromStack(stack);

Expand Down Expand Up @@ -49,7 +50,7 @@ test('External security group is open', () => {
});

// WHEN
const stack = new CIStack(app, 'MyTestStack', {});
const stack = new CIStack(app, 'MyTestStack', { env: { account: 'test-account', region: 'us-east-1' } });
const template = Template.fromStack(stack);

// THEN
Expand Down Expand Up @@ -93,7 +94,11 @@ test('External security group is restricted', () => {
});

// WHEN
const stack = new CIStack(app, 'MyTestStack', { useSsl: true, restrictServerAccessTo: Peer.ipv4('10.0.0.0/24') });
const stack = new CIStack(app, 'MyTestStack', {
env: { account: 'test-account', region: 'us-east-1' },
useSsl: true,
restrictServerAccessTo: Peer.ipv4('10.0.0.0/24'),
});
const template = Template.fromStack(stack);

// THEN
Expand Down Expand Up @@ -135,7 +140,9 @@ test('MainNode', () => {
});

// WHEN
const stack = new CIStack(app, 'MyTestStack', {});
const stack = new CIStack(app, 'MyTestStack', {
env: { account: 'test-account', region: 'us-east-1' },
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', {
Expand Down Expand Up @@ -165,7 +172,9 @@ test('LoadBalancer', () => {
});

// WHEN
const stack = new CIStack(app, 'MyTestStack', {});
const stack = new CIStack(app, 'MyTestStack', {
env: { account: 'test-account', region: 'us-east-1' },
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', {
Expand All @@ -188,7 +197,9 @@ test('CloudwatchCpuAlarm', () => {
});

// WHEN
const stack = new CIStack(app, 'MyTestStack', {});
const stack = new CIStack(app, 'MyTestStack', {
env: { account: 'test-account', region: 'us-east-1' },
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
Expand All @@ -205,11 +216,79 @@ test('CloudwatchMemoryAlarm', () => {
});

// WHEN
const stack = new CIStack(app, 'MyTestStack', {});
const stack = new CIStack(app, 'MyTestStack', {
env: { account: 'test-account', region: 'us-east-1' },
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
MetricName: 'mem_used_percent',
Statistic: 'Average',
});
});

test('LoadBalancer Access Logging', () => {
const app = new App({
context: {
useSsl: 'false', runWithOidc: 'false', serverAccessType: 'ipv4', restrictServerAccessTo: '10.10.10.10/32',
},
});

// WHEN
const stack = new CIStack(app, 'MyTestStack', {
env: { account: 'test-account', region: 'us-east-1' },
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', {
LoadBalancerAttributes: [
{
Key: 'deletion_protection.enabled',
Value: 'false',
},
{
Key: 'access_logs.s3.enabled',
Value: 'true',
},
{
Key: 'access_logs.s3.bucket',
Value: {
Ref: 'jenkinsAuditBucket110D3080',
},
},
{
Key: 'access_logs.s3.prefix',
Value: 'loadBalancerAccessLogs',
},
],
});

Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', {
PolicyDocument: {
Statement: [
{
Action: 's3:PutObject',
Effect: 'Allow',
Principal: {
Service: 'logdelivery.elasticloadbalancing.amazonaws.com',
},
Resource: {
'Fn::Join': [
'',
[
{
'Fn::GetAtt': [
'jenkinsAuditBucket110D3080',
'Arn',
],
},
'/loadBalancerAccessLogs/*',
],
],
},
},
],
Version: '2012-10-17',
},
});
});
54 changes: 6 additions & 48 deletions test/compute/agent-node-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ test('Agents Resource is present', () => {
useSsl: 'true', runWithOidc: 'true', serverAccessType: 'ipv4', restrictServerAccessTo: '10.10.10.10/32',
},
});
const stack = new CIStack(app, 'TestStack', {});
const stack = new CIStack(app, 'TestStack', {
env: { account: 'test-account', region: 'us-east-1' },
});
const template = Template.fromStack(stack);

template.hasResourceProperties('AWS::IAM::Role', {
Expand All @@ -29,17 +31,7 @@ test('Agents Resource is present', () => {
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: {
'Fn::Join': [
'',
[
'ec2.',
{
Ref: 'AWS::URLSuffix',
},
],
],
},
Service: 'ec2.amazonaws.com',
},
},
],
Expand Down Expand Up @@ -71,49 +63,14 @@ test('Agents Resource is present', () => {
'ecr-public:CompleteLayerUpload',
'ecr-public:PutImage',
],
Condition: {
StringEquals: {
'aws:RequestedRegion': {
Ref: 'AWS::Region',
},
'aws:PrincipalAccount': [
{
Ref: 'AWS::AccountId',
},
],
},
},
Effect: 'Allow',
Resource: {
'Fn::Join': [
'',
[
'arn:aws:ecr-public::',
{
Ref: 'AWS::AccountId',
},
':repository/*',
],
],
},
Resource: 'arn:aws:ecr-public::test-account:repository/*',
},
{
Action: [
'ecr-public:GetAuthorizationToken',
'sts:GetServiceBearerToken',
],
Condition: {
StringEquals: {
'aws:RequestedRegion': {
Ref: 'AWS::Region',
},
'aws:PrincipalAccount': [
{
Ref: 'AWS::AccountId',
},
],
},
},
Effect: 'Allow',
Resource: '*',
},
Expand All @@ -131,6 +88,7 @@ test('Agents Node policy with assume role Resource is present', () => {
});
const stack = new CIStack(app, 'TestStack', {
agentAssumeRole: ['arn:aws:iam::12345:role/test-role', 'arn:aws:iam::901523:role/test-role2'],
env: { account: 'test-account', region: 'us-east-1' },
});
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Expand Down

0 comments on commit 30cb8a3

Please sign in to comment.