Skip to content

Commit 24da674

Browse files
authored
Merge pull request #1 from mastodon/INF-94-fastly-file-service
Separated out the files service into a separate module
2 parents 19a4043 + d226ee6 commit 24da674

9 files changed

+278
-2
lines changed

README.md

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,49 @@
1-
# terraform-fastly-files-service
2-
Terraform module for creating a fastly service for Mastodon's files backend
1+
# Mastodon Terraform - Fastly Service for Mastodon Application File Hosting
2+
3+
Terraform module for creating a service in Fastly for directing traffic towards an external S3-style bucket for hosting mastodon media files (for example, files.mastodon.social).
4+
5+
## Requirements
6+
7+
| Name | Version |
8+
|------|---------|
9+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
10+
| <a name="requirement_fastly"></a> [fastly](#requirement\_fastly) | >= 4.1.0 |
11+
12+
## Providers
13+
14+
| Name | Version |
15+
|------|---------|
16+
| <a name="provider_fastly"></a> [fastly](#provider\_fastly) | >= 4.1.0 |
17+
18+
## Modules
19+
20+
No modules.
21+
22+
## Resources
23+
24+
| Name | Type |
25+
|------|------|
26+
| [fastly_service_vcl.files_service](https://registry.terraform.io/providers/fastly/fastly/latest/docs/resources/service_vcl) | resource |
27+
28+
## Inputs
29+
30+
| Name | Description | Type | Default | Required |
31+
|------|-------------|------|---------|:--------:|
32+
| <a name="input_app_hostname"></a> [app\_hostname](#input\_app\_hostname) | Hostname of the mastodon app that this service belongs to. | `string` | n/a | yes |
33+
| <a name="input_backend_address"></a> [backend\_address](#input\_backend\_address) | Address to use for connecting to the backend. Can be a hostname or an IP address. | `string` | n/a | yes |
34+
| <a name="input_backend_name"></a> [backend\_name](#input\_backend\_name) | Optional name for the backend. | `string` | `""` | no |
35+
| <a name="input_backend_ssl_check"></a> [backend\_ssl\_check](#input\_backend\_ssl\_check) | Be strict about checking SSL certs when connecting to the backend. | `bool` | `true` | no |
36+
| <a name="input_force_tls_hsts"></a> [force\_tls\_hsts](#input\_force\_tls\_hsts) | Force TLS and HTTP Strict Transport Security (HSTS) to ensure that every request is secure. | `bool` | `true` | no |
37+
| <a name="input_hostname"></a> [hostname](#input\_hostname) | Hostname the service points to. | `string` | n/a | yes |
38+
| <a name="input_hsts_duration"></a> [hsts\_duration](#input\_hsts\_duration) | Number of seconds for the client to remember only to use HTTPS. | `number` | `31557600` | no |
39+
| <a name="input_name"></a> [name](#input\_name) | Name of the fastly service (defaults to hostname). | `string` | `""` | no |
40+
| <a name="input_shield_region"></a> [shield\_region](#input\_shield\_region) | Which Fastly shield region to use. Should correspond with the shield code. | `string` | n/a | yes |
41+
| <a name="input_ssl_hostname"></a> [ssl\_hostname](#input\_ssl\_hostname) | Hostname to use for SSL verification (if different from 'hostname'). | `string` | `""` | no |
42+
43+
## Outputs
44+
45+
| Name | Description |
46+
|------|-------------|
47+
| <a name="output_active_version"></a> [active\_version](#output\_active\_version) | The currently active version of the Fastly Service |
48+
| <a name="output_cloned_version"></a> [cloned\_version](#output\_cloned\_version) | The latest cloned version by the provider |
49+
| <a name="output_id"></a> [id](#output\_id) | The ID of this resource |

main.tf

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
locals {
2+
name = var.name != "" ? var.name : var.hostname
3+
4+
backend_name = var.backend_name != "" ? var.backend_name : "${var.hostname} - backend"
5+
ssl_hostname = var.ssl_hostname != "" ? var.ssl_hostname : var.hostname
6+
7+
vcl_exoscale_forward = templatefile("${path.module}/vcl/exoscale_forward.vcl", { hostname = replace(var.app_hostname, ".", "-") })
8+
vcl_remove_cookies_headers = file("${path.module}/vcl/remove_cookies_headers.vcl")
9+
vcl_remove_response_headers = file("${path.module}/vcl/remove_response_headers.vcl")
10+
vcl_segmented_caching = file("${path.module}/vcl/segmented_caching.vcl")
11+
}
12+
13+
resource "fastly_service_vcl" "files_service" {
14+
name = local.name
15+
16+
http3 = true
17+
stale_if_error = true
18+
19+
domain {
20+
name = var.hostname
21+
}
22+
23+
backend {
24+
name = local.backend_name
25+
address = var.backend_address
26+
27+
keepalive_time = 0
28+
override_host = local.ssl_hostname
29+
port = 443
30+
shield = var.shield_region
31+
ssl_check_cert = var.backend_ssl_check
32+
ssl_cert_hostname = var.backend_ssl_check ? local.ssl_hostname : ""
33+
ssl_sni_hostname = local.ssl_hostname
34+
use_ssl = true
35+
}
36+
37+
# Set custom Fastly purge rules
38+
39+
condition {
40+
name = "Purge"
41+
statement = "req.request == \"FASTLYPURGE\""
42+
type = "REQUEST"
43+
priority = 10
44+
}
45+
46+
header {
47+
name = "Fastly Purge"
48+
action = "set"
49+
destination = "http.Fastly-Purge-Requires-Auth"
50+
type = "request"
51+
52+
priority = 10
53+
request_condition = "Purge"
54+
source = "\"1\""
55+
}
56+
57+
# Force TLS/HSTS settings
58+
# Creates similar objects to what the GUI switch creates.
59+
60+
dynamic "request_setting" {
61+
for_each = var.force_tls_hsts ? [1] : []
62+
content {
63+
name = "Generated by force TLS and enable HSTS"
64+
65+
bypass_busy_wait = false
66+
force_miss = false
67+
force_ssl = true
68+
max_stale_age = 0
69+
timer_support = false
70+
xff = ""
71+
}
72+
}
73+
74+
dynamic "header" {
75+
for_each = var.force_tls_hsts ? [1] : []
76+
content {
77+
action = "set"
78+
destination = "http.Strict-Transport-Security"
79+
name = "Generated by force TLS and enable HSTS"
80+
type = "response"
81+
82+
ignore_if_set = false
83+
priority = 100
84+
source = "\"max-age=${var.hsts_duration}\""
85+
}
86+
}
87+
88+
# Custom VCL snippets
89+
90+
snippet {
91+
name = "Enable segmented caching"
92+
content = local.vcl_segmented_caching
93+
type = "recv"
94+
priority = 100
95+
}
96+
97+
snippet {
98+
name = "Remove cookies and headers from request"
99+
content = local.vcl_remove_cookies_headers
100+
type = "recv"
101+
priority = 100
102+
}
103+
104+
snippet {
105+
name = "Rewrite request to Exoscale"
106+
content = local.vcl_exoscale_forward
107+
type = "miss"
108+
priority = 100
109+
}
110+
111+
snippet {
112+
name = "Remove headers from origin response"
113+
content = local.vcl_remove_response_headers
114+
type = "fetch"
115+
priority = 100
116+
}
117+
118+
# Additional products
119+
product_enablement {
120+
brotli_compression = false
121+
domain_inspector = false
122+
image_optimizer = false
123+
origin_inspector = false
124+
websockets = false
125+
}
126+
}

outputs.tf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
output "id" {
2+
description = "The ID of this resource"
3+
value = fastly_service_vcl.files_service.id
4+
}
5+
6+
output "active_version" {
7+
description = "The currently active version of the Fastly Service"
8+
value = fastly_service_vcl.files_service.active_version
9+
}
10+
11+
output "cloned_version" {
12+
description = "The latest cloned version by the provider"
13+
value = fastly_service_vcl.files_service.cloned_version
14+
}

variables.tf

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
variable "name" {
2+
description = "Name of the fastly service (defaults to hostname)."
3+
type = string
4+
default = ""
5+
}
6+
7+
variable "hostname" {
8+
description = "Hostname the service points to."
9+
type = string
10+
}
11+
12+
variable "app_hostname" {
13+
description = "Hostname of the mastodon app that this service belongs to."
14+
type = string
15+
}
16+
17+
variable "ssl_hostname" {
18+
description = "Hostname to use for SSL verification (if different from 'hostname')."
19+
type = string
20+
default = ""
21+
}
22+
23+
variable "backend_name" {
24+
description = "Optional name for the backend."
25+
type = string
26+
default = ""
27+
}
28+
29+
variable "backend_address" {
30+
description = "Address to use for connecting to the backend. Can be a hostname or an IP address."
31+
type = string
32+
}
33+
34+
variable "backend_ssl_check" {
35+
description = "Be strict about checking SSL certs when connecting to the backend."
36+
type = bool
37+
default = true
38+
}
39+
40+
variable "shield_region" {
41+
description = "Which Fastly shield region to use. Should correspond with the shield code."
42+
type = string
43+
}
44+
45+
variable "force_tls_hsts" {
46+
description = "Force TLS and HTTP Strict Transport Security (HSTS) to ensure that every request is secure."
47+
type = bool
48+
default = true
49+
}
50+
51+
variable "hsts_duration" {
52+
description = "Number of seconds for the client to remember only to use HTTPS."
53+
type = number
54+
default = 31557600
55+
}

vcl/exoscale_forward.vcl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
if (req.method == "GET" && !req.backend.is_shield) {
2+
set bereq.url = "/${hostname}" + req.url;
3+
}
4+
5+
if (req.url.ext ~ "(?i)^(jpe?g|png|gif|mp4|mp3|gz|svg|avif|webp)$") {
6+
7+
} else {
8+
error 404;
9+
}

vcl/remove_cookies_headers.vcl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
unset req.http.Cookie;

vcl/remove_response_headers.vcl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
unset beresp.http.x-amz-id-2;
2+
unset beresp.http.x-amz-request-id;
3+
unset beresp.http.x-amz-meta-server-side-encryption;
4+
unset beresp.http.x-amz-server-side-encryption;
5+
unset beresp.http.x-amz-bucket-region;
6+
unset beresp.http.x-amzn-requestid;
7+
8+
set beresp.http.Access-Control-Allow-Origin = "*";
9+
set beresp.http.Access-Control-Allow-Methods = "GET";
10+
set beresp.http.Access-Control-Allow-Headers = "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type";
11+
set beresp.http.Cache-Control = "public, max-age=315576000, immutable";

vcl/segmented_caching.vcl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
if (req.url.ext ~ "^(mp4|mp3|gz|zip)$") {
2+
set req.enable_segmented_caching = true;
3+
}

versions.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
terraform {
2+
required_version = ">= 1.0.0"
3+
4+
required_providers {
5+
fastly = {
6+
source = "fastly/fastly"
7+
version = ">= 4.1.0"
8+
}
9+
}
10+
}

0 commit comments

Comments
 (0)