eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1# πŸ€– The server-release-feed mirror (issue #550 Part 12): a second, boring
2# home for the signed update feed β€” manifest.json plus the per-version
3# binaries. GitHub's asset CDN has served freshly-clobbered manifests stale
4# (and brand-new binaries 404) for minutes after a publish β€” documented
5# operator pain β€” and that feed is the entire fleet's update path.
6# deploy/release-server.sh copies every publish/promote here
7# (LCHAT_RELEASE_MIRROR_BUCKET); updaters fall back to it
8# (LCHAT_RELEASE_MIRROR_URL). Versioning doubles as the rollback story for a
9# clobbered manifest.json.
10#
11# NOT YET APPLIED β€” needs a deliberate, named
12# `AWS_PROFILE=… tofu apply` in infra/prod; plan only until then.
13
14resource "aws_s3_bucket" "releases" {
15 bucket = "eleven-releases-${local.account_id}"
16}
17
18resource "aws_s3_bucket_versioning" "releases" {
19 bucket = aws_s3_bucket.releases.id
20
21 versioning_configuration {
22 status = "Enabled"
23 }
24}
25
26# Manifest clobbers are tiny and binaries land under fresh server-<ver>/ keys,
27# so noncurrent versions exist only as the manifest's rollback window β€” 90
28# days is generous without growing forever.
29resource "aws_s3_bucket_lifecycle_configuration" "releases" {
30 bucket = aws_s3_bucket.releases.id
31
32 rule {
33 id = "expire-noncurrent-90d"
34 status = "Enabled"
35
36 filter {}
37
38 noncurrent_version_expiration {
39 noncurrent_days = 90
40 }
41 }
42}
43
44# This bucket is deliberately PUBLIC-READ β€” the one bucket in the account
45# where that is the point, against the everything-else-blocked posture
46# (backup.tf, hibernate.tf). Instances and self-hosted boxes must fetch
47# updates anonymously, and the content is public, non-secret, and
48# trust-anchored elsewhere: the manifest is Ed25519-signed against a key baked
49# into every binary, and binaries are sha256-pinned by that signed manifest,
50# so the mirror provides availability only β€” a tampered object can only cause
51# a refusal, never an install (docs/server-releases.md Β§ Trust model). ACLs
52# stay fully blocked; only the bucket *policy* door is opened, and the
53# GET-only policy below is the entire public surface (no anonymous List).
54# Writes stay IAM-only via the release-publisher user.
55resource "aws_s3_bucket_public_access_block" "releases" {
56 bucket = aws_s3_bucket.releases.id
57
58 block_public_acls = true
59 ignore_public_acls = true
60 block_public_policy = false
61 restrict_public_buckets = false
62}
63
64data "aws_iam_policy_document" "releases_public_read" {
65 statement {
66 sid = "PublicReadReleaseObjects"
67 actions = ["s3:GetObject"]
68 resources = ["${aws_s3_bucket.releases.arn}/*"]
69
70 principals {
71 type = "*"
72 identifiers = ["*"]
73 }
74 }
75
76 # Public means anonymous, not unencrypted: deny any request over plain
77 # HTTP. Updaters fetch the mirror via its https URL already (the manifest
78 # is Ed25519-signed regardless β€” TLS here is defence in depth, not the
79 # trust anchor).
80 statement {
81 sid = "DenyInsecureTransport"
82 effect = "Deny"
83 actions = ["s3:*"]
84 resources = [
85 aws_s3_bucket.releases.arn,
86 "${aws_s3_bucket.releases.arn}/*",
87 ]
88
89 principals {
90 type = "*"
91 identifiers = ["*"]
92 }
93
94 condition {
95 test = "Bool"
96 variable = "aws:SecureTransport"
97 values = ["false"]
98 }
99 }
100}
101
102resource "aws_s3_bucket_policy" "releases" {
103 bucket = aws_s3_bucket.releases.id
104 policy = data.aws_iam_policy_document.releases_public_read.json
105
106 # The policy grants public access, so the access block must relax
107 # block_public_policy before it can be attached.
108 depends_on = [aws_s3_bucket_public_access_block.releases]
109}
110
111# The publisher's credentials: object put + list on this bucket, nothing else
112# in the account β€” and no DeleteObject, so a leaked publisher key can
113# overwrite the current manifest (noncurrent versions survive, versioning is
114# the rollback) but never destroy history. Mint the access key by hand
115# (aws iam create-access-key --user-name release-publisher) β€” keys never land
116# in tofu state.
117resource "aws_iam_user" "release_publisher" {
118 name = "release-publisher"
119}
120
121data "aws_iam_policy_document" "release_publisher" {
122 statement {
123 sid = "ListReleasesBucket"
124 actions = ["s3:ListBucket"]
125 resources = [aws_s3_bucket.releases.arn]
126 }
127
128 statement {
129 sid = "WriteReleaseObjects"
130 actions = [
131 "s3:GetObject",
132 "s3:PutObject",
133 ]
134 resources = ["${aws_s3_bucket.releases.arn}/*"]
135 }
136}
137
138resource "aws_iam_user_policy" "release_publisher" {
139 name = "release-publisher-bucket"
140 user = aws_iam_user.release_publisher.name
141 policy = data.aws_iam_policy_document.release_publisher.json
142}
143
144output "releases_bucket" {
145 value = aws_s3_bucket.releases.bucket
146}
147
148# What instances set as LCHAT_RELEASE_MIRROR_URL.
149output "releases_mirror_url" {
150 value = "https://${aws_s3_bucket.releases.bucket_regional_domain_name}/releases"
151}
152