main
tf 266 lines 6.47 KB
Raw
1 locals {
2 releases_domain = "releases.nixos.org"
3
4 releases_index = templatefile("${path.module}/s3_listing.html.tpl", {
5 bucket_name = aws_s3_bucket.releases.bucket
6 bucket_url = "https://${aws_s3_bucket.releases.bucket_domain_name}"
7 bucket_website = "https://${local.releases_domain}"
8 })
9
10 releases_backend = "nix-releases.s3-eu-west-1.amazonaws.com"
11 }
12
13 resource "aws_s3_bucket" "releases" {
14 bucket = "nix-releases"
15 }
16
17 resource "aws_s3_bucket_lifecycle_configuration" "releases" {
18 bucket = aws_s3_bucket.releases.id
19
20
21 transition_default_minimum_object_size = "varies_by_storage_class"
22 rule {
23 id = "tf-s3-lifecycle-20230907091915137900000001"
24 status = "Enabled"
25
26 filter {
27 prefix = ""
28 }
29
30 transition {
31 days = 365
32 storage_class = "STANDARD_IA"
33 }
34 }
35 }
36
37 import {
38 id = aws_s3_bucket.releases.id
39 to = aws_s3_bucket_lifecycle_configuration.releases
40 }
41
42 resource "aws_s3_bucket_cors_configuration" "releases" {
43 bucket = aws_s3_bucket.releases.id
44
45 cors_rule {
46 allowed_headers = ["*"]
47 allowed_methods = ["HEAD", "GET"]
48 allowed_origins = ["*"]
49 expose_headers = ["ETag"]
50 max_age_seconds = 3600
51 }
52 }
53
54 import {
55 to = aws_s3_bucket_cors_configuration.releases
56 id = aws_s3_bucket.releases.id
57 }
58
59 resource "aws_s3_bucket_object" "releases-index-html" {
60 acl = "public-read"
61 bucket = aws_s3_bucket.releases.bucket
62 content_type = "text/html"
63 etag = md5(local.releases_index)
64 key = "index.html"
65 content = local.releases_index
66 }
67
68 resource "aws_s3_bucket_policy" "releases" {
69 bucket = aws_s3_bucket.releases.id
70 policy = <<EOF
71 {
72 "Version": "2008-10-17",
73 "Statement": [
74 {
75 "Sid": "AllowPublicRead",
76 "Effect": "Allow",
77 "Principal": {
78 "AWS": "*"
79 },
80 "Action": "s3:GetObject",
81 "Resource": "arn:aws:s3:::nix-releases/*"
82 },
83 {
84 "Sid": "AllowPublicList",
85 "Effect": "Allow",
86 "Principal": {
87 "AWS": "*"
88 },
89 "Action": [
90 "s3:ListBucket",
91 "s3:GetBucketLocation"
92 ],
93 "Resource": "arn:aws:s3:::nix-releases"
94 },
95 {
96 "Sid": "AllowUpload",
97 "Effect": "Allow",
98 "Principal": {
99 "AWS": [
100 "arn:aws:iam::080433136561:user/s3-upload-releases",
101 "arn:aws:iam::065343343465:user/nixos-s3-upload-releases"
102 ]
103 },
104 "Action": [
105 "s3:PutObject",
106 "s3:PutObjectAcl"
107 ],
108 "Resource": "arn:aws:s3:::nix-releases/*"
109 }
110 ]
111 }
112 EOF
113 }
114
115 resource "fastly_service_vcl" "releases" {
116 name = local.releases_domain
117 default_ttl = 86400
118
119 backend {
120 address = local.releases_backend
121 auto_loadbalance = false
122 between_bytes_timeout = 10000
123 connect_timeout = 5000
124 error_threshold = 0
125 first_byte_timeout = 15000
126 max_conn = 200
127 name = local.releases_backend
128 override_host = local.releases_backend
129 port = 443
130 shield = "dub-dublin-ie"
131 ssl_cert_hostname = local.releases_backend
132 ssl_check_cert = true
133 use_ssl = true
134 weight = 100
135 }
136
137 request_setting {
138 name = "Redirect HTTP to HTTPS"
139 force_ssl = true
140 }
141
142 condition {
143 name = "Generated by synthetic response for 404 page"
144 priority = 0
145 statement = "beresp.status == 404"
146 type = "CACHE"
147 }
148
149 condition {
150 name = "Match /"
151 priority = 10
152 statement = "req.url ~ \"^/$\""
153 type = "REQUEST"
154 }
155
156 domain {
157 name = local.releases_domain
158 }
159
160 header {
161 action = "set"
162 destination = "url"
163 ignore_if_set = false
164 name = "Landing page"
165 priority = 10
166 request_condition = "Match /"
167 source = "\"/index.html\""
168 type = "request"
169 }
170
171 # Clean headers for caching
172 header {
173 destination = "http.x-amz-request-id"
174 type = "cache"
175 action = "delete"
176 name = "remove x-amz-request-id"
177 }
178 header {
179 destination = "http.x-amz-version-id"
180 type = "cache"
181 action = "delete"
182 name = "remove x-amz-version-id"
183 }
184 header {
185 destination = "http.x-amz-id-2"
186 type = "cache"
187 action = "delete"
188 name = "remove x-amz-id-2"
189 }
190
191 # Allow CORS GET requests.
192 header {
193 destination = "http.access-control-allow-origin"
194 type = "cache"
195 action = "set"
196 name = "CORS Allow"
197 source = "\"*\""
198 }
199
200 response_object {
201 cache_condition = "Generated by synthetic response for 404 page"
202 content = "404"
203 content_type = "text/html"
204 name = "Generated by synthetic response for 404 page"
205 response = "Not Found"
206 status = 404
207 }
208
209 snippet {
210 content = "set req.url = querystring.remove(req.url);"
211 name = "Remove all query strings"
212 priority = 50
213 type = "recv"
214 }
215
216 # Work around the 2GB size limit for large files
217 #
218 # See https://docs.fastly.com/en/guides/segmented-caching
219 snippet {
220 content = <<-EOT
221 if (req.url.path ~ "^/nixos/") {
222 set req.enable_segmented_caching = true;
223 }
224 EOT
225 name = "Enable segment caching for ISOs and friends"
226 priority = 60
227 type = "recv"
228 }
229
230 snippet {
231 content = <<-EOT
232 if (beresp.status == 403) {
233 set beresp.status = 404;
234 set beresp.ttl = 86400s;
235 set beresp.grace = 0s;
236 set beresp.cacheable = true;
237 }
238 EOT
239 name = "Change 403 from S3 to 404"
240 priority = 100
241 type = "fetch"
242 }
243
244 logging_s3 {
245 name = "${local.releases_domain}-to-s3"
246 bucket_name = local.fastlylogs["bucket_name"]
247 compression_codec = "zstd"
248 domain = local.fastlylogs["s3_domain"]
249 format = local.fastlylogs["format"]
250 format_version = 2
251 path = "${local.releases_domain}/"
252 period = local.fastlylogs["period"]
253 message_type = "blank"
254 s3_iam_role = local.fastlylogs["iam_role_arn"]
255 }
256 }
257
258 resource "fastly_tls_subscription" "releases-2025-11" {
259 domains = [for domain in fastly_service_vcl.releases.domain : domain.name]
260 configuration_id = local.fastly_tls13_quic_configuration_id
261 certificate_authority = "lets-encrypt"
262 }
263
264 output "releases-managed_dns_challenge" {
265 value = fastly_tls_subscription.releases-2025-11.managed_dns_challenges
266 }