Skip to content

Commit

Permalink
feat: update GitHub auth, add honeycomb
Browse files Browse the repository at this point in the history
  • Loading branch information
JakePartusch committed Jul 16, 2023
1 parent 52b902a commit aeb3947
Show file tree
Hide file tree
Showing 23 changed files with 1,020 additions and 188 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ node_modules
cdk.out
.env
.sst

.DS_Store
```
35 changes: 0 additions & 35 deletions bin/control-plane.app.ts

This file was deleted.

13 changes: 0 additions & 13 deletions bin/domain.app.ts

This file was deleted.

18 changes: 0 additions & 18 deletions bin/oidc.app.ts

This file was deleted.

6 changes: 6 additions & 0 deletions cdk.context.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"hosted-zone:account=857786057494:domainName=notlify.dev:region=us-east-1": {
"Id": "/hostedzone/Z06816922G44NH91Z579D",
"Name": "notlify.dev."
}
}
115 changes: 105 additions & 10 deletions lib/control-plane.stack.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,72 @@
import { CfnOutput } from "aws-cdk-lib";
import { CfnEventBusPolicy } from "aws-cdk-lib/aws-events";
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
import { LayerVersion } from "aws-cdk-lib/aws-lambda";
import * as sst from "sst/constructs";
import { FunctionProps } from "sst/constructs";

interface ControlPlaneStackProps {
gitHubToken: string;
gitHubWorkflowUrl: string;
dataPlaneAccounts: string[];
orgId: string;
domain: string;
}

const { GH_TOKEN, GH_WORKFLOW_URL, DATA_PLANE_ACCOUNTS, ORG_ID } = process.env;
const {
GH_TOKEN,
GH_WORKFLOW_URL,
DATA_PLANE_ACCOUNTS,
ORG_ID,
DOMAIN,
HONEYCOMB_KEY,
} = process.env;

export function ControlPlaneStack(ctx: sst.StackContext) {
const { stack } = ctx;
const { stack, app } = ctx;
const { stage } = app;

const otelLayer = LayerVersion.fromLayerVersionArn(
stack,
"OtelLayer",
"arn:aws:lambda:us-east-1:901920570463:layer:aws-otel-nodejs-arm64-ver-1-13-0:2"
);

const defaultTracingProps = (): FunctionProps => ({
layers: [otelLayer],
tracing: "pass_through",
copyFiles: [{ from: "src/control-plane/common/collector.yaml" }],
architecture: "arm_64",
runtime: "nodejs18.x",
nodejs: {
esbuild: {
external: [
"@opentelemetry/api",
"@opentelemetry/sdk-node",
"@opentelemetry/auto-instrumentations-node",
"@aws-sdk/client-dynamodb",
"@aws-sdk/client-s3",
"@aws-sdk/client-sts",
"@aws-sdk/client-cloudformation",
],
},
},
});

const getDefaultTracingEnv = () => ({
OTEL_EXPORTER_OTLP_ENDPOINT: `https://api.honeycomb.io`,
OTEL_EXPORTER_OTLP_HEADERS: `x-honeycomb-team=${HONEYCOMB_KEY}`,
OTEL_PROPAGATORS: `tracecontext`,
AWS_LAMBDA_EXEC_WRAPPER: `/opt/otel-handler`,
OPENTELEMETRY_COLLECTOR_CONFIG_FILE: `/var/task/src/control-plane/common/collector.yaml`,
OTEL_TRACES_SAMPLER: "always_on",
});

const props: ControlPlaneStackProps = {
gitHubToken: GH_TOKEN!,
gitHubWorkflowUrl: GH_WORKFLOW_URL!,
dataPlaneAccounts: DATA_PLANE_ACCOUNTS!.split(","),
orgId: ORG_ID!,
domain: DOMAIN!,
};

const dataPlaneAssumePolicyStatements: PolicyStatement[] =
Expand Down Expand Up @@ -50,20 +97,33 @@ export function ControlPlaneStack(ctx: sst.StackContext) {
sourceFilesBucket.addNotifications(stack, {
deploymetnInitiated: {
function: {
handler:
"src/control-plane/lambdas/deployment-initiated.handler.handler",
permissions: [sourceFilesBucket, table],
handler: "src/control-plane/lambdas/deployment-initiated.handler",
permissions: [
sourceFilesBucket,
table,
...dataPlaneAssumePolicyStatements,
],
environment: {
TABLE_NAME: table.tableName,
SOURCE_FILES_BUCKET_NAME: sourceFilesBucket.bucketName,
GITHUB_TOKEN: props.gitHubToken,
GITHUB_WORKFLOW_URL: props.gitHubWorkflowUrl,
...getDefaultTracingEnv(),
OTEL_SERVICE_NAME: `deployment-initiated-${stage}`,
},
...defaultTracingProps(),
},
events: ["object_created"],
},
});

const JWT_SECRET_KEY = new sst.Config.Secret(stack, "JWT_SECRET_KEY");
const JWT_PUBLIC_KEY = new sst.Config.Secret(stack, "JWT_PUBLIC_KEY");
const GITHUB_OAUTH_SECRET = new sst.Config.Secret(
stack,
"GITHUB_OAUTH_SECRET"
);

const api = new sst.Api(stack, "Api", {
cors: {
allowHeaders: ["*"],
Expand All @@ -81,19 +141,47 @@ export function ControlPlaneStack(ctx: sst.StackContext) {
table,
...dataPlaneAssumePolicyStatements,
],
bind: [JWT_PUBLIC_KEY],
environment: {
TABLE_NAME: table.tableName,
SOURCE_FILES_BUCKET_NAME: sourceFilesBucket.bucketName,
GITHUB_TOKEN: props.gitHubToken,
GITHUB_WORKFLOW_URL: props.gitHubWorkflowUrl,
...getDefaultTracingEnv(),
OTEL_SERVICE_NAME: `control-plane-${stage}`,
},
...defaultTracingProps(),
},
},
"ANY /github/callback": {
function: {
handler: "src/control-plane/auth.handler",
bind: [JWT_SECRET_KEY, GITHUB_OAUTH_SECRET],
timeout: 30,
environment: {
...getDefaultTracingEnv(),
OTEL_SERVICE_NAME: `auth-${stage}`,
},
...defaultTracingProps(),
},
},
},
});

new CfnOutput(stack, `API Gateway`, {
value: `${api.url}/api`,
const site = new sst.StaticSite(stack, "web", {
path: "src/control-plane/ui/app",
buildOutput: "public",
buildCommand: "npm run build",
errorPage: "404.html",
environment: {
API_URL: api.url,
},
customDomain: {
domainName:
app.stage === "prod" ? props.domain : `${app.stage}.${props.domain}`,
domainAlias: app.stage === "prod" ? `www.${props.domain}` : undefined,
isExternalDomain: false,
},
});

const eventBus = new sst.EventBus(stack, "SstCloudformationEventBus", {
Expand All @@ -103,13 +191,15 @@ export function ControlPlaneStack(ctx: sst.StackContext) {
targets: {
deploymentProgressHandler: {
function: {
handler:
"src/control-plane/lambdas/deployment-progress.handler.handler",
handler: "src/control-plane/lambdas/deployment-progress.handler",
environment: {
TABLE_NAME: table.tableName,
...getDefaultTracingEnv(),
OTEL_SERVICE_NAME: `deployment-progress-${stage}`,
},
timeout: 60,
permissions: [table, ...dataPlaneAssumePolicyStatements],
...defaultTracingProps(),
},
},
},
Expand All @@ -128,4 +218,9 @@ export function ControlPlaneStack(ctx: sst.StackContext) {
value: props.orgId,
},
});

stack.addOutputs({
ApiEndpoint: api.url,
SiteURL: site.url,
});
}
2 changes: 0 additions & 2 deletions lib/data-plane.construct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,6 @@ export class DataPlaneConstruct extends Construct {
targets: [new EventBusTarget(controlPlaneEventBus)],
});

controlPlaneEventBus.grantPutEventsTo;

new CfnOutput(this, "CrossAccountRoleArnOutput", {
value: crossAccountRole.roleArn,
});
Expand Down
15 changes: 8 additions & 7 deletions lib/domain.stack.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as sst from "sst/constructs";
import { Domain } from "./domain.construct";

export class DomainStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const { DOMAIN } = process.env;

new Domain(this, "NotlifyDomain", {
domainName: "notlify.dev",
export function DomainStack(ctx: sst.StackContext) {
const { stack, app } = ctx;

if (app.stage === "prod") {
new Domain(stack, "NotlifyDomain", {
domainName: DOMAIN!,
});
}
}
26 changes: 17 additions & 9 deletions lib/oidc.stack.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
import * as cdk from "aws-cdk-lib";
import * as sst from "sst/constructs";
import {
ManagedPolicy,
OpenIdConnectProvider,
Role,
WebIdentityPrincipal,
} from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";

export class OidcStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
interface OidcStack {
repo: string;
}

const { REPO } = process.env;

export function OidcStack(ctx: sst.StackContext) {
const { stack, app } = ctx;

const props: OidcStack = {
repo: REPO!,
};

const provider = new OpenIdConnectProvider(this, "GitHubProvider", {
if (app.stage === "prod") {
const provider = new OpenIdConnectProvider(stack, "GitHubProvider", {
url: "https://token.actions.githubusercontent.com",
clientIds: ["sts.amazonaws.com"],
thumbprints: ["6938fd4d98bab03faadb97b34396831e3780aea1"],
});

const deploymentRole = new Role(this, "GitHubDeploymentRole", {
const deploymentRole = new Role(stack, "GitHubDeploymentRole", {
roleName: "GitHubDeployer",
assumedBy: new WebIdentityPrincipal(provider.openIdConnectProviderArn, {
StringLike: {
// Only allow specified subjects to assume this role
[`token.actions.githubusercontent.com:sub`]:
"repo:JakePartusch/notlify:ref:refs/heads/main",
[`token.actions.githubusercontent.com:sub`]: `repo:${props.repo}:ref:refs/heads/main`,
},
StringEquals: {
// Audience is always sts.amazonaws.com with AWS official Github Action
Expand Down
Loading

0 comments on commit aeb3947

Please sign in to comment.