-
Notifications
You must be signed in to change notification settings - Fork 1
/
docker-compose.yml
275 lines (259 loc) · 12.4 KB
/
docker-compose.yml
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
services:
traefik:
image: traefik:v3.2.0
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/secrets/traefik-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
secrets:
- traefik-tls.yml
authelia:
image: authelia/authelia:4.38.17
restart: unless-stopped
command:
- --config
- /run/secrets/authelia-access-control.yml
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/verify?rd=https://auth.${DOMAIN}/
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/verify?auth=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
# 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_DOMAIN: ${DOMAIN}
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
volumes:
- authelia-config:/config
secrets:
- smtp-pass
- authelia-access-control.yml
- authelia-jwt-secret
- authelia-session-secret
- authelia-storage-encryption-key
authelia-cache:
image: redis:7.2-alpine
restart: unless-stopped
volumes:
- authelia-cache:/data
navidrome:
image: deluan/navidrome:0.53.3
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_REVERSEPROXYWHITELIST: 0.0.0.0/0
# 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
# 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
volumes:
- navidrome-data:/data
- /srv/music:/music:ro
syncthing:
build:
context: images/syncthing
args:
syncthing_version: 1.28.0
image: mustash-syncthing:1.28.0
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:
- navidrome-data:/mnt/navidrome-data
- syncthing-config:/mnt/syncthing-config
volumes:
authelia-cache:
authelia-config:
navidrome-data:
syncthing-config:
traefik-certs:
secrets:
# Ideally there should be a way to define the ac rules (e.g. required roles)
# at the service level using traefik annotations, but there doesn't seem to be
# a way to do that currently.
# On top of that authelia doesn't currently support defining ac rules using
# environment variables. The only way is to have them in a configuration file
# in the container.
# Mounting using secrets instead of a normal bind mount makes it a bit nicer
# on the original file on the host (e.g. no ownership change) and allows if
# needed to specify a different owner in the container.
authelia-access-control.yml:
file: configs/authelia/access-control.yml
# 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
# Nothing sensitive in there, it's just more convenient to mount it as a
# secret, cf. comment on authelia's ac secret.
traefik-tls.yml:
file: configs/traefik/tls.yml
# 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