main
tf 207 lines 7.47 KB
Raw
1 # Artifacts Proxy Service
2 #
3 # This service provides IPv6-enabled access to GitHub releases through Fastly CDN.
4 # It transparently follows GitHub's S3 redirects to provide direct file access.
5 #
6 # Supported URL patterns:
7 # - /nix-installer/tag/* -> /NixOS/nix-installer/releases/download/*
8 # - /nix-installer -> /NixOS/nix-installer/releases/latest/download/nix-installer.sh
9 # - /nix-installer/* -> /NixOS/nix-installer/releases/latest/download/*
10 # - /experimental-installer/tag/* -> /NixOS/experimental-nix-installer/releases/download/* (legacy)
11 # - /experimental-installer -> /NixOS/experimental-nix-installer/releases/latest/download/nix-installer.sh (legacy)
12 # - /experimental-installer/* -> /NixOS/experimental-nix-installer/releases/latest/download/* (legacy)
13 # - /patchelf/* -> /NixOS/patchelf/releases/download/*
14 #
15 # Testing commands:
16 #
17 # Basic functionality tests:
18 # curl -I https://artifacts.nixos.org/nix/0.27.0/nix-installer.sh
19 # curl -s https://artifacts.nixos.org/nix/0.27.0/nix-installer.sh | head -n 5
20 #
21 # IPv6 connectivity test:
22 # curl -6 -I https://artifacts.nixos.org/nix/0.27.0/nix-installer.sh
23 #
24 # Performance comparison (should show redirect following):
25 # time curl -s https://artifacts.nixos.org/nix/0.27.0/nix-installer-x86_64-linux > /dev/null
26 # time curl -s https://github.com/NixOS/experimental-nix-installer/releases/download/0.27.0/nix-installer-x86_64-linux > /dev/null
27 #
28 # Error cases (should return 404):
29 # curl -I https://artifacts.nixos.org/invalid/path
30 # curl -I https://artifacts.nixos.org/patchelf/999.999.999/nonexistent-file
31
32 locals {
33 artifacts_domain = "artifacts.nixos.org"
34 }
35
36 resource "fastly_service_vcl" "artifacts" {
37 name = local.artifacts_domain
38 default_ttl = 3600
39
40 backend {
41 address = "github.com"
42 auto_loadbalance = false
43 between_bytes_timeout = 10000
44 connect_timeout = 1000
45 error_threshold = 0
46 first_byte_timeout = 15000
47 max_conn = 200
48 name = "github.com"
49 override_host = "github.com"
50 port = 443
51 ssl_cert_hostname = "github.com"
52 ssl_check_cert = true
53 use_ssl = true
54 weight = 100
55 request_condition = "Use GitHub backend"
56 }
57
58 backend {
59 address = "objects.githubusercontent.com"
60 auto_loadbalance = false
61 between_bytes_timeout = 10000
62 connect_timeout = 1000
63 error_threshold = 0
64 first_byte_timeout = 15000
65 max_conn = 200
66 name = "objects_githubusercontent_com"
67 override_host = "objects.githubusercontent.com"
68 port = 443
69 ssl_cert_hostname = "objects.githubusercontent.com"
70 ssl_check_cert = true
71 use_ssl = true
72 weight = 100
73 request_condition = "Use Objects backend"
74 }
75
76 condition {
77 name = "Use GitHub backend"
78 priority = 10
79 statement = "!req.http.X-Use-Objects-Backend"
80 type = "REQUEST"
81 }
82
83 condition {
84 name = "Use Objects backend"
85 priority = 10
86 statement = "req.http.X-Use-Objects-Backend"
87 type = "REQUEST"
88 }
89
90
91 request_setting {
92 name = "Redirect HTTP to HTTPS"
93 force_ssl = true
94 }
95
96 domain {
97 name = local.artifacts_domain
98 }
99
100 # Main VCL snippet to handle the redirect logic
101 snippet {
102 content = <<-EOT
103 # Only rewrite if this is the first request (not a restart)
104 if (!req.http.X-Rewritten) {
105 # New nix-installer routes (NixOS/nix-installer)
106 if (req.url ~ "^/nix-installer/tag/") {
107 set req.url = regsub(req.url.path, "^/nix-installer/tag/", "/NixOS/nix-installer/releases/download/");
108 set req.http.X-Rewritten = "true";
109 } else if (req.url ~ "^(/nix-installer|/nix-installer/)$") {
110 set req.url = regsub(req.url.path, "^(/nix-installer|/nix-installer/)$", "/NixOS/nix-installer/releases/latest/download/nix-installer.sh");
111 set req.http.X-Rewritten = "true";
112 } else if (req.url ~ "^/nix-installer/") {
113 set req.url = regsub(req.url.path, "^/nix-installer", "/NixOS/nix-installer/releases/latest/download/");
114 set req.http.X-Rewritten = "true";
115 # Legacy experimental-installer routes (NixOS/experimental-nix-installer)
116 } else if (req.url ~ "^/experimental-installer/tag/") {
117 set req.url = regsub(req.url.path, "^/experimental-installer/tag/", "/NixOS/experimental-nix-installer/releases/download/");
118 set req.http.X-Rewritten = "true";
119 } else if (req.url ~ "^(/experimental-installer|/experimental-installer/)$") {
120 set req.url = regsub(req.url.path, "^(/experimental-installer|/experimental-installer/)$", "/NixOS/experimental-nix-installer/releases/latest/download/nix-installer.sh");
121 set req.http.X-Rewritten = "true";
122 } else if (req.url ~ "^/experimental-installer/") {
123 set req.url = regsub(req.url.path, "^/experimental-installer", "/NixOS/experimental-nix-installer/releases/latest/download/");
124 set req.http.X-Rewritten = "true";
125 } else if (req.url ~ "^/patchelf/") {
126 set req.url = regsub(req.url.path, "^/patchelf/", "/NixOS/patchelf/releases/download/");
127 set req.http.X-Rewritten = "true";
128 } else {
129 error 600;
130 }
131 }
132 EOT
133 name = "GitHub releases redirect"
134 priority = 100
135 type = "recv"
136 }
137
138 # Handle redirects from GitHub to S3
139 snippet {
140 content = <<-EOT
141 if (beresp.status == 302 && beresp.http.Location ~ "^https://objects\.githubusercontent\.com/") {
142 # Extract the full path including query parameters
143 set req.url = regsub(beresp.http.Location, "^https://objects\.githubusercontent\.com", "");
144 set req.http.X-Use-Objects-Backend = "true";
145 # Set correct host header for S3
146 set req.http.Host = "objects.githubusercontent.com";
147 # Clear GitHub-specific headers that might interfere
148 unset req.http.Authorization;
149 unset req.http.Cookie;
150 restart;
151 }
152 EOT
153 name = "Follow GitHub redirects"
154 priority = 100
155 type = "fetch"
156 }
157
158
159
160
161 # Handle 404 errors
162 snippet {
163 content = <<-EOT
164 if (obj.status == 600) {
165 set obj.status = 404;
166 set obj.http.Content-Type = "text/html";
167 synthetic {"<h1>Not Found</h1>"};
168 return(deliver);
169 }
170 EOT
171 name = "Handle 404 errors"
172 priority = 100
173 type = "error"
174 }
175
176 # Add HSTS header for security
177 header {
178 destination = "http.Strict-Transport-Security"
179 type = "response"
180 action = "set"
181 name = "Add HSTS"
182 source = "\"max-age=300\""
183 }
184
185 logging_s3 {
186 name = "${local.artifacts_domain}-to-s3"
187 bucket_name = local.fastlylogs["bucket_name"]
188 compression_codec = "zstd"
189 domain = local.fastlylogs["s3_domain"]
190 format = local.fastlylogs["format"]
191 format_version = 2
192 path = "${local.artifacts_domain}/"
193 period = local.fastlylogs["period"]
194 message_type = "blank"
195 s3_iam_role = local.fastlylogs["iam_role_arn"]
196 }
197 }
198
199 resource "fastly_tls_subscription" "artifacts" {
200 domains = [for domain in fastly_service_vcl.artifacts.domain : domain.name]
201 configuration_id = local.fastly_tls13_quic_configuration_id
202 certificate_authority = "lets-encrypt"
203 }
204
205 output "artifacts-managed_dns_challenge" {
206 value = fastly_tls_subscription.artifacts.managed_dns_challenges
207 }