Skip to content

Commit

Permalink
Add some compute rules (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
ekrengel committed Oct 30, 2019
1 parent b65635e commit bb21069
Show file tree
Hide file tree
Showing 9 changed files with 4,573 additions and 3 deletions.
113 changes: 113 additions & 0 deletions compute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2016-2019, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import * as aws from "@pulumi/aws";
import { EnforcementLevel, Policy, typedRule } from "@pulumi/policy";

import * as assert from "assert";

export const compute: Policy[] = [
Ec2InstanceDetailedMonitoringEnabled("mandatory"),
Ec2InstanceNoPublicIP("mandatory"),
Ec2VolumeInUseCheck("mandatory", true),
ElbAccessLoggingEnabled("mandatory"),
ElbAccessLoggingEnabled("mandatory"),
EncryptedVolumes("mandatory"),
];

export function Ec2InstanceDetailedMonitoringEnabled(enforcementLevel: EnforcementLevel = "advisory"): Policy {
return {
name: "ec2-instance-detailed-monitoring-enabled",
description: "Checks whether detailed monitoring is enabled for EC2 instances.",
enforcementLevel: enforcementLevel,
rules: [
typedRule(aws.ec2.Instance.isInstance, it => assert.ok(it.monitoring,
"You should enable detailed monitoring on EC2 instances.")),
],
};
}

export function Ec2InstanceNoPublicIP(enforcementLevel: EnforcementLevel = "advisory"): Policy {
return {
name: "ec2-instance-no-public-ip",
description: "Checks whether Amazon EC2 instances have a public IP association. " +
"This rule applies only to IPv4.",
enforcementLevel: enforcementLevel,
rules: [
typedRule(aws.ec2.Instance.isInstance, it => assert.ok(!it.associatePublicIpAddress,
"An EC2 instance should not have a public IP associated with it.")),
],
};
}

function Ec2VolumeInUseCheck(enforcementLevel: EnforcementLevel = "advisory", checkDeletion: boolean): Policy {
return {
name: "ec2-volume-inuse-check",
description: "Checks whether EBS volumes are attached to EC2 instances. " +
"Optionally checks if EBS volumes are marked for deletion when an instance is terminated.",
enforcementLevel: enforcementLevel,
rules: [
typedRule(aws.ec2.Instance.isInstance, it => {
assert.ok(it.ebsBlockDevices && it.ebsBlockDevices.length > 0,
"EC2 instance has no EBS volumes attached.");

if (checkDeletion) {
for (const vol of it.ebsBlockDevices) {
assert.ok(vol.deleteOnTermination && vol.deleteOnTermination === true,
"ECS instance has an EBS volume that is not marked for termination on delete.");
}
}
}),
],
};
}

export function ElbAccessLoggingEnabled(enforcementLevel: EnforcementLevel = "advisory"): Policy {
return {
name: "elb-logging-enabled",
description: "Checks whether the Application Load Balancers and the Classic Load Balancers have logging enabled.",
enforcementLevel: enforcementLevel,
rules: [
typedRule(aws.elasticloadbalancing.LoadBalancer.isInstance,
it => assert.ok(it.accessLogs && it.accessLogs.enabled, "An ELB should have access logs enabled.")),
typedRule(aws.elasticloadbalancingv2.LoadBalancer.isInstance,
it => assert.ok(it.accessLogs && it.accessLogs.enabled, "An ELB should have access logs enabled.")),
typedRule(aws.applicationloadbalancing.LoadBalancer.isInstance,
it => assert.ok(it.accessLogs && it.accessLogs.enabled, "An ALB should have access logs enabled.")),
],
};
}

export function EncryptedVolumes(enforcementLevel: EnforcementLevel = "advisory", kmsId?: string): Policy {
return {
name: "encrypted-volumes",
description: "Checks whether the EBS volumes that are in an attached state are encrypted. " +
"If you specify the ID of a KMS key for encryption using the kmsId parameter, " +
"the rule checks if the EBS volumes in an attached state are encrypted with that KMS key.",
enforcementLevel: enforcementLevel,
rules: [
typedRule(aws.ec2.Instance.isInstance,
it => {
if (it.ebsBlockDevices && it.ebsBlockDevices.length > 0) {
for (const ebs of it.ebsBlockDevices) {
assert.ok(ebs.encrypted, "EBS volumes should be encrypted.");
if (kmsId) {
assert.ok(ebs.kmsKeyId === kmsId, "EBS volume not encrypted with required key.");
}
}
}
}),
],
};
}
2 changes: 2 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import { PolicyPack } from "@pulumi/policy";

import { compute } from "./compute";
import { elasticsearch } from "./elasticsearch";
import { storage } from "./storage";

Expand All @@ -22,5 +23,6 @@ export const policyPack = new PolicyPack("pulumi-awsguard", {
policies: [
...storage,
...elasticsearch,
...compute,
],
});
3 changes: 3 additions & 0 deletions integration-tests/compute/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: awsguard-test-compute
runtime: nodejs
description: Tests for policy rules related to AWS Compute.
141 changes: 141 additions & 0 deletions integration-tests/compute/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2016-2019, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";

const config = new pulumi.Config();
const testScenario = config.getNumber("scenario");

console.log(`Running test scenario #${testScenario}`);

const ami = aws.getAmi({
filters: [{
name: "name",
values: ["amzn-ami-hvm-*"],
}],
owners: ["137112412989"], // This owner ID is Amazon
mostRecent: true,
});

let ec2InstanceArgs: aws.ec2.InstanceArgs = {
ami: ami.id,
monitoring: true,
instanceType: "t2.micro",
ebsBlockDevices: [{
deviceName: "/dev/test",
encrypted: true,
}],
};

const elbBucket = new aws.s3.Bucket("test-bucket", {});

let elbArgs: aws.elasticloadbalancing.LoadBalancerArgs = {
accessLogs: {
enabled: true,
bucket: elbBucket.arn,
},
listeners: [],
};

let elbV2Args: aws.elasticloadbalancingv2.LoadBalancerArgs = {
accessLogs: {
enabled: true,
bucket: elbBucket.arn,
},
};

let albArgs: aws.applicationloadbalancing.LoadBalancerArgs = {
accessLogs: {
enabled: true,
bucket: elbBucket.arn,
},
};

switch (testScenario) {
case 1:
// Happy Path.
break;
case 2:
// Monitoring is undefined.
ec2InstanceArgs = {
ami: ami.id,
instanceType: "t2.micro",
};
break;
case 3:
// Monitoring is false.
ec2InstanceArgs = {
ami: ami.id,
monitoring: false,
instanceType: "t2.micro",
};
break;
case 4:
// Public IP is associated.
ec2InstanceArgs = {
ami: ami.id,
monitoring: true,
instanceType: "t2.micro",
associatePublicIpAddress: true,
};
break;
case 5:
// Elastic Load Balancers do not have access logs specified.
elbArgs = { listeners: [] };
elbV2Args = {};
albArgs = {};
break;
case 6:
// No EBS volume attached to EC2 instance.
ec2InstanceArgs = {
ami: ami.id,
monitoring: true,
instanceType: "t2.micro",
};
break;
case 7:
// EBS volume will not be deleted.
ec2InstanceArgs = {
ami: ami.id,
monitoring: true,
instanceType: "t2.micro",
ebsBlockDevices: [{
deviceName: "/dev/test",
encrypted: true,
deleteOnTermination: false,
}],
};
break;
case 8:
// EBS volume not encrypted.
ec2InstanceArgs = {
ami: ami.id,
monitoring: true,
instanceType: "t2.micro",
ebsBlockDevices: [{
deviceName: "/dev/test",
deleteOnTermination: false,
encrypted: false,
}],
};
break;
default:
throw new Error(`Unexpected test scenario ${testScenario}`);
}

export const ec2Instance = new aws.ec2.Instance("test-ec2-instance", ec2InstanceArgs);
export const elb = new aws.elasticloadbalancing.LoadBalancer("test-elb", elbArgs);
export const elbV2 = new aws.elasticloadbalancingv2.LoadBalancer("test-elb-v2", elbV2Args);
export const alb = new aws.applicationloadbalancing.LoadBalancer("test-alb", albArgs);
Loading

0 comments on commit bb21069

Please sign in to comment.