main
tf 110 lines 3.43 KB
Raw
1 # Forward S3 Object Created events on the nix-cache bucket to the
2 # https://cache-updates.snix.store webhook via an EventBridge API destination.
3 #
4 # Lives in terraform-iam (rather than terraform/) because creating the
5 # EventBridge target requires iam:PassRole on the IAM role below, which the
6 # AWSPowerUserAccess SSO role used by terraform/ does not have.
7
8 locals {
9 cache_webhook_url = "https://cache-updates.snix.store"
10 cache_webhook_header_key = "X-API-Key"
11 cache_bucket_name = "nix-cache"
12 }
13
14 resource "secret_resource" "cache_webhook_api_key" {}
15
16 # Cost: $1.00 per million events ingested. S3 EventBridge events are opt-in
17 # data plane events, billed as custom events on the default bus.
18 # https://aws.amazon.com/eventbridge/pricing/
19 resource "aws_s3_bucket_notification" "cache" {
20 provider = aws.us
21 bucket = local.cache_bucket_name
22 eventbridge = true
23 }
24
25 resource "aws_cloudwatch_event_connection" "cache_webhook" {
26 provider = aws.us
27 name = "cache-updates-snix-store"
28 authorization_type = "API_KEY"
29
30 auth_parameters {
31 api_key {
32 key = local.cache_webhook_header_key
33 value = secret_resource.cache_webhook_api_key.value
34 }
35 }
36 }
37
38 # Cost: $0.20 per million invocations.
39 # https://aws.amazon.com/eventbridge/pricing/
40 resource "aws_cloudwatch_event_api_destination" "cache_webhook" {
41 provider = aws.us
42 name = "cache-updates-snix-store"
43 invocation_endpoint = local.cache_webhook_url
44 http_method = "POST"
45 # Tweak this based on the amount of uploads per 24 hours?
46 invocation_rate_limit_per_second = 300
47 connection_arn = aws_cloudwatch_event_connection.cache_webhook.arn
48 }
49
50 resource "aws_cloudwatch_event_rule" "cache_object_created" {
51 provider = aws.us
52 name = "nix-cache-object-created"
53 description = "S3 Object Created events on nix-cache forwarded to cache-updates.snix.store"
54
55 event_pattern = jsonencode({
56 source = ["aws.s3"]
57 "detail-type" = ["Object Created"]
58 detail = {
59 bucket = {
60 name = [local.cache_bucket_name]
61 }
62 }
63 })
64 }
65
66 data "aws_iam_policy_document" "cache_webhook_assume" {
67 statement {
68 effect = "Allow"
69 actions = ["sts:AssumeRole"]
70 principals {
71 type = "Service"
72 identifiers = ["events.amazonaws.com"]
73 }
74 }
75 }
76
77 data "aws_iam_policy_document" "cache_webhook_invoke" {
78 statement {
79 effect = "Allow"
80 actions = ["events:InvokeApiDestination"]
81 resources = [aws_cloudwatch_event_api_destination.cache_webhook.arn]
82 }
83 }
84
85 resource "aws_iam_role" "cache_webhook" {
86 provider = aws.us
87 name = "EventBridgeInvokeCacheWebhook"
88 assume_role_policy = data.aws_iam_policy_document.cache_webhook_assume.json
89 }
90
91 resource "aws_iam_role_policy" "cache_webhook" {
92 provider = aws.us
93 name = "InvokeCacheWebhook"
94 role = aws_iam_role.cache_webhook.id
95 policy = data.aws_iam_policy_document.cache_webhook_invoke.json
96 }
97
98 resource "aws_cloudwatch_event_target" "cache_webhook" {
99 provider = aws.us
100 rule = aws_cloudwatch_event_rule.cache_object_created.name
101 target_id = "cache-updates-snix-store"
102 arn = aws_cloudwatch_event_api_destination.cache_webhook.arn
103 role_arn = aws_iam_role.cache_webhook.arn
104
105 # https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-rule-retry-policy.html
106 retry_policy {
107 maximum_event_age_in_seconds = 86400
108 maximum_retry_attempts = 185
109 }
110 }