terraform-iam: configure OIDC for NixOS/nix releases
zimbatm committed
Oct 29, 2025 at 10:53 UTC
79e18baaad01e59312099c9352c625282fbe43e0
3 files changed
+207
terraform-iam/assume_github_actions_policy_document/main.tf
new
+40
@@ -0,0 +1,40 @@
1
+terraform {
2
+ required_providers {
3
+ aws = {
4
+ source = "hashicorp/aws"
5
+ }
6
+ }
7
+}
8
+
9
+variable "subject_filter" {
10
+ type = list(string)
11
+}
12
+
13
+data "aws_caller_identity" "current" {}
14
+
15
+data "aws_iam_openid_connect_provider" "github_actions" {
16
+ url = "https://token.actions.githubusercontent.com"
17
+}
18
+
19
+data "aws_iam_policy_document" "assume_github_actions" {
20
+
21
+ statement {
22
+ effect = "Allow"
23
+ actions = ["sts:AssumeRoleWithWebIdentity"]
24
+
25
+ principals {
26
+ type = "Federated"
27
+ identifiers = [data.aws_iam_openid_connect_provider.github_actions.arn]
28
+ }
29
+
30
+ condition {
31
+ test = "StringLike"
32
+ variable = "token.actions.githubusercontent.com:sub"
33
+ values = var.subject_filter
34
+ }
35
+ }
36
+}
37
+
38
+output "json" {
39
+ value = data.aws_iam_policy_document.assume_github_actions.json
40
+}
terraform-iam/assume_identity_center_permission_policy/main.tf
new
+52
@@ -0,0 +1,52 @@
1
+terraform {
2
+ required_providers {
3
+ aws = {
4
+ source = "hashicorp/aws"
5
+ }
6
+ }
7
+}
8
+
9
+variable "target_account_id" {
10
+ description = "AWS account ID where the reserved SSO roles exist (the target account)."
11
+ type = string
12
+}
13
+
14
+variable "sso_region" {
15
+ description = "Region of the AWS IAM Identity Center instance."
16
+ type = string
17
+ default = "eu-north-1"
18
+}
19
+
20
+variable "permission_set_name" {
21
+ description = "Name of the IAM Identity Center permission set (without the AWSReservedSSO_ prefix)."
22
+ type = string
23
+}
24
+
25
+locals {
26
+ reserved_role_pattern = format(
27
+ "arn:aws:iam::%s:role/aws-reserved/sso.amazonaws.com/%s/AWSReservedSSO_%s_*",
28
+ var.target_account_id,
29
+ var.sso_region,
30
+ var.permission_set_name,
31
+ )
32
+}
33
+
34
+data "aws_iam_policy_document" "this" {
35
+ statement {
36
+ effect = "Allow"
37
+ actions = ["sts:AssumeRole"]
38
+ principals {
39
+ type = "AWS"
40
+ identifiers = [format("arn:aws:iam::%s:root", var.target_account_id)]
41
+ }
42
+ condition {
43
+ test = "ArnLike"
44
+ variable = "aws:PrincipalArn"
45
+ values = [local.reserved_role_pattern]
46
+ }
47
+ }
48
+}
49
+
50
+output "json" {
51
+ value = data.aws_iam_policy_document.this.json
52
+}
terraform-iam/nix_repo_oidc.tf
new
+115
@@ -0,0 +1,115 @@
1
+# In this document we configure OIDC from GitHub to allow automatically
2
+# publishing NixOS/nix releases using GitHub Actions.
3
+#
4
+# This means that everyone with merge rights in the Nix repo can publish
5
+# releases (with a public trail).
6
+
7
+resource "aws_iam_openid_connect_provider" "github_actions" {
8
+ url = "https://token.actions.githubusercontent.com"
9
+
10
+ client_id_list = ["sts.amazonaws.com"]
11
+ thumbprint_list = [
12
+ # https://github.com/aws-actions/configure-aws-credentials/issues/357#issuecomment-1626357333
13
+ "6938fd4d98bab03faadb97b34396831e3780aea1",
14
+ ]
15
+}
16
+
17
+import {
18
+ to = aws_iam_openid_connect_provider.github_actions
19
+ id = format(
20
+ "arn:aws:iam::%s:oidc-provider/token.actions.githubusercontent.com",
21
+ data.aws_caller_identity.current.account_id,
22
+ )
23
+}
24
+
25
+data "aws_iam_policy_document" "nix_release" {
26
+ statement {
27
+ effect = "Allow"
28
+ actions = [
29
+ "s3:GetObject",
30
+ "s3:PutObject"
31
+ ]
32
+ # Only allow uploading in the /nix/ prefix in the bucket
33
+ resources = ["arn:aws:s3:::nix-releases/nix/*"]
34
+ }
35
+
36
+ statement {
37
+ effect = "Allow"
38
+ actions = [
39
+ "s3:ListBucket",
40
+ "s3:ListBucketMultipartUploads"
41
+ ]
42
+ resources = ["arn:aws:s3:::nix-releases"]
43
+ condition {
44
+ test = "StringLike"
45
+ variable = "s3:prefix"
46
+ values = ["nix/*"]
47
+ }
48
+ }
49
+
50
+ statement {
51
+ effect = "Allow"
52
+ actions = [
53
+ "s3:PutObject"
54
+ ]
55
+ # The release also publishes the install script when it's the latest
56
+ # release.
57
+ resources = ["arn:aws:s3:::nix-channels/nix-latest/install"]
58
+ }
59
+
60
+ statement {
61
+ effect = "Allow"
62
+ actions = [
63
+ "s3:ListBucket"
64
+ ]
65
+ resources = ["arn:aws:s3:::nix-channels"]
66
+ condition {
67
+ test = "StringLike"
68
+ variable = "s3:prefix"
69
+ values = ["nix-latest/*"]
70
+ }
71
+ }
72
+}
73
+
74
+resource "aws_iam_policy" "nix_release" {
75
+ name = "nix-release"
76
+ policy = data.aws_iam_policy_document.nix_release.json
77
+}
78
+
79
+data "aws_caller_identity" "current" {}
80
+
81
+module "assume_nix_releases_permission" {
82
+ source = "./assume_identity_center_permission_policy"
83
+ target_account_id = data.aws_caller_identity.current.account_id
84
+ permission_set_name = "NixReleases"
85
+ sso_region = "eu-north-1"
86
+}
87
+
88
+module "assume_nix_release" {
89
+ source = "./assume_github_actions_policy_document"
90
+
91
+ # Only allow to assume this role in the NixOS/nix repo, and while running
92
+ # in the "releases" environment.
93
+ subject_filter = ["repo:NixOS/nix:environment:releases"]
94
+}
95
+
96
+data "aws_iam_policy_document" "assume_nix_release" {
97
+ source_policy_documents = [
98
+ module.assume_nix_releases_permission.json,
99
+ module.assume_nix_release.json,
100
+ ]
101
+}
102
+
103
+resource "aws_iam_role" "nix_release" {
104
+ name = "nix-release"
105
+ assume_role_policy = data.aws_iam_policy_document.assume_nix_release.json
106
+}
107
+
108
+resource "aws_iam_role_policy_attachment" "nix_release_managed_policy" {
109
+ role = aws_iam_role.nix_release.name
110
+ policy_arn = aws_iam_policy.nix_release.arn
111
+}
112
+
113
+output "nix_release_role_arn" {
114
+ value = aws_iam_role.nix_release.arn
115
+}