Skip to content

Commit

Permalink
fix(ec2): fix misconfiguration of arm-backed bastion host instances (a…
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacob-Doetsch committed Dec 30, 2020
1 parent 32e9c23 commit 8cc7b74
Show file tree
Hide file tree
Showing 5 changed files with 782 additions and 8 deletions.
25 changes: 18 additions & 7 deletions packages/@aws-cdk/aws-ec2/lib/bastion-host.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { IPrincipal, IRole, PolicyStatement } from '@aws-cdk/aws-iam';
import { CfnOutput, Resource, Stack } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { AmazonLinuxGeneration, InstanceClass, InstanceSize, InstanceType } from '.';
import { AmazonLinuxGeneration, InstanceArchitecture, InstanceClass, InstanceSize, InstanceType } from '.';
import { Connections } from './connections';
import { IInstance, Instance } from './instance';
import { IMachineImage, MachineImage } from './machine-image';
import { AmazonLinuxCpuType, IMachineImage, MachineImage } from './machine-image';
import { IPeer } from './peer';
import { Port } from './port';
import { ISecurityGroup } from './security-group';
Expand Down Expand Up @@ -146,14 +146,17 @@ export class BastionHostLinux extends Resource implements IInstance {
constructor(scope: Construct, id: string, props: BastionHostLinuxProps) {
super(scope, id);
this.stack = Stack.of(scope);

const instanceType = props.instanceType ?? InstanceType.of(InstanceClass.T3, InstanceSize.NANO);
this.instance = new Instance(this, 'Resource', {
vpc: props.vpc,
availabilityZone: props.availabilityZone,
securityGroup: props.securityGroup,
instanceName: props.instanceName ?? 'BastionHost',
instanceType: props.instanceType ?? InstanceType.of(InstanceClass.T3, InstanceSize.NANO),
machineImage: props.machineImage ?? MachineImage.latestAmazonLinux({ generation: AmazonLinuxGeneration.AMAZON_LINUX_2 }),
instanceType,
machineImage: props.machineImage ?? MachineImage.latestAmazonLinux({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
cpuType: instanceType.architecture as string as AmazonLinuxCpuType,
}),
vpcSubnets: props.subnetSelection ?? {},
blockDevices: props.blockDevices ?? undefined,
});
Expand All @@ -165,8 +168,6 @@ export class BastionHostLinux extends Resource implements IInstance {
],
resources: ['*'],
}));
this.instance.addUserData('yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm');

this.connections = this.instance.connections;
this.role = this.instance.role;
this.grantPrincipal = this.instance.role;
Expand All @@ -176,13 +177,23 @@ export class BastionHostLinux extends Resource implements IInstance {
this.instancePrivateDnsName = this.instance.instancePrivateDnsName;
this.instancePublicIp = this.instance.instancePublicIp;
this.instancePublicDnsName = this.instance.instancePublicDnsName;
this.applyUserData();

new CfnOutput(this, 'BastionHostId', {
description: 'Instance ID of the bastion host. Use this to connect via SSM Session Manager',
value: this.instanceId,
});
}

/**
* Install the architecture-appropriate SSM Agent package
*/
private applyUserData(): void {
const instanceType = new InstanceType(this.instance.instance.instanceType!);
const ssmAgentArch = instanceType.architecture === InstanceArchitecture.ARM_64 ? 'linux_arm64' : 'linux_amd64';
this.instance.addUserData(`yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/${ssmAgentArch}/amazon-ssm-agent.rpm`);
}

/**
* Allow SSH access from the given peer or peers
*
Expand Down
39 changes: 39 additions & 0 deletions packages/@aws-cdk/aws-ec2/lib/instance-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,21 @@ export enum InstanceClass {
INF1 = 'inf1'
}

/**
* Identifies an instance's CPU architecture
*/
export enum InstanceArchitecture {
/**
* ARM64, Graviton architecture
*/
ARM_64 = 'arm64',

/**
* x86-64 architecture
*/
X86_64 = 'x86_64',
}

/**
* What size of instance to use
*/
Expand Down Expand Up @@ -544,6 +559,7 @@ export enum InstanceSize {
* know the identifier of the type you want.
*/
export class InstanceType {

/**
* Instance type for EC2 instances
*
Expand All @@ -556,7 +572,13 @@ export class InstanceType {
return new InstanceType(`${instanceClass}.${instanceSize}`);
}

/**
* The instance's CPU architecture
*/
public readonly architecture: InstanceArchitecture;

constructor(private readonly instanceTypeIdentifier: string) {
this.architecture = this.resolveArchitecture();
}

/**
Expand All @@ -565,4 +587,21 @@ export class InstanceType {
public toString(): string {
return this.instanceTypeIdentifier;
}

/**
* Returns the CPU architecture for the instance type
*/
private resolveArchitecture(): InstanceArchitecture {
const ARM_INSTANCE_CLASSES = new Set<InstanceClass>([
InstanceClass.A1, InstanceClass.C6G, InstanceClass.C6GD,
InstanceClass.T4G, InstanceClass.M6G, InstanceClass.R6G,
]);
const instanceClass = this.instanceTypeIdentifier.split('.')[0] as InstanceClass;

if (ARM_INSTANCE_CLASSES.has(instanceClass)) {
return InstanceArchitecture.ARM_64;
}

return InstanceArchitecture.X86_64;
}
}
41 changes: 40 additions & 1 deletion packages/@aws-cdk/aws-ec2/test/bastion-host.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, haveResource } from '@aws-cdk/assert';
import { Stack } from '@aws-cdk/core';
import { nodeunitShim, Test } from 'nodeunit-shim';
import { BastionHostLinux, BlockDeviceVolume, SubnetType, Vpc } from '../lib';
import { BastionHostLinux, BlockDeviceVolume, InstanceClass, InstanceSize, InstanceType, SubnetType, Vpc } from '../lib';

nodeunitShim({
'default instance is created in basic'(test: Test) {
Expand Down Expand Up @@ -83,6 +83,45 @@ nodeunitShim({
],
}));

test.done();
},
'x86-64 instances use x86-64 ssm agent package'(test: Test) {
// GIVEN
const stack = new Stack();
const vpc = new Vpc(stack, 'VPC');

// WHEN
new BastionHostLinux(stack, 'Bastion', {
vpc,
});

// THEN
expect(stack).to(haveResource('AWS::EC2::Instance', {
UserData: {
'Fn::Base64': '#!/bin/bash\nyum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm',
},
}));

test.done();
},
'arm instances use arm ssm agent package'(test: Test) {
// GIVEN
const stack = new Stack();
const vpc = new Vpc(stack, 'VPC');

// WHEN
new BastionHostLinux(stack, 'Bastion', {
vpc,
instanceType: InstanceType.of(InstanceClass.T4G, InstanceSize.NANO),
});

// THEN
expect(stack).to(haveResource('AWS::EC2::Instance', {
UserData: {
'Fn::Base64': '#!/bin/bash\nyum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_arm64/amazon-ssm-agent.rpm',
},
}));

test.done();
},
});
Loading

0 comments on commit 8cc7b74

Please sign in to comment.