Skip to content

Commit

Permalink
Vpc: bastion private key
Browse files Browse the repository at this point in the history
  • Loading branch information
fwang committed Oct 2, 2024
1 parent 7bb41b0 commit d5bbd90
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 4 deletions.
80 changes: 76 additions & 4 deletions platform/src/components/aws/vpc.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { ComponentResourceOptions, Output, all, output } from "@pulumi/pulumi";
import { Component, Transform, transform } from "../component";
import {
ComponentResourceOptions,
Output,
all,
interpolate,
output,
} from "@pulumi/pulumi";
import { $print, Component, Transform, transform } from "../component";
import { Input } from "../input";
import {
autoscaling,
ec2,
getAvailabilityZonesOutput,
iam,
servicediscovery,
ssm,
} from "@pulumi/aws";
import { Vpc as VpcV1 } from "./vpc-v1";
import { Link } from "../link";
import { VisibleError } from "../error";
import { PrivateKey } from "@pulumi/tls";
export type { VpcArgs as VpcV1Args } from "./vpc-v1";

export interface VpcArgs {
Expand Down Expand Up @@ -136,6 +144,7 @@ interface VpcRef {
elasticIps: Output<ec2.Eip[]>;
bastionInstance: Output<ec2.Instance | undefined>;
cloudmapNamespace: servicediscovery.PrivateDnsNamespace;
privateKeyValue: Output<string | undefined>;
}

/**
Expand Down Expand Up @@ -193,6 +202,7 @@ export class Vpc extends Component implements Link.Linkable {
private privateRouteTables: Output<ec2.RouteTable[]>;
private bastionInstance: Output<ec2.Instance | undefined>;
private cloudmapNamespace: servicediscovery.PrivateDnsNamespace;
private privateKeyValue: Output<string | undefined>;
public static v1 = VpcV1;

constructor(name: string, args?: VpcArgs, opts?: ComponentResourceOptions) {
Expand All @@ -202,6 +212,8 @@ export class Vpc extends Component implements Link.Linkable {
_message: `To continue using the previous version, rename "Vpc" to "Vpc.v${$cli.state.version[name]}". Or recreate this component to update - https://sst.dev/docs/component/aws/cluster#forceupgrade`,
});

const parent = this;

if (args && "ref" in args) {
const ref = args as VpcRef;
this.vpc = ref.vpc;
Expand All @@ -216,14 +228,16 @@ export class Vpc extends Component implements Link.Linkable {
this.elasticIps = ref.elasticIps;
this.bastionInstance = ref.bastionInstance;
this.cloudmapNamespace = ref.cloudmapNamespace;
this.privateKeyValue = ref.privateKeyValue;
registerOutputs();
return;
}
const parent = this;

const zones = normalizeAz();
const nat = normalizeNat();

const vpc = createVpc();
const { keyPair, privateKeyValue } = createKeyPair();
const internetGateway = createInternetGateway();
const securityGroup = createSecurityGroup();
const { publicSubnets, publicRouteTables } = createPublicSubnets();
Expand All @@ -245,6 +259,23 @@ export class Vpc extends Component implements Link.Linkable {
this.privateRouteTables = privateRouteTables;
this.bastionInstance = output(bastionInstance);
this.cloudmapNamespace = cloudmapNamespace;
this.privateKeyValue = output(privateKeyValue);
registerOutputs();

function registerOutputs() {
parent.registerOutputs({
_tunnel: all([parent.bastionInstance, parent.privateKeyValue]).apply(
([bastion, privateKeyValue]) => {
if (!bastion) return;
return {
ip: bastion.publicIp,
username: "ec2-user",
privateKey: privateKeyValue!,
};
},
),
});
}

function normalizeAz() {
const zones = getAvailabilityZonesOutput(
Expand Down Expand Up @@ -279,6 +310,35 @@ export class Vpc extends Component implements Link.Linkable {
);
}

function createKeyPair() {
if (!args?.bastion) return {};

const tlsPrivateKey = new PrivateKey(
`${name}TlsPrivateKey`,
{
algorithm: "RSA",
rsaBits: 4096,
},
{ parent },
);

new ssm.Parameter(`${name}PrivateKey`, {
name: interpolate`/sst/vpc/${vpc.id}/private-key`,
type: ssm.ParameterType.SecureString,
value: tlsPrivateKey.privateKeyOpenssh,
});

const keyPair = new ec2.KeyPair(
`${name}KeyPair`,
{
publicKey: tlsPrivateKey.publicKeyOpenssh,
},
{ parent },
);

return { keyPair, privateKeyValue: tlsPrivateKey.privateKeyOpenssh };
}

function createInternetGateway() {
return new ec2.InternetGateway(
...transform(
Expand Down Expand Up @@ -446,6 +506,7 @@ export class Vpc extends Component implements Link.Linkable {
vpcSecurityGroupIds: [sg.id],
iamInstanceProfile: instanceProfile.name,
sourceDestCheck: false,
keyName: keyPair?.keyName,
tags: {
Name: `${name} NAT Instance`,
"sst:lookup-type": "nat",
Expand Down Expand Up @@ -579,7 +640,7 @@ export class Vpc extends Component implements Link.Linkable {
}

function createBastion() {
if (!args?.bastion) return output(undefined);
if (!args?.bastion) return undefined;

return natInstances.apply((natInstances) => {
if (natInstances.length) return natInstances[0];
Expand Down Expand Up @@ -663,6 +724,7 @@ export class Vpc extends Component implements Link.Linkable {
subnetId: publicSubnets.apply((v) => v[0].id),
vpcSecurityGroupIds: [sg.id],
iamInstanceProfile: instanceProfile.name,
keyName: keyPair?.keyName,
tags: {
"sst:lookup-type": "bastion",
},
Expand Down Expand Up @@ -934,6 +996,15 @@ export class Vpc extends Component implements Link.Linkable {
{ vpc: vpcID },
);

const privateKeyValue = bastionInstance.apply((v) => {
if (!v) return;
const param = ssm.Parameter.get(
`${name}PrivateKey`,
interpolate`/sst/vpc/${vpc.id}/private-key`,
);
return param.value;
});

return new Vpc(name, {
ref: true,
vpc,
Expand All @@ -948,6 +1019,7 @@ export class Vpc extends Component implements Link.Linkable {
elasticIps,
bastionInstance,
cloudmapNamespace,
privateKeyValue: output(privateKeyValue),
} satisfies VpcRef as VpcArgs);
}

Expand Down
6 changes: 6 additions & 0 deletions platform/src/components/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export class Component extends ComponentResource {
args.type === "pulumi-nodejs:dynamic:Resource" ||
args.type === "random:index/randomId:RandomId" ||
args.type === "random:index/randomPassword:RandomPassword" ||
args.type === "tls:index/privateKey:PrivateKey" ||
// resources manually named
[
"aws:appsync/dataSource:DataSource",
Expand Down Expand Up @@ -256,6 +257,11 @@ export class Component extends ComponentResource {
field: "name",
cb: () => physicalName(255, args.name).toLowerCase(),
},
{
types: ["aws:ec2/keyPair:KeyPair"],
field: "keyName",
cb: () => physicalName(255, args.name),
},
{
types: [
"aws:ec2/eip:Eip",
Expand Down

0 comments on commit d5bbd90

Please sign in to comment.