From a273a55e8a0027b96c0fda73bdf1ad587615fcf7 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 9 Dec 2021 11:13:57 -0800 Subject: [PATCH] Split CI OIDC provider into its own stack (#948) --- tools/ci-cdk/bin/ci-cdk.ts | 7 +++- .../ci-cdk/lib/constructs/github-oidc-role.ts | 19 ++-------- tools/ci-cdk/lib/oidc-provider-stack.ts | 35 +++++++++++++++++++ .../lib/smithy-rs/pull-request-cdn-stack.ts | 10 ++++-- .../test/constructs/github-oidc-role.test.ts | 14 +++----- tools/ci-cdk/test/oidc-provider-stack.test.ts | 21 +++++++++++ 6 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 tools/ci-cdk/lib/oidc-provider-stack.ts create mode 100644 tools/ci-cdk/test/oidc-provider-stack.test.ts diff --git a/tools/ci-cdk/bin/ci-cdk.ts b/tools/ci-cdk/bin/ci-cdk.ts index 1bc36afe03..08d3876f58 100644 --- a/tools/ci-cdk/bin/ci-cdk.ts +++ b/tools/ci-cdk/bin/ci-cdk.ts @@ -7,7 +7,12 @@ import "source-map-support/register"; import * as cdk from "@aws-cdk/core"; import { PullRequestCdnStack } from "../lib/smithy-rs/pull-request-cdn-stack"; +import { OidcProviderStack } from "../lib/oidc-provider-stack"; const app = new cdk.App(); -new PullRequestCdnStack(app, "smithy-rs-pull-request-cdn-stack", {}); +const oidcProviderStack = new OidcProviderStack(app, "oidc-provider-stack", {}); + +new PullRequestCdnStack(app, "smithy-rs-pull-request-cdn-stack", { + githubActionsOidcProvider: oidcProviderStack.githubActionsOidcProvider, +}); diff --git a/tools/ci-cdk/lib/constructs/github-oidc-role.ts b/tools/ci-cdk/lib/constructs/github-oidc-role.ts index 4f453a6a17..c98f3ba00b 100644 --- a/tools/ci-cdk/lib/constructs/github-oidc-role.ts +++ b/tools/ci-cdk/lib/constructs/github-oidc-role.ts @@ -6,23 +6,14 @@ import { FederatedPrincipal, OpenIdConnectProvider, Role } from "@aws-cdk/aws-iam"; import { Construct, Tags } from "@aws-cdk/core"; -/// This thumbprint is used to validate GitHub's identity to AWS. -/// -/// It was obtained by following instructions at: -/// https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html -/// -/// This was done with the initial Idp URL of: -/// https://token.actions.githubusercontent.com/.well-known/openid-configuration -const GITHUB_CERTIFICATE_THUMBPRINT = "A031C46782E6E6C662C2C87C76DA9AA62CCABD8E"; - export interface Properties { name: string; githubOrg: string; githubRepo: string; + oidcProvider: OpenIdConnectProvider; } export class GitHubOidcRole extends Construct { - public readonly oidcProvider: OpenIdConnectProvider; public readonly oidcRole: Role; constructor(scope: Construct, id: string, properties: Properties) { @@ -32,16 +23,10 @@ export class GitHubOidcRole extends Construct { Tags.of(this).add("construct-name", properties.name); Tags.of(this).add("construct-type", "GitHubOidcRole"); - this.oidcProvider = new OpenIdConnectProvider(this, "oidc-provider", { - url: "https://token.actions.githubusercontent.com", - thumbprints: [GITHUB_CERTIFICATE_THUMBPRINT], - clientIds: ["sts.amazonaws.com"], - }); - this.oidcRole = new Role(this, "oidc-role", { roleName: `${properties.name}-github-oidc-role`, assumedBy: new FederatedPrincipal( - this.oidcProvider.openIdConnectProviderArn, + properties.oidcProvider.openIdConnectProviderArn, { StringLike: { "token.actions.githubusercontent.com:sub": `repo:${properties.githubOrg}/${properties.githubRepo}:*`, diff --git a/tools/ci-cdk/lib/oidc-provider-stack.ts b/tools/ci-cdk/lib/oidc-provider-stack.ts new file mode 100644 index 0000000000..5624282169 --- /dev/null +++ b/tools/ci-cdk/lib/oidc-provider-stack.ts @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +import { OpenIdConnectProvider } from "@aws-cdk/aws-iam"; +import { Construct, StackProps, Stack, Tags } from "@aws-cdk/core"; + +/// This thumbprint is used to validate GitHub's identity to AWS. +/// +/// It was obtained by following instructions at: +/// https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html +/// +/// This was done with the initial Idp URL of: +/// https://token.actions.githubusercontent.com/.well-known/openid-configuration +const GITHUB_CERTIFICATE_THUMBPRINT = "A031C46782E6E6C662C2C87C76DA9AA62CCABD8E"; + +// There can only be one OIDC provider for a given URL per AWS account, +// so put these in their own stack to be shared with other stacks. +export class OidcProviderStack extends Stack { + public readonly githubActionsOidcProvider: OpenIdConnectProvider; + + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + // Tag the resources created by this stack to make identifying resources easier + Tags.of(this).add("stack", id); + + this.githubActionsOidcProvider = new OpenIdConnectProvider(this, "oidc-provider", { + url: "https://token.actions.githubusercontent.com", + thumbprints: [GITHUB_CERTIFICATE_THUMBPRINT], + clientIds: ["sts.amazonaws.com"], + }); + } +} diff --git a/tools/ci-cdk/lib/smithy-rs/pull-request-cdn-stack.ts b/tools/ci-cdk/lib/smithy-rs/pull-request-cdn-stack.ts index ff663fcb6c..f44840dbbc 100644 --- a/tools/ci-cdk/lib/smithy-rs/pull-request-cdn-stack.ts +++ b/tools/ci-cdk/lib/smithy-rs/pull-request-cdn-stack.ts @@ -3,16 +3,21 @@ * SPDX-License-Identifier: Apache-2.0. */ +import { OpenIdConnectProvider } from "@aws-cdk/aws-iam"; import * as cdk from "@aws-cdk/core"; -import { Duration, RemovalPolicy, Tags } from "@aws-cdk/core"; +import { Duration, RemovalPolicy, StackProps, Tags } from "@aws-cdk/core"; import { CloudFrontS3Cdn } from "../constructs/cloudfront-s3-cdn"; import { GitHubOidcRole } from "../constructs/github-oidc-role"; +export interface Properties extends StackProps { + githubActionsOidcProvider: OpenIdConnectProvider; +} + export class PullRequestCdnStack extends cdk.Stack { public readonly smithyRsOidcRole: GitHubOidcRole; public readonly pullRequestCdn: CloudFrontS3Cdn; - constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { + constructor(scope: cdk.Construct, id: string, props: Properties) { super(scope, id, props); // Tag the resources created by this stack to make identifying resources easier @@ -22,6 +27,7 @@ export class PullRequestCdnStack extends cdk.Stack { name: "smithy-rs-pull-request", githubOrg: "awslabs", githubRepo: "smithy-rs", + oidcProvider: props.githubActionsOidcProvider, }); this.pullRequestCdn = new CloudFrontS3Cdn(this, "pull-request-cdn", { diff --git a/tools/ci-cdk/test/constructs/github-oidc-role.test.ts b/tools/ci-cdk/test/constructs/github-oidc-role.test.ts index 7e07bbd3e2..091ebdf170 100644 --- a/tools/ci-cdk/test/constructs/github-oidc-role.test.ts +++ b/tools/ci-cdk/test/constructs/github-oidc-role.test.ts @@ -7,25 +7,21 @@ import { Match, Template } from "@aws-cdk/assertions"; import * as cdk from "@aws-cdk/core"; import { Stack } from "@aws-cdk/core"; import { GitHubOidcRole } from "../../lib/constructs/github-oidc-role"; +import { OidcProviderStack } from "../../lib/oidc-provider-stack"; -test("it should have an OIDC provider and access role", () => { +test("it should have an OIDC access role", () => { const app = new cdk.App(); + const oidcStack = new OidcProviderStack(app, "oidc-provider-stack", {}); const stack = new Stack(app, "test-stack"); new GitHubOidcRole(stack, "test", { name: "some-name", githubOrg: "some-org", githubRepo: "some-repo", + oidcProvider: oidcStack.githubActionsOidcProvider, }); const template = Template.fromStack(stack); - // Verify the OIDC provider - template.hasResourceProperties("Custom::AWSCDKOpenIdConnectProvider", { - ClientIDList: ["sts.amazonaws.com"], - ThumbprintList: ["A031C46782E6E6C662C2C87C76DA9AA62CCABD8E"], - Url: "https://token.actions.githubusercontent.com", - }); - // Verify the OIDC role to be assumed template.hasResourceProperties( "AWS::IAM::Role", @@ -42,7 +38,7 @@ test("it should have an OIDC provider and access role", () => { }, Principal: { Federated: { - Ref: Match.anyValue(), + "Fn::ImportValue": Match.anyValue(), }, }, Effect: "Allow", diff --git a/tools/ci-cdk/test/oidc-provider-stack.test.ts b/tools/ci-cdk/test/oidc-provider-stack.test.ts new file mode 100644 index 0000000000..bb3475a991 --- /dev/null +++ b/tools/ci-cdk/test/oidc-provider-stack.test.ts @@ -0,0 +1,21 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +import { Template } from "@aws-cdk/assertions"; +import * as cdk from "@aws-cdk/core"; +import { OidcProviderStack } from "../lib/oidc-provider-stack"; + +test("it should have an OIDC provider", () => { + const app = new cdk.App(); + const stack = new OidcProviderStack(app, "oidc-provider-stack", {}); + const template = Template.fromStack(stack); + + // Verify the OIDC provider + template.hasResourceProperties("Custom::AWSCDKOpenIdConnectProvider", { + ClientIDList: ["sts.amazonaws.com"], + ThumbprintList: ["A031C46782E6E6C662C2C87C76DA9AA62CCABD8E"], + Url: "https://token.actions.githubusercontent.com", + }); +});