main
tf 393 lines 10.6 KB
Raw
1 locals {
2 tarballs_domain = "tarballs.nixos.org"
3 # Use the website endpoint because the bucket is configured with website
4 # enabled. This also means we can't use TLS between Fastly and AWS because
5 # the website endpoint only has port 80 open.
6 tarballs_backend = "nixpkgs-tarballs.s3-website-eu-west-1.amazonaws.com"
7 # TODO: Uncomment this once has been applied once. This is to work around fastly bug https://github.com/fastly/terraform-provider-fastly/issues/884
8 # tarballs_backend = aws_s3_bucket_website_configuration.nixpkgs-tarballs.website_endpoint
9 }
10
11 resource "aws_s3_bucket" "nixpkgs-tarballs" {
12 bucket = "nixpkgs-tarballs"
13 }
14
15 resource "aws_s3_bucket_website_configuration" "nixpkgs-tarballs" {
16 bucket = aws_s3_bucket.nixpkgs-tarballs.id
17 index_document {
18 suffix = "index.html"
19 }
20 }
21
22 import {
23 to = aws_s3_bucket_website_configuration.nixpkgs-tarballs
24 id = aws_s3_bucket.nixpkgs-tarballs.id
25 }
26
27 resource "aws_s3_bucket_policy" "nixpkgs-tarballs" {
28 bucket = aws_s3_bucket.nixpkgs-tarballs.id
29
30 # imported from existing
31 policy = <<EOF
32 {
33 "Version": "2008-10-17",
34 "Statement": [
35 {
36 "Sid": "AllowPublicRead",
37 "Effect": "Allow",
38 "Principal": {
39 "AWS": "*"
40 },
41 "Action": "s3:GetObject",
42 "Resource": "arn:aws:s3:::nixpkgs-tarballs/*"
43 },
44 {
45 "Sid": "AllowUpload",
46 "Effect": "Allow",
47 "Principal": {
48 "AWS": "arn:aws:iam::080433136561:user/s3-upload-tarballs"
49 },
50 "Action": [
51 "s3:PutObject",
52 "s3:PutObjectAcl"
53 ],
54 "Resource": "arn:aws:s3:::nixpkgs-tarballs/*"
55 },
56 {
57 "Sid": "AllowUpload2",
58 "Effect": "Allow",
59 "Principal": {
60 "AWS": "arn:aws:iam::080433136561:user/s3-upload-tarballs"
61 },
62 "Action": "s3:ListBucket",
63 "Resource": "arn:aws:s3:::nixpkgs-tarballs"
64 },
65 {
66 "Sid": "CopumpkinAllowUpload",
67 "Effect": "Allow",
68 "Principal": {
69 "AWS": "arn:aws:iam::390897850978:root"
70 },
71 "Action": [
72 "s3:PutObject",
73 "s3:PutObjectAcl"
74 ],
75 "Resource": "arn:aws:s3:::nixpkgs-tarballs/*"
76 },
77 {
78 "Sid": "CopumpkinAllowUpload2",
79 "Effect": "Allow",
80 "Principal": {
81 "AWS": "arn:aws:iam::390897850978:root"
82 },
83 "Action": "s3:ListBucket",
84 "Resource": "arn:aws:s3:::nixpkgs-tarballs"
85 },
86 {
87 "Sid": "ShlevyAllowUpload",
88 "Effect": "Allow",
89 "Principal": {
90 "AWS": "arn:aws:iam::976576280863:user/shlevy"
91 },
92 "Action": [
93 "s3:PutObject",
94 "s3:PutObjectAcl"
95 ],
96 "Resource": "arn:aws:s3:::nixpkgs-tarballs/*"
97 },
98 {
99 "Sid": "ShlevyAllowUpload2",
100 "Effect": "Allow",
101 "Principal": {
102 "AWS": "arn:aws:iam::976576280863:user/shlevy"
103 },
104 "Action": "s3:ListBucket",
105 "Resource": "arn:aws:s3:::nixpkgs-tarballs"
106 },
107 {
108 "Sid": "DaiderdAllowUpload",
109 "Effect": "Allow",
110 "Principal": {
111 "AWS": "arn:aws:iam::014292808257:user/lnl7"
112 },
113 "Action": [
114 "s3:PutObject",
115 "s3:PutObjectAcl"
116 ],
117 "Resource": "arn:aws:s3:::nixpkgs-tarballs/*"
118 },
119 {
120 "Sid": "DaiderdAllowUpload2",
121 "Effect": "Allow",
122 "Principal": {
123 "AWS": "arn:aws:iam::014292808257:user/lnl7"
124 },
125 "Action": "s3:ListBucket",
126 "Resource": "arn:aws:s3:::nixpkgs-tarballs"
127 },
128 {
129 "Sid": "LovesegfaultAllowUpload",
130 "Effect": "Allow",
131 "Principal": {
132 "AWS": "arn:aws:iam::839273551904:root"
133 },
134 "Action": [
135 "s3:PutObject",
136 "s3:PutObjectAcl"
137 ],
138 "Resource": "arn:aws:s3:::nixpkgs-tarballs/*"
139 },
140 {
141 "Sid": "LovesegfaultAllowUpload2",
142 "Effect": "Allow",
143 "Principal": {
144 "AWS": "arn:aws:iam::839273551904:root"
145 },
146 "Action": "s3:ListBucket",
147 "Resource": "arn:aws:s3:::nixpkgs-tarballs"
148 }
149 ]
150 }
151 EOF
152 }
153
154 resource "aws_s3_bucket_object" "nixpkgs-tarballs-index" {
155 bucket = aws_s3_bucket.nixpkgs-tarballs.id
156 content_type = "text/html"
157 etag = filemd5("${path.module}/nixpkgs-tarballs/index.html")
158 key = "index.html"
159 source = "${path.module}/nixpkgs-tarballs/index.html"
160 }
161
162 resource "fastly_service_vcl" "nixpkgs-tarballs" {
163 name = local.tarballs_domain
164 default_ttl = 86400
165
166 backend {
167 address = local.tarballs_backend
168 auto_loadbalance = false
169 between_bytes_timeout = 10000
170 connect_timeout = 5000
171 error_threshold = 0
172 first_byte_timeout = 15000
173 max_conn = 200
174 name = local.tarballs_backend
175 override_host = local.tarballs_backend
176 port = 80
177 shield = "dub-dublin-ie"
178 use_ssl = false
179 weight = 100
180 }
181
182 request_setting {
183 name = "Redirect HTTP to HTTPS"
184 force_ssl = true
185 }
186
187 condition {
188 name = "Generated by synthetic response for 404 page"
189 priority = 0
190 statement = "beresp.status == 404"
191 type = "CACHE"
192 }
193
194 condition {
195 name = "Match /"
196 priority = 10
197 statement = "req.url ~ \"^/$\""
198 type = "REQUEST"
199 }
200
201 domain {
202 name = local.tarballs_domain
203 }
204
205 header {
206 action = "set"
207 destination = "url"
208 ignore_if_set = false
209 name = "Landing page"
210 priority = 10
211 request_condition = "Match /"
212 source = "\"/index.html\""
213 type = "request"
214 }
215
216 # Clean headers for caching
217 header {
218 destination = "http.x-amz-request-id"
219 type = "cache"
220 action = "delete"
221 name = "remove x-amz-request-id"
222 }
223 header {
224 destination = "http.x-amz-version-id"
225 type = "cache"
226 action = "delete"
227 name = "remove x-amz-version-id"
228 }
229 header {
230 destination = "http.x-amz-id-2"
231 type = "cache"
232 action = "delete"
233 name = "remove x-amz-id-2"
234 }
235
236 # Allow CORS GET requests.
237 header {
238 destination = "http.access-control-allow-origin"
239 type = "cache"
240 action = "set"
241 name = "CORS Allow"
242 source = "\"*\""
243 }
244
245 response_object {
246 cache_condition = "Generated by synthetic response for 404 page"
247 content = "404"
248 content_type = "text/html"
249 name = "Generated by synthetic response for 404 page"
250 response = "Not Found"
251 status = 404
252 }
253
254 snippet {
255 content = "set req.url = querystring.remove(req.url);"
256 name = "Remove all query strings"
257 priority = 50
258 type = "recv"
259 }
260
261 snippet {
262 content = <<-EOT
263 if (beresp.status == 403) {
264 set beresp.status = 404;
265 set beresp.ttl = 86400s;
266 set beresp.grace = 0s;
267 set beresp.cacheable = true;
268 }
269 EOT
270 name = "Change 403 from S3 to 404"
271 priority = 100
272 type = "fetch"
273 }
274
275 logging_s3 {
276 name = "${local.tarballs_domain}-to-s3"
277 bucket_name = local.fastlylogs["bucket_name"]
278 compression_codec = "zstd"
279 domain = local.fastlylogs["s3_domain"]
280 format = local.fastlylogs["format"]
281 format_version = 2
282 path = "${local.tarballs_domain}/"
283 period = local.fastlylogs["period"]
284 message_type = "blank"
285 s3_iam_role = local.fastlylogs["iam_role_arn"]
286 }
287 }
288
289 resource "fastly_tls_subscription" "nixpkgs-tarballs-2025-11" {
290 domains = [for domain in fastly_service_vcl.nixpkgs-tarballs.domain : domain.name]
291 configuration_id = local.fastly_tls13_quic_configuration_id
292 certificate_authority = "lets-encrypt"
293 }
294
295 output "nixpkgs-tarballs-managed_dns_challenge" {
296 value = fastly_tls_subscription.nixpkgs-tarballs-2025-11.managed_dns_challenges
297 }
298
299 # Create an S3 bucket for CloudTrail logs
300 resource "aws_s3_bucket" "nixpkgs-tarballs-cloudtrail-logs" {
301 bucket = "nixpkgs-tarballs-cloudtrail-logs"
302 # We can potentially make this public for transparency?
303 # But first I want to see what the logs look like.
304 acl = "private"
305 }
306
307 resource "aws_s3_bucket_versioning" "nixpkgs-tarballs-cloudtrail-logs" {
308 bucket = aws_s3_bucket.nixpkgs-tarballs-cloudtrail-logs.id
309 versioning_configuration {
310 status = "Enabled"
311 }
312 }
313
314
315 import {
316 to = aws_s3_bucket_versioning.nixpkgs-tarballs-cloudtrail-logs
317 id = aws_s3_bucket.nixpkgs-tarballs-cloudtrail-logs.id
318 }
319
320 # Attach a policy to the CloudTrail logs S3 bucket
321 data "aws_iam_policy_document" "nixpkgs-tarballs-cloudtrail-logs-policy" {
322 statement {
323 sid = "AWSCloudTrailAclCheck"
324 effect = "Allow"
325
326 principals {
327 type = "Service"
328 identifiers = ["cloudtrail.amazonaws.com"]
329 }
330
331 actions = ["s3:GetBucketAcl"]
332 resources = [aws_s3_bucket.nixpkgs-tarballs-cloudtrail-logs.arn]
333 condition {
334 test = "StringEquals"
335 variable = "aws:SourceArn"
336 values = ["arn:${data.aws_partition.current.partition}:cloudtrail:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:trail/nixpkgs-tarballs"]
337 }
338 }
339
340 statement {
341 sid = "AWSCloudTrailWrite"
342 effect = "Allow"
343
344 principals {
345 type = "Service"
346 identifiers = ["cloudtrail.amazonaws.com"]
347 }
348
349 actions = ["s3:PutObject"]
350 resources = ["${aws_s3_bucket.nixpkgs-tarballs-cloudtrail-logs.arn}/*"]
351
352 condition {
353 test = "StringEquals"
354 variable = "s3:x-amz-acl"
355 values = ["bucket-owner-full-control"]
356 }
357 condition {
358 test = "StringEquals"
359 variable = "aws:SourceArn"
360 values = ["arn:${data.aws_partition.current.partition}:cloudtrail:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:trail/nixpkgs-tarballs"]
361 }
362 }
363 }
364
365 data "aws_caller_identity" "current" {}
366 data "aws_partition" "current" {}
367 data "aws_region" "current" {}
368
369 resource "aws_s3_bucket_policy" "nixpkgs-tarballs-cloudtrail-logs-policy" {
370 bucket = aws_s3_bucket.nixpkgs-tarballs-cloudtrail-logs.id
371 policy = data.aws_iam_policy_document.nixpkgs-tarballs-cloudtrail-logs-policy.json
372 }
373
374 # Create a CloudTrail
375 resource "aws_cloudtrail" "nixpkgs-tarballs" {
376 name = "nixpkgs-tarballs"
377 s3_bucket_name = aws_s3_bucket.nixpkgs-tarballs-cloudtrail-logs.bucket
378 enable_log_file_validation = true
379 depends_on = [
380 aws_s3_bucket_policy.nixpkgs-tarballs-cloudtrail-logs-policy
381 ]
382 # You must specify a log group and a role ARN.
383
384 event_selector {
385 read_write_type = "WriteOnly"
386 include_management_events = false
387
388 data_resource {
389 type = "AWS::S3::Object"
390 values = ["arn:aws:s3:::${aws_s3_bucket.nixpkgs-tarballs.bucket}/"]
391 }
392 }
393 }