Repository navigation
Expand file tree
/
Copy pathsensitive.example.yaml
More file actions
323 lines (308 loc) · 19.7 KB
/
Copy pathsensitive.example.yaml
File metadata and controls
323 lines (308 loc) · 19.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
---
# Template for secrets/sensitive.sops.yaml.
#
# TWENTY-EIGHT KEYS. Read the list as a description of the stack: Caddy needs no
# credential — its certificate and key are files under certificates/, never
# tracked, handled the way the estate's Grafana leaf is — step-ca needs
# exactly one, the password that unlocks its intermediate key, AdGuard Home
# needs one, the hash of its admin password (#135), Immich needs one, the
# password its server presents to its own Postgres (#132), Vaultwarden needs
# one, the hash of its admin token (#131), Paperless-ngx needs three (#133),
# Glance needs eight: the Prometheus reader, a Fever key, two read-only keys
# for the Household page's numbers, and four lists of the operator's own
# (ADR-0098, ADR-0100, ADR-0103), ntfy
# needs six (#136), Miniflux needs two (#147), linkding one (#144), Actual one,
# its server password (all at the end of this file, #142), and Stirling-PDF
# one, its admin's first password (#143). Home Assistant needs NONE from
# here, which is not the same as needing none. Its device
# credentials — the Hue application key, the Ring token, everything a config
# flow produces — are written by Home Assistant into its own store in the
# home-assistant-config volume, and no environment variable or rendered file
# could carry them in; compose.yaml and ADR-0035 say what protects them
# instead. This file grows with the tier and the compose file's `${VAR:?}`
# guards say what is required, so `make render STACK=sensitive` asks for what
# this stack needs and nothing else. Actual's is the one exception:
# seed-actual-password.sh requires it instead, for the reason given with it.
#
# ---------------------------------------------------------------------------
# Create this ON TRINITY, not on the monitoring host
# ---------------------------------------------------------------------------
#
# make secrets-init STACK=sensitive # first time only — creates the age keypair
# make secrets-edit STACK=sensitive # opens the encrypted file in $EDITOR
#
# secrets-init writes trinity's public key into the `sensitive` rule in
# .sops.yaml — its own rule, so that the tier's host can open the tier's
# secrets and nothing else: not the SNMP communities, not the Grafana admin
# password. It refuses if the key it would write is already a recipient of
# another rule. The technical second's recipient (ADR-0024, #294) is added to
# the same rule with `make secrets-add-recipient PUBKEY=... STACK=sensitive`
# on the day the tier holds real data, which is what ADR-0023 asks for the
# household's secrets.
#
# The password below is the one `make tier-ca ARGS=--mint` is given on the
# monitoring host when the tier's root and intermediate are minted; step
# encrypts both keys with it, and the two copies must match or step-ca cannot
# open its own key. Generate it with `make gen-secret`, put it here first, then
# pass it to --mint from a file (never argv) — docs/runbooks/build-the-tier-ca.md.
# It also unlocks the root key that stays on the monitoring host, whose age key
# cannot open THIS file — so using that root is a two-host operation on
# purpose (ADR-0037). Long and random; it is typed nowhere after that.
STEPCA_PASSWORD: change-me-to-something-long
# A bcrypt HASH, not the password (#135). AdGuard Home stores its admin
# password hashed and compares against the hash, so the hash is the only form
# this repository ever needs; the password itself lives in the password
# manager and is typed into the UI at adguard.matrix.elysium. Make it with
#
# make hash-password
#
# which prompts for the password and prints the hash — the password is never
# an argument, so it is never in shell history or `ps`. The hash starts `$2a$`
# and carries more `$`; paste it as-is. render-config.sh escapes it for
# compose's .env, and compose.yaml's entrypoint substitutes it into the
# tracked AdGuardHome.yaml at start, so the file in git never holds it.
ADGUARD_ADMIN_PASSWORD_HASH: change-me-to-the-output-of-make-hash-password
# The password Immich's server presents to its own Postgres (#132), and the
# one Postgres creates its role with on first start — one value, two readers,
# so the pair cannot drift. Generated (`make gen-secret`), never typed. It
# was named in compose.yaml's guards and this file's header but had no entry
# here until the #131 merge, so `make render STACK=sensitive` would have asked
# for a key the template never mentioned.
IMMICH_DB_PASSWORD: change-me-to-something-long-and-random
# Vaultwarden's /admin token, as an Argon2id PHC string — never the token
# itself (#131). Generate it on any machine with docker, from the pinned image:
#
# docker run --rm -it "$(COMPOSE_FILE=stacks/sensitive/compose.yaml \
# ./scripts/image-for.sh vaultwarden)" /vaultwarden hash --preset owasp
#
# It asks for the token twice and prints `ADMIN_TOKEN='$argon2id$v=19$...'`.
# The value below is the part between the quotes, dollar signs and all;
# render-config.sh escapes it for compose's .env the way it does the hash
# above. The token you typed is what opens /admin and is held nowhere in this
# estate — keep it with the operator's other credentials, since it is
# precisely the thing this vault cannot hold for you.
VAULTWARDEN_ADMIN_TOKEN: change-me-to-an-argon2id-phc-string
# Paperless-ngx (#133). All three generated (`make gen-secret`), none typed:
#
# PAPERLESS_SECRET_KEY signs sessions and tokens. Rotating it logs every
# browser out and invalidates API tokens — nothing
# stored is encrypted with it.
# PAPERLESS_DBPASS the Postgres role's password. One value read by
# two containers (PAPERLESS_DBPASS in the app,
# POSTGRES_PASSWORD in the database), so the pair
# cannot drift. Postgres reads it once, at first
# start, to create the role: rotating it here after
# that changes what the app sends and not what the
# database expects, so a rotation is `ALTER ROLE`
# first and this file second.
# PAPERLESS_ADMIN_PASSWORD the superuser created on first start, and only
# then — the same variable does not change an
# existing account's password, so a rotation is
# done in the UI and recorded here afterwards.
# TOTP is enrolled on this account at first login
# (ADR-0022); stacks/sensitive/README.md has the
# step.
PAPERLESS_SECRET_KEY: change-me-to-something-long-and-random
PAPERLESS_DBPASS: change-me-to-something-long-and-random
PAPERLESS_ADMIN_PASSWORD: change-me-to-something-long-and-random
# Stirling-PDF (#143, ADR-0063). Generated (`make gen-secret`), not typed:
#
# STIRLING_ADMIN_PASSWORD the admin created on the first start against an
# empty stirling-pdf-configs volume, and only then,
# in PAPERLESS_ADMIN_PASSWORD's shape: a rotation is
# done in the UI and recorded here afterwards. TOTP
# is enrolled on this account at first login
# (ADR-0022). Since the volume is not backed up, a
# rebuild recreates the admin from this value, so
# it has to stay current.
STIRLING_ADMIN_PASSWORD: change-me-to-something-long-and-random
# Glance's Household page (ADR-0103), which took over from Homepage (#137):
# one credential per live number, each minted in the UI of the service it
# reads, AFTER that service is up, and each scoped so it can read the number
# and do nothing else. The other tiles are links because their services cannot
# issue a token like that. Both guards are `:?`, so `make up STACK=sensitive`
# refuses until both exist: mint them, `make secrets-edit STACK=sensitive`,
# then `make up`. The two minted for Homepage were carried over as they were,
# under these names.
#
# GLANCE_IMMICH_API_KEY Immich › Account Settings › API Keys › New, as
# the admin (server statistics are an admin
# endpoint), with ONE permission ticked:
# `server.statistics`. Not "all": an unscoped key
# is the admin account.
# GLANCE_PAPERLESS_TOKEN A Paperless user named `homepage` (Homepage's
# name, kept so nothing in Paperless moved), not staff and
# not superuser, with View and nothing else on
# Document, Tag, Correspondent and Document Type;
# then its token, from Profile › API Auth Token
# while logged in as it (or from /admin › Auth
# tokens). Paperless counts only what the user can
# see, and a document is owned by whoever uploaded
# it — so add `homepage` to the view permissions
# of the consumption workflow, or the page shows a
# smaller number than is true, which is the green
# dot this page refuses in another form.
GLANCE_IMMICH_API_KEY: change-me-to-an-immich-api-key-scoped-to-server-statistics
GLANCE_PAPERLESS_TOKEN: change-me-to-the-homepage-users-token
# Prometheus on `prometheus` wants a token off-host (#182, ADR-0067). These
# two are the READER token from secrets/observability.sops.yaml
# (INGEST_TOKEN_READER), copied here because this file is decrypted on trinity
# and that one is not. The reader can query and do nothing else. It cannot
# push, cannot reach the admin API, and cannot reach Loki's delete API.
#
# GLANCE_PROMETHEUS_TOKEN the token itself, for Glance's panels
# (ADR-0098). glance.yml sends it as
# `Authorization: Bearer …`.
# HA_PROMETHEUS_AUTHORIZATION the whole header value, "Bearer <token>".
# Home Assistant reads it with `!env_var` into
# the rack-power sensor's `headers:`, and
# `!env_var` cannot concatenate.
#
# Rotating the reader is three edits, all of the same value: this file twice
# and the observability one once. Then `make up` on both hosts.
GLANCE_PROMETHEUS_TOKEN: change-me-to-the-reader-token
HA_PROMETHEUS_AUTHORIZATION: Bearer change-me-to-the-reader-token
# The LG TV's full wired MAC, for Home Assistant's Wake-on-LAN call
# (ADR-0097, #1218). It is not a credential. It is here because
# docs/security.md truncates every MAC in the repository to its OUI, and SOPS
# is where a withheld value lives. Lowercase, colon-separated. Read it from
# the TV (Settings, Support, TV information) or from its reservation on
# morpheus.
HA_MEDIATV_MAC: "00:00:5e:00:53:00"
# The Ring Alarm's MQTT broker (ADR-0104). One password per client, and the
# broker builds its password file from both on every start, so changing one
# here and `make up` is the rotation. Generate each with scripts/gen-secret.sh.
#
# MQTT_HA_PASSWORD Home Assistant's, user `homeassistant`. Also typed
# once into Home Assistant's MQTT integration, so a
# rotation changes it there as well.
# MQTT_RING_PASSWORD ring-mqtt's, user `ring-mqtt`. Only compose.yaml
# uses it.
MQTT_HA_PASSWORD: change-me
MQTT_RING_PASSWORD: change-me
# ntfy (#136). compose.yaml builds its users, access list and token from these
# on every start (DIFFERENCE 12), so changing one here and `make up` IS the
# rotation — there is no ntfy CLI step.
#
# NTFY_ALERTMANAGER_TOKEN the bearer token Alertmanager publishes
# with. The SAME value as
# ALERTMANAGER_NTFY_TOKEN in
# secrets/observability.sops.yaml; rotate
# both, trinity first. Made with the
# pinned image's `ntfy token generate` —
# the command is beside that key.
# NTFY_ALERTMANAGER_PASSWORD_HASH a bcrypt hash of a password nobody keeps.
# ntfy wants one per user, and this user
# authenticates by token alone:
# `make gen-secret`, then `make
# hash-password` on it, then forget it.
# NTFY_PHONE_PASSWORD_HASH the bcrypt hash of the password the ntfy
# app on each phone logs in with, as user
# `phone` (`make hash-password`). The
# password goes in the password manager.
# NTFY_TOPIC_ALERTS / _URGENT / _SECURITY
# three topic names, each `make gen-secret`
# output. The same three names end the URLs
# in secrets/observability.sops.yaml. With
# deny-all a name grants nothing by itself,
# but #136 asked that they be kept like a
# credential, and they are.
#
# Both hashes carry `$`; paste them as they are — render-config.sh escapes them
# for compose's .env, as it does AdGuard's.
NTFY_ALERTMANAGER_TOKEN: tk_changemechangemechangeme00
NTFY_ALERTMANAGER_PASSWORD_HASH: change-me-to-the-output-of-make-hash-password
NTFY_PHONE_PASSWORD_HASH: change-me-to-the-output-of-make-hash-password
NTFY_TOPIC_ALERTS: change-me-to-something-long-and-random
NTFY_TOPIC_URGENT: change-me-to-something-long-and-random
NTFY_TOPIC_SECURITY: change-me-to-something-long-and-random
# Miniflux (#147, ADR-0057). Both generated (`make gen-secret`), none typed:
#
# MINIFLUX_DBPASS the Postgres role's password, read by both
# containers — miniflux-db as POSTGRES_PASSWORD, and
# miniflux spliced into DATABASE_URL — so the pair
# cannot drift. Because it sits inside a connection
# string it must be gen-secret's alphanumeric
# output: a space or a quote changes what the string
# says. Rotation is
# PAPERLESS_DBPASS's: `ALTER ROLE` first, this file
# second.
# MINIFLUX_ADMIN_PASSWORD the admin created on first start, and only then —
# every later start logs "Skipping admin user
# creation" and leaves the account alone. A rotation
# is done in the UI, or with `miniflux
# -reset-password` (stacks/sensitive/README.md), and
# recorded here afterwards. There is no second factor
# to enrol: Miniflux has no TOTP, and ADR-0057 says
# why its passkeys are not one.
MINIFLUX_DBPASS: change-me-to-something-long-and-random
MINIFLUX_ADMIN_PASSWORD: change-me-to-something-long-and-random
# Glance's feed widget (ADR-0098): the Fever API key of Miniflux's user
# `glance`, which follows only the feeds the start page shows and is never
# the admin. ADR-0057 keeps Miniflux's REST API off and pre-authorises a Fever
# credential per user on three conditions — a generated password, kept in the
# password manager, noted in stacks/sensitive/README.md with the client and
# the date — and this is one. The key can read and mark that user's items and
# nothing else; it cannot add a feed or reach the admin.
#
# 1. Miniflux › Settings › Users › Add user: `glance`, not an administrator,
# a `make gen-secret` password (into the password manager).
# 2. Log in as `glance`. Settings › Integrations › Fever: enable it, Fever
# username `glance`, Fever password a second `make gen-secret` (into the
# password manager too). Save.
# 3. The key is the MD5 of "username:password", which is what Fever clients
# send and what Miniflux stores:
#
# printf '%s' 'glance:<fever password>' | md5sum
#
# The 32 hex characters go here.
#
# Rotation is a new Fever password in step 2, then step 3 again.
GLANCE_MINIFLUX_FEVER_KEY: change-me-to-the-md5-of-glance-colon-fever-password
# Glance's own lists (ADR-0100). NOT credentials. They are here because this
# repository is public and they describe a person: what they watch, read,
# invest in and log in to. Each is ONE LINE of JSON, which Glance substitutes
# into pages/home.yml as YAML. Keep each on one line, in single quotes, and
# never put a single quote inside: secrets-env.sh strips the outer quotes and
# unescapes nothing. `make up STACK=sensitive` after an edit.
#
# GLANCE_MARKETS the ticker strip: [{"symbol":"SPY","name":"S&P 500"}, …]
# Yahoo Finance symbols (BTC-USD, GC=F, ^FTSE).
# GLANCE_BOOKMARKS bookmark groups: [{"title":"Daily","links":[{"title":
# "Gmail","url":"https://mail.google.com","icon":
# "/assets/icons/gmail.svg"}]}]. Icons are files under
# stacks/sensitive/glance/assets/icons, or leave it out.
# GLANCE_VIDEO_TABS one videos widget per tab: [{"type":"videos","title":
# "Tech","channels":["UC…"]}]. A channel's id is the
# `UC…` in its page source ("externalId").
# GLANCE_REDDIT_TABS one reddit widget per tab: [{"type":"reddit",
# "subreddit":"worldnews"}].
GLANCE_MARKETS: '[{"symbol":"SPY","name":"S&P 500"}]'
GLANCE_BOOKMARKS: '[{"title":"Daily","links":[{"title":"Gmail","url":"https://mail.google.com"}]}]'
GLANCE_VIDEO_TABS: '[{"type":"videos","title":"Homelab","channels":["UCR-DXc1voovS8nhAvccRZhg"]}]'
GLANCE_REDDIT_TABS: '[{"type":"reddit","subreddit":"worldnews"}]'
# linkding (#144, ADR-0061). The superuser created on first start, and only
# then, as PAPERLESS_ADMIN_PASSWORD is: a later change here does not reach the
# account, so a rotation is done in linkding's settings and recorded here
# afterwards. Generated (`make gen-secret`), and kept in Vaultwarden. This
# password is the whole of the login: linkding has no second factor, and
# ADR-0061 says why that is accepted. Its user name is LINKDING_SUPERUSER_NAME
# in stacks/sensitive/.env.example.
LINKDING_SUPERUSER_PASSWORD: change-me-to-something-long-and-random
# Actual's server password (#142, ADR-0062) — the one key here no container
# is handed. Actual has no password setting: a fresh server is claimed by the
# first client that sets one. scripts/seed-actual-password.sh claims it with
# this value, with no network, before the service's first start, and on every
# `make up` after that it logs in with it as a check. It is read straight from
# this file, so there is no `${VAR:?}` guard for it and it is not in .env.
# Generate it with `make gen-secret` and keep a copy in the password manager:
# it is what the household types into each Actual client, once per device.
#
# Changing it here changes nothing on the server: seed-actual-password.sh
# never overwrites a set password, and warns on the next `make up` that this
# value no longer logs in. Change it in Actual (Settings › Change password),
# or with `docker exec -it sensitive-actual node src/scripts/reset-password.js`
# on trinity, which needs a terminal, and then here. Measured on 26.9.0: a
# changed password does NOT sign out a device that is already signed in.
# stacks/sensitive/README.md has the way to do that.
ACTUAL_SERVER_PASSWORD: change-me-to-the-output-of-make-gen-secret