Skip to content

Commit 347c982

Browse files
ruzickapCopilot
andauthored
feat: Add Route53 entry and IAM role for GitHub Actions OIDC integration (#12)
* feat: add Route53 entry and IAM role for GH Actions OIDC, update mise configuration * fix: correct syntax for token.actions.githubusercontent.com in IAM role policy * Update cloudformation/route53-gh-action-iam-role-oidc.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: correct casing for ManagedPolicyArns in IAM role CloudFormation template * fix: correct casing for ManagedPolicyArns in CloudFormation deployment script --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 95a6e96 commit 347c982

File tree

6 files changed

+227
-141
lines changed

6 files changed

+227
-141
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
# pre-commit configuration file
22
.pre-commit-config.yaml
3+
4+
# mise local files (https://mise.jdx.dev/configuration/environments.html#config-environments)
5+
mise.local.toml
6+
mise.*.local.toml

README.md

Lines changed: 116 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,130 @@
11
# k8s-multicluster-gitops
22

3-
Infrastructure as Code for provisioning multiple Kubernetes clusters, managed
4-
using GitOps with ArgoCD
3+
Infrastructure as Code (IaC) for provisioning and managing multiple Kubernetes
4+
clusters across multiple cloud accounts, using GitOps principles with ArgoCD.
55

6-
Create all "kind" clusters:
6+
## Requirements
7+
8+
Guides on setting up Kubernetes clusters in the cloud are common, but few cover
9+
managing clusters across multiple providers and accounts (a key need for large
10+
enterprises)
11+
12+
This project aims to provide a practical example:
13+
14+
- ✅ Provisioning and managing Kubernetes clusters across multiple cloud
15+
providers (AWS, Azure, GCP).
16+
- ✅ Deploying and maintaining Kubernetes clusters across multiple accounts and
17+
regions.
18+
19+
## Architecture
20+
21+
You likely need to deploy multiple Kubernetes clusters across various cloud
22+
providers and accounts.
23+
24+
Each cloud provider has a designated "primary account" where subdomains are hosted:
25+
26+
- `aws.mylabs.dev` - AWS
27+
- `az.mylabs.dev` - Azure
28+
- `gcp.mylabs.dev` - Google Cloud Platform
29+
30+
> The second-level domain `mylabs.dev` is hosted externally (e.g., Cloudflare),
31+
> and it's the user's responsibility to configure DNS delegation properly.
32+
33+
An IAM role (or the equivalent for each cloud provider) will be created in the
34+
primary account. This role will allow GitHub Actions / mise to manage resources
35+
in the primary account and will also be used to access other accounts where
36+
Kubernetes clusters are deployed.
37+
38+
## Cloud Providers - Multi-Account Setup
39+
40+
Let's assume you have 2 AWS accounts, 2 Azure accounts, 2 GCP accounts and you
41+
want to deploy 2 Kubernetes clusters (EKS, AKS, GKE) in each account:
42+
43+
| Cloud Provider | Account 01 | Account 02 |
44+
|--------------------------------------------------|--------------------------------------------------------------|--------------------------------------------------------------|
45+
| **AWS** (`aws.mylabs.dev`, `k8s.aws.mylabs.dev`) | `k01.k8s.aws.mylabs.dev` (US), `k02.k8s.aws.mylabs.dev` (EU) | `k03.k8s.aws.mylabs.dev` (US), `k04.k8s.aws.mylabs.dev` (EU) |
46+
| **Azure** (`az.mylabs.dev`, `k8s.az.mylabs.dev`) | `k01.k8s.az.mylabs.dev` (US), `k02.k8s.az.mylabs.dev` (EU) | `k03.k8s.az.mylabs.dev` (US), `k04.k8s.az.mylabs.dev` (EU) |
47+
| **GCP** (`gcp.mylabs.dev`, `k8s.gcp.mylabs.dev`) | `k01.k8s.gcp.mylabs.dev` (US), `k02.k8s.gcp.mylabs.dev` (EU) | `k03.k8s.gcp.mylabs.dev` (US), `k04.k8s.gcp.mylabs.dev` (EU) |
48+
49+
### AWS
50+
51+
#### Primary account
52+
53+
Choose one of your AWS accounts to act as the **primary account** and create a
54+
Route 53 hosted zone for `aws.mylabs.dev`
55+
56+
> Ensure that the necessary environment variables are set for the AWS CLI
57+
> (e.g., `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`).
58+
59+
```bash
60+
mise run create:aws-primary:cf-route53-gh-action-iam-role-oidc
61+
```
62+
63+
> For more details please inspect the [mise.toml](./mise.toml) file.
64+
65+
Next, manually set up the DNS delegation between your second-level domain
66+
`mylabs.dev` and the `aws.mylabs.dev` hosted zone in Route 53.
67+
68+
Example:
69+
70+
![Cloudflare DNS records for mylabs.dev](images/cloudflare-mylabs-dev-dns-records.avif)
71+
72+
#### Tenant Accounts
73+
74+
Create an IAM role in each tenant account that allows the primary account to
75+
assume a role in the tenant account.
76+
77+
> Make sure to use AWS credentials (`AWS_ACCESS_KEY_ID`,
78+
> `AWS_SECRET_ACCESS_KEY`) for the tenant account.
79+
80+
```bash
81+
mise run create:aws-tenant:cf-iam-role
82+
```
83+
84+
### Azure
85+
86+
### GCP
87+
88+
## K8s Clusters
89+
90+
All the "kubeconfig files" will be stored in the `clusters/.kubeconfigs`
91+
directory.
92+
93+
### Kind
94+
95+
The [kind clusters](https://kind.sigs.k8s.io/) are created using the `kind`
96+
tool, which is a tool for running Kubernetes clusters in Docker containers.
797

898
```bash
9-
mise task run "create:kind:*"
10-
mise task run "delete:kind:*"
99+
mise run create:kind:kind01-internal
100+
mise run delete:kind:kind01-internal
101+
mise run create:kind:kind02-internal
102+
mise run delete:kind:kind02-internal
103+
mise run create-kind-all
104+
mise run delete-kind-all
11105
```
12106

13-
Create all "k3d" clusters:
107+
### K3d
108+
109+
The [k3d clusters](https://k3d.io/) are created using the `k3d` tool, which
110+
is a lightweight wrapper to run `k3s` in Docker containers.
14111

15112
```bash
16-
mise task run "create:k3d:*"
17-
mise task run "delete:k3d:*"
113+
mise run create:k3d:k3d01-internal
114+
mise run delete:k3d:k3d01-internal
115+
mise run create:k3d:k3d02-internal
116+
mise run delete:k3d:k3d02-internal
117+
mise run create-k3d-all
118+
mise run delete-k3d-all
18119
```
19120

20-
> Same for eksctl, az, terraform-aws, terraform-az, ... clusters
121+
> You can also create all the clusters at once using the `create-all` and
122+
> `delete-all` commands:
123+
>
124+
> ```bash
125+
> mise run "create-kind-all" ::: "create-k3d-all"
126+
> mise run "delete-kind-all" ::: "delete-k3d-all"
127+
> ```
21128
22129
## Architecture diagrams
23130
@@ -34,10 +141,6 @@ flowchart TB
34141
aws.mylabs.dev@{ icon: "logos:aws-route53", form: "circle", label: "aws.mylabs.dev", pos: "b", h: 60 }
35142
k8s.aws.mylabs.dev@{ icon: "logos:aws-route53", form: "square", label: "k8s.aws.mylabs.dev", pos: "b", h: 60 }
36143
end
37-
subgraph "AWS Account 03"
38-
k05.k8s.aws.mylabs.dev@{ icon: "logos:aws-route53", form: "square", label: "k05.k8s.aws.mylabs.dev", pos: "b", h: 60 }
39-
k06.k8s.aws.mylabs.dev@{ icon: "logos:aws-route53", form: "square", label: "k06.k8s.aws.mylabs.dev", pos: "b", h: 60 }
40-
end
41144
subgraph "AWS Account 02"
42145
k03.k8s.aws.mylabs.dev@{ icon: "logos:aws-route53", form: "square", label: "k03.k8s.aws.mylabs.dev", pos: "b", h: 60 }
43146
k04.k8s.aws.mylabs.dev@{ icon: "logos:aws-route53", form: "square", label: "k04.k8s.aws.mylabs.dev", pos: "b", h: 60 }
@@ -53,10 +156,6 @@ flowchart TB
53156
az.mylabs.dev@{ icon: "logos:azure-icon", form: "circle", label: "az.mylabs.dev", pos: "b", h: 60 }
54157
k8s.az.mylabs.dev@{ icon: "logos:azure-icon", form: "square", label: "k8s.az.mylabs.dev", pos: "b", h: 60 }
55158
end
56-
subgraph "Azure Account 03"
57-
k05.k8s.az.mylabs.dev@{ icon: "logos:azure-icon", form: "square", label: "k05.k8s.az.mylabs.dev", pos: "b", h: 60 }
58-
k06.k8s.az.mylabs.dev@{ icon: "logos:azure-icon", form: "square", label: "k06.k8s.az.mylabs.dev", pos: "b", h: 60 }
59-
end
60159
subgraph "Azure Account 02"
61160
k03.k8s.az.mylabs.dev@{ icon: "logos:azure-icon", form: "square", label: "k03.k8s.az.mylabs.dev", pos: "b", h: 60 }
62161
k04.k8s.az.mylabs.dev@{ icon: "logos:azure-icon", form: "square", label: "k04.k8s.az.mylabs.dev", pos: "b", h: 60 }
@@ -72,10 +171,6 @@ flowchart TB
72171
gcp.mylabs.dev@{ icon: "logos:google-cloud", form: "circle", label: "gcp.mylabs.dev", pos: "b", h: 60 }
73172
k8s.gcp.mylabs.dev@{ icon: "logos:google-cloud", form: "square", label: "k8s.gcp.mylabs.dev", pos: "b", h: 60 }
74173
end
75-
subgraph "GCP Account 03"
76-
k05.k8s.gcp.mylabs.dev@{ icon: "logos:google-cloud", form: "square", label: "k05.k8s.gcp.mylabs.dev", pos: "b", h: 60 }
77-
k06.k8s.gcp.mylabs.dev@{ icon: "logos:google-cloud", form: "square", label: "k06.k8s.gcp.mylabs.dev", pos: "b", h: 60 }
78-
end
79174
subgraph "GCP Account 02"
80175
k03.k8s.gcp.mylabs.dev@{ icon: "logos:google-cloud", form: "square", label: "k03.k8s.gcp.mylabs.dev", pos: "b", h: 60 }
81176
k04.k8s.gcp.mylabs.dev@{ icon: "logos:google-cloud", form: "square", label: "k04.k8s.gcp.mylabs.dev", pos: "b", h: 60 }
@@ -92,28 +187,20 @@ flowchart TB
92187
k8s.aws.mylabs.dev --> k02.k8s.aws.mylabs.dev
93188
k8s.aws.mylabs.dev --> k03.k8s.aws.mylabs.dev
94189
k8s.aws.mylabs.dev --> k04.k8s.aws.mylabs.dev
95-
k8s.aws.mylabs.dev --> k05.k8s.aws.mylabs.dev
96-
k8s.aws.mylabs.dev --> k06.k8s.aws.mylabs.dev
97190
mylabs.dev --> az.mylabs.dev
98191
az.mylabs.dev --> k8s.az.mylabs.dev
99192
k8s.az.mylabs.dev --> k01.k8s.az.mylabs.dev
100193
k8s.az.mylabs.dev --> k02.k8s.az.mylabs.dev
101194
k8s.az.mylabs.dev --> k03.k8s.az.mylabs.dev
102195
k8s.az.mylabs.dev --> k04.k8s.az.mylabs.dev
103-
k8s.az.mylabs.dev --> k05.k8s.az.mylabs.dev
104-
k8s.az.mylabs.dev --> k06.k8s.az.mylabs.dev
105196
mylabs.dev --> gcp.mylabs.dev
106197
gcp.mylabs.dev --> k8s.gcp.mylabs.dev
107198
k8s.gcp.mylabs.dev --> k01.k8s.gcp.mylabs.dev
108199
k8s.gcp.mylabs.dev --> k02.k8s.gcp.mylabs.dev
109200
k8s.gcp.mylabs.dev --> k03.k8s.gcp.mylabs.dev
110201
k8s.gcp.mylabs.dev --> k04.k8s.gcp.mylabs.dev
111-
k8s.gcp.mylabs.dev --> k05.k8s.gcp.mylabs.dev
112-
k8s.gcp.mylabs.dev --> k06.k8s.gcp.mylabs.dev
113202
```
114203
115-
---
116-
117204
## Tests
118205

119206
```bash

cloudformation/aws-cf-route53-gh-action-iam-role-oidc.yml

Lines changed: 0 additions & 93 deletions
This file was deleted.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
AWSTemplateFormatVersion: 2010-09-09
2+
3+
Description: Route53 entry and IAM role for GitHub Actions OIDC federated role
4+
5+
Parameters:
6+
PrimaryDomain:
7+
Description: "Primary domain for AWS services (e.g. aws.mylabs.dev)"
8+
Type: String
9+
K8sDomain:
10+
Description: "K8s domain hosting cluster subdomains (e.g. k8s.aws.mylabs.dev)"
11+
Type: String
12+
GithubActionsThumbprint:
13+
Type: CommaDelimitedList
14+
Default: 6938fd4d98bab03faadb97b34396831e3780aea1
15+
Description: >
16+
Comma separated list of thumbprints for GitHub Actions tokens.
17+
Default comes from https://github.blog/changelog/2022-01-13-github-actions-update-on-oidc-based-deployments-to-aws/
18+
AudienceList:
19+
Type: CommaDelimitedList
20+
Default: sts.amazonaws.com
21+
Description: >
22+
Comma separated list of allowed audience for the tokens.
23+
Default is audience for the official AWS configure action from https://github.com/aws-actions/configure-aws-credentials
24+
SubjectClaimFilters:
25+
Type: CommaDelimitedList
26+
Description: >
27+
Subject claim filter for valid tokens.
28+
Default allows any branch or tag of the McK-Internal/VM-wiz-automation to assume the role.
29+
See https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims
30+
for examples of filtering by branch or deployment environment.
31+
ManagedPolicyArns:
32+
Type: CommaDelimitedList
33+
Description: Comma separated list for arns for managed policies to attach to the role
34+
RoleName:
35+
Description: "Name of the new IAM role used by GitHub Actions"
36+
Type: String
37+
38+
Resources:
39+
PrimaryHostedZone:
40+
Type: AWS::Route53::HostedZone
41+
Properties:
42+
Name: !Ref PrimaryDomain
43+
HostedZoneConfig:
44+
Comment: Primary domain for AWS services
45+
K8sHostedZone:
46+
Type: AWS::Route53::HostedZone
47+
Properties:
48+
Name: !Ref K8sDomain
49+
HostedZoneConfig:
50+
Comment: K8s domain hosting cluster subdomains
51+
nsRootPrimaryHostedZoneRecordSet:
52+
Type: "AWS::Route53::RecordSet"
53+
Properties:
54+
HostedZoneId: !Ref PrimaryHostedZone
55+
Name: !Sub "${K8sDomain}."
56+
Type: NS
57+
TTL: 86400
58+
ResourceRecords: !GetAtt K8sHostedZone.NameServers
59+
GitHubIdentityProvider:
60+
Type: AWS::IAM::OIDCProvider
61+
Properties:
62+
ClientIdList: !Ref AudienceList
63+
ThumbprintList: !Ref GithubActionsThumbprint
64+
Url: https://token.actions.githubusercontent.com
65+
GitHubActionsServiceRole:
66+
Type: AWS::IAM::Role
67+
Properties:
68+
AssumeRolePolicyDocument:
69+
Version: "2012-10-17"
70+
Statement:
71+
- Sid: RoleForGitHubActions
72+
Effect: Allow
73+
Principal:
74+
Federated: !Ref GitHubIdentityProvider
75+
Action:
76+
- "sts:AssumeRoleWithWebIdentity"
77+
Condition:
78+
StringLike:
79+
"token.actions.githubusercontent.com:sub": !Ref SubjectClaimFilters
80+
Description: Service Role for use in GitHub Actions
81+
RoleName: !Ref RoleName
82+
MaxSessionDuration: 36000
83+
ManagedPolicyArns: !Ref ManagedPolicyArns
84+
85+
Outputs:
86+
GitHubActionsServiceRoleArn:
87+
Description: Arn of service role for use in GitHub actions
88+
Value: !GetAtt GitHubActionsServiceRole.Arn
67.7 KB
Binary file not shown.

0 commit comments

Comments
 (0)