-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
399 lines (377 loc) · 17.1 KB
/
docker-compose.yml
File metadata and controls
399 lines (377 loc) · 17.1 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
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
services:
traefik:
image: traefik:v3.6.14
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.http.address=:80
- --entrypoints.https.address=:443
- --entrypoints.http.http.redirections.entrypoint.to=https
- --entrypoints.http.http.redirections.entrypoint.scheme=https
# The auth headers get dropped even when the auth middleware is not mapped
# so malicious clients cannot peddle them to misconfigured services
- --entrypoints.https.http.middlewares=drop-auth-headers@docker,hsts-headers@file
# Currently the default TLS configuration can only be set through the file
# provider, cf. https://github.com/traefik/traefik/issues/5507
- --providers.file.filename=/run/configs/tls.yml
# Default cert resolver, required even if only one is configured. This
# parameter avoids having to configure the resolver on every service.
- --entrypoints.https.http.tls.certresolver=letsencrypt
# Letsencrypt ACME
# See note at the botton on the TLS-ALPN-01 challenge
- --certificatesresolvers.letsencrypt.acme.tlschallenge=true
- --certificatesresolvers.letsencrypt.acme.email=${ADMIN_MAIL}
- --certificatesresolvers.letsencrypt.acme.storage=/certs/letsencrypt-acme.json
# In test environments, uncomment the following line to avoid hitting
# letsencrypt's rate limits.
# Note: traefik does not update existing certificates when the parameter
# changes, you need to remove the letsencrypt-acme.json file to refresh
# them.
#- --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
# ZeroSSL ACME (free account required)
#- --certificatesresolvers.zerossl.acme.httpchallenge.entrypoint=http
#- --certificatesresolvers.zerossl.acme.email=${ADMIN_MAIL}
#- --certificatesresolvers.zerossl.acme.storage=/certs/zerossl-acme.json
#- --certificatesresolvers.zerossl.acme.caserver=https://acme.zerossl.com/v2/DV90
#- --certificatesresolvers.zerossl.acme.eab.kid=${ZEROSSL_EAB_KID}
#- --certificatesresolvers.zerossl.acme.eab.hmacencoded=${ZEROSSL_EAB_HMAC_B64}
# Add support for authenticating subsonic clients.
# Only the password scheme is supported (`p` subsonic parameter), not the
# token scheme (`t` and `s` parameters). The password scheme is sometimes
# labelled "less secure" or "legacy" in subsonic clients, and must usually
# be explicitly enabled.
#
# Read the SECURITY WARNING section in the plugin's readme before
# deploying, and make sure to understand the security and privacy
# implications of using this plugin.
# THE AUTHORS OF- AND CONTRIBUTORS TO THIS COMPOSE FILE DECLINE ALL
# RESPONSIBILITY FOR YOUR USE OF THIS PLUGIN.
- --experimental.plugins.subsonic-basicauth.modulename=github.com/crazygolem/traefik-subsonic-basicauth
- --experimental.plugins.subsonic-basicauth.version=v0.2.0
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-certs:/certs
configs:
- source: traefik-tls.yml
target: /run/configs/tls.yml
authelia:
image: authelia/authelia:4.39.19
restart: unless-stopped
user: 1000:1000
depends_on:
authelia-cache:
condition: service_started
expose:
- 9091
labels:
traefik.enable: true
traefik.http.routers.authelia.rule: Host(`auth.${DOMAIN}`)
traefik.http.routers.authelia.entrypoints: https
# Standard authentication middleware to be used by most services
traefik.http.middlewares.authelia.forwardauth.address: http://authelia:9091/api/authz/web
traefik.http.middlewares.authelia.forwardauth.authResponseHeaders: Remote-User,Remote-Groups,Remote-Name,Remote-Email
# Security middleware for the entrypoints
traefik.http.middlewares.drop-auth-headers.headers.customrequestheaders.Remote-User:
traefik.http.middlewares.drop-auth-headers.headers.customrequestheaders.Remote-Groups:
traefik.http.middlewares.drop-auth-headers.headers.customrequestheaders.Remote-Name:
traefik.http.middlewares.drop-auth-headers.headers.customrequestheaders.Remote-Email:
# Subsonicauth middleware
traefik.http.middlewares.authelia-subsonicauth.chain.middlewares: subsonicauth-sub2basic@docker,authservice-basicauth@docker,subsonicauth-cleanup@docker
traefik.http.middlewares.authservice-basicauth.forwardauth.address: http://authelia:9091/api/authz/basic
traefik.http.middlewares.authservice-basicauth.forwardauth.authResponseHeaders: Remote-User,Remote-Groups,Remote-Name,Remote-Email
traefik.http.middlewares.subsonicauth-sub2basic.plugin.subsonic-basicauth.auth: proxy
traefik.http.middlewares.subsonicauth-sub2basic.plugin.subsonic-basicauth.header: Authorization
traefik.http.middlewares.subsonicauth-cleanup.headers.customrequestheaders.Authorization: # empty removes the header
healthcheck:
disable: true
environment:
TZ: Europe/Zurich
X_AUTHELIA_CONFIG_FILTERS: template
X_AUTHELIA_CONFIG: /run/configs/
# If the file does not exist, authelia outputs an error, generates the
# file and then exits. After it restarts the service then works fine.
AUTHELIA_AUTHENTICATION_BACKEND_FILE_PATH: /config/users.yml
AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_CASE_INSENSITIVE: true
AUTHELIA_AUTHENTICATION_BACKEND_FILE_WATCH: true
AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET_FILE: /run/secrets/authelia-jwt-secret
AUTHELIA_NOTIFIER_SMTP_ADDRESS: ${SMTP_HOST}:${SMTP_PORT}
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE: /run/secrets/smtp-pass
AUTHELIA_NOTIFIER_SMTP_SENDER: ${SYSTEM_MAIL_SENDER_NAME} <${SYSTEM_MAIL_SENDER_ADDR}>
AUTHELIA_NOTIFIER_SMTP_USERNAME: ${SMTP_USER}
AUTHELIA_PASSWORD_POLICY_ZXCVBN_ENABLED: true
AUTHELIA_SESSION_EXPIRATION: 48h
AUTHELIA_SESSION_INACTIVITY: 8h
AUTHELIA_SESSION_REDIS_HOST: authelia-cache
AUTHELIA_SESSION_REDIS_PORT: 6379
AUTHELIA_SESSION_SECRET_FILE: /run/secrets/authelia-session-secret
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: /run/secrets/authelia-storage-encryption-key
AUTHELIA_STORAGE_LOCAL_PATH: /config/storage.sqlite3
AUTHELIA_TOTP_ISSUER: ${DOMAIN}
PORTAL_URL: https://auth.${DOMAIN}
DOMAIN: ${DOMAIN}
volumes:
- authelia-config:/config
configs:
- source: authelia-configuration.yml
target: /run/configs/configuration.yml
- source: authelia-access-control.yml
target: /run/configs/access-control.yml
secrets:
- smtp-pass
- authelia-jwt-secret
- authelia-session-secret
- authelia-storage-encryption-key
authelia-cache:
image: valkey/valkey:8.1-alpine
restart: unless-stopped
user: 1000:1000
volumes:
- authelia-cache:/data
navidrome:
image: deluan/navidrome:0.61.2
restart: unless-stopped
user: 1000:1000
depends_on:
# Only actually needed when deploying from scratch, but what you gonna do
user-volumes:
condition: service_completed_successfully
expose:
- 4533
labels:
traefik.enable: true
traefik.http.routers.navidrome.rule: Host(`music.${DOMAIN}`)
traefik.http.routers.navidrome.entrypoints: https
traefik.http.routers.navidrome.middlewares: authelia@docker
traefik.http.routers.navidrome-subsonic.rule: Host(`music.${DOMAIN}`) && PathPrefix(`/rest/`) && !Query(`c`, `NavidromeUI`)
traefik.http.routers.navidrome-subsonic.entrypoints: https
traefik.http.routers.navidrome-subsonic.middlewares: authelia-subsonicauth@docker
environment:
# All requests come through traefik, and we cannot specify a hostname only
# an IP range, so we must trust all IPs. This means that any other service
# in the same docker network can make a request to navidrome, and easily
# impersonate an admin.
ND_EXTAUTH_TRUSTEDSOURCES: 0.0.0.0/0
ND_EXTAUTH_LOGOUTURL: https://auth.${DOMAIN}/logout
# Listen kids, don't passwords like navidrome 🙄
ND_PASSWORDENCRYPTIONKEY: ${NAVIDROME_WTF}
# Authentication is fully delegated to traefik/authelia (even for subsonic
# clients) so users don't need to manage their password in navidrome. The
# email doesn't seem to be used for anything but gravatar (which is
# disabled), so really the user screen is useless...
ND_ENABLEUSEREDITING: false
# Regular local backups saved in the data volume. This is meant mostly as
# a lazy fail-safe, and should be coupled with a more robust backup
# mechanism. The schedule and retention configuration below is adapted for
# a stable and low-use instance.
ND_BACKUP_PATH: /data/backups
ND_BACKUP_SCHEDULE: 0 5 ? * 1
ND_BACKUP_COUNT: 4
# The feature is disabled to prevent accidentally leaking sensitive config
# values: The current implementation uses a blacklist to redact those
# values (and long values still have some of their content shown), and
# expects every developer to remember to add their new sensitive option to
# the list, or reviewers to catch it before it's too late.
ND_DEVUISHOWCONFIG: false
# The sharing feature is still under development, and currently does not
# implement permissions, giving all users admin-like rights over all
# shares.
ND_ENABLESHARING: false
# Various other adjustments that are not relevant to integration.
ND_DEFAULTPLAYLISTPUBLICVISIBILITY: true
ND_SCANNER_PURGEMISSING: always
# Navidrome's default mapping for the YEAR tag (and equivalents) is a bit
# inconsistent (mapped to recordingdate or releasedate depending on the
# file format), and more importantly not in the same way as my main music
# library manager. I am also using this tag inconsistently (sometimes as
# recording and other times as release date). All this can lead to albums
# being incorrectly split by navidrome in some situations when the date is
# taken into account for identifying albums.
# This is the default configuration without the `releasedate` attribute.
ND_PID_ALBUM: musicbrainz_albumid|albumartistid,album,albumversion
volumes:
- navidrome-data:/data
- navidrome-cache:/data/cache
- /srv/music:/music:ro
syncthing:
build:
context: images/syncthing
args:
syncthing_version: 2.0.16
image: mustash-syncthing:2.0.16
restart: unless-stopped
user: 1000:1000
depends_on:
# If run as root, the entrypoint only sets the ownership of the HOME
# directory, not the actual config directory below it.
user-volumes:
condition: service_completed_successfully
expose:
- 8384 # Web UI, through traefik
labels:
traefik.enable: true
traefik.http.routers.syncthing.rule: Host(`syncthing.${DOMAIN}`)
traefik.http.routers.syncthing.entrypoints: https
traefik.http.routers.syncthing.middlewares: authelia@docker
ports:
- 22000:22000/tcp # TCP file transfers
- 22000:22000/udp # QUIC file transfers
environment:
GUI_ENABLED: true
GUI_UNPROTECTED: true # The GUI is behind authelia and hopefully it works
INSTANCE_NAME: ${DOMAIN}
volumes:
- syncthing-config:/var/syncthing/config
# Mounted as a subdirectory of /srv/music used by navidrome to allow other
# sources to contribute music.
- /srv/music/library:/data/music-library
# Set the ownership of freshly created volumes because docker doesn't provide
# this feature essential to non-root containers, and compose didn't find it
# judicious to palliate that 🙄
user-volumes:
build:
context: images/user-volumes
volumes:
- authelia-cache:/mnt/authelia-cache
- authelia-config:/mnt/authelia-config
- navidrome-cache:/mnt/navidrome-cache
- navidrome-data:/mnt/navidrome-data
- syncthing-config:/mnt/syncthing-config
volumes:
authelia-cache:
labels:
volume-backup: exclude
authelia-config:
navidrome-cache:
labels:
volume-backup: exclude
navidrome-data:
syncthing-config:
traefik-certs:
secrets:
# Random, persistent string generated for this environment
authelia-jwt-secret:
file: secrets/authelia/jwt-secret
# Random, persistent string generated for this environment
authelia-session-secret:
file: secrets/authelia/session-secret
# Random, persistent string generated for this environment
authelia-storage-encryption-key:
file: secrets/authelia/storage-encryption-key
# Password for the authentication against an external SMTP server
smtp-pass:
file: secrets/smtp-pass
# Some configurations cannot be passed as environment variables (usually because
# some value contain arbitrary structured data) or labels (because the container
# does not support reading them). The `config` top-level element allows to pass
# those configs as file to the containers, while still keeping their content in
# a single compose file along all the other non-sensitive configs.
configs:
# Main authelia configuration. Requires the 'template' config filter to be
# enabled.
authelia-configuration.yml:
content: |
server:
endpoints:
authz:
web:
implementation: ForwardAuth
authn_strategies:
- name: CookieSession
basic:
implementation: ForwardAuth
authn_strategies:
- name: HeaderAuthorization
schemes: [ Basic ]
session:
cookies:
- domain: '{{ env "DOMAIN" }}'
authelia_url: '{{ env "PORTAL_URL" }}'
# Service-dependent config for authelia.
# Ideally there should be a way to define the ac rules (e.g. required roles)
# at the service level using docker annotations, but there doesn't seem to be
# a way to do that currently.
# https://github.com/authelia/authelia/discussions/5023
authelia-access-control.yml:
content: |
access_control:
default_policy: deny
rules:
- domain:
- music.{{ env "DOMAIN" }}
resources:
# Non-sensitive assets from navidrome repo's ui/public/ directory.
# If manifest.webmanifest and the assets it references are not
# listed here, navidrome cannot be properly installed as a PWA.
- '/favicon\.ico$'
- '/robots\.txt$'
- '^/app/android-chrome(-\d+x\d+)?\.png$'
- '^/app/apple-touch-icon(-\d+x\d+)?\.png$'
- '^/app/favicon(-\d+x\d+)?\.png$'
- '^/app/manifest\.webmanifest$'
- '^/app/safari-pinned-tab\.svg$'
# Public shares endpoint. If public shares are not enabled,
# navidrome will reject the requests itself.
- '^/share/'
policy: bypass
- domain:
- music.{{ env "DOMAIN" }}
subject:
- [ group:user ]
policy: one_factor
- domain:
- syncthing.{{ env "DOMAIN" }}
subject:
- [ group:admin ]
policy: two_factor
traefik-tls.yml:
content: |
tls:
options:
default:
sniStrict: true
# From Mozilla's SSL Configuration Generator, intermediate settings
# https://ssl-config.mozilla.org
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
curvePreferences:
- X25519
- CurveP256
- CurveP384
http:
middlewares:
hsts-headers:
headers:
stsSeconds: 15552000
stsIncludeSubdomains: true
# NOTE ON THE ACME TLS-ALPN-01 CHALLENGE
#
# If AAAA records are present for the host, letsencrypt will use it to perform
# the challenge verification and won't try A records if the verification fails
# due to an unreachable host. Combine this with the fact that docker does not
# enable IPv6 by default, and this leads to very confusing results and unhelpful
# errors.
#
# On top of that, if IPv6 is enabled in docker and not carefully configured,
# each container can potentially get a public IPv6 address and become reachable
# on any port from the outside world. In short: IPv6 in docker does not work the
# same as IPv4, and in particular there is no NAT later protecting the
# containers.
#
# So for now, the best course of action seem to be removing the AAAA records for
# the host, handle letsencrypt certificates from outside of docker, or use
# another challenge type (HTTP-01 works well from within docker, even with AAAA
# records pointing to the host).
#
# See also:
# - https://stackoverflow.com/q/66090810
# - https://github.com/robbertkl/docker-ipv6nat