dash 3.2.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/dash/cli/app/boot.rb +2 -2
- data/lib/dash/cli/app.rb +1 -1
- data/lib/dash/cli/base.rb +1 -1
- data/lib/dash/cli/doctor/endpoint_checks.rb +1 -1
- data/lib/dash/cli/doctor/host_checks.rb +4 -4
- data/lib/dash/cli/main.rb +5 -5
- data/lib/dash/cli/proxy/legacy_rename.rb +58 -0
- data/lib/dash/cli/proxy/loadbalancer_reboot.rb +1 -1
- data/lib/dash/cli/proxy/reboot.rb +8 -8
- data/lib/dash/cli/proxy.rb +14 -10
- data/lib/dash/commander.rb +1 -1
- data/lib/dash/commands/accessory/proxy.rb +1 -1
- data/lib/dash/commands/app/execution.rb +1 -1
- data/lib/dash/commands/app/proxy.rb +1 -1
- data/lib/dash/commands/app.rb +1 -1
- data/lib/dash/commands/docker.rb +35 -1
- data/lib/dash/commands/loadbalancer.rb +47 -17
- data/lib/dash/commands/lock.rb +1 -1
- data/lib/dash/commands/proxy/cert_transfer.rb +4 -4
- data/lib/dash/commands/proxy.rb +112 -13
- data/lib/dash/commands/prune.rb +1 -1
- data/lib/dash/configuration/accessory.rb +2 -2
- data/lib/dash/configuration/docs/accessory.yml +3 -3
- data/lib/dash/configuration/docs/configuration.yml +1 -1
- data/lib/dash/configuration/docs/proxy.yml +38 -38
- data/lib/dash/configuration/loadbalancer.rb +2 -2
- data/lib/dash/configuration/proxy/acme.rb +5 -5
- data/lib/dash/configuration/proxy/boot.rb +2 -4
- data/lib/dash/configuration/proxy/run.rb +10 -10
- data/lib/dash/configuration/proxy.rb +36 -14
- data/lib/dash/configuration/role.rb +2 -2
- data/lib/dash/configuration/validator/proxy.rb +23 -23
- data/lib/dash/configuration.rb +8 -8
- data/lib/dash/utils.rb +1 -1
- data/lib/dash/version.rb +1 -1
- metadata +2 -1
data/lib/dash/commands/proxy.rb
CHANGED
|
@@ -28,7 +28,7 @@ class Dash::Commands::Proxy < Dash::Commands::Base
|
|
|
28
28
|
*proxy_run_config.network_args,
|
|
29
29
|
"--detach",
|
|
30
30
|
"--restart", "unless-stopped",
|
|
31
|
-
"--volume", "
|
|
31
|
+
"--volume", "dash-proxy-config:/home/dash-proxy/.config/dash-proxy",
|
|
32
32
|
*config_digest_label_args(digest),
|
|
33
33
|
*proxy_run_config.docker_options_args,
|
|
34
34
|
*proxy_run_config.image,
|
|
@@ -38,6 +38,58 @@ class Dash::Commands::Proxy < Dash::Commands::Base
|
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
+
# Stage 3c migrations. All three are idempotent and guarded on the
|
|
42
|
+
# destination not existing, so a second deploy is a no-op. Stage 3d deletes
|
|
43
|
+
# them along with the legacy constants they read.
|
|
44
|
+
|
|
45
|
+
# Copies the pre-rename config volume into the new one, before anything
|
|
46
|
+
# starts. The volume holds the routing table and the ACME account and
|
|
47
|
+
# certificate cache; losing it means re-issuing every certificate and
|
|
48
|
+
# spending Let's Encrypt rate limits to get back where we were.
|
|
49
|
+
#
|
|
50
|
+
# Runs in the dash-proxy image itself — already pulled by this point in the
|
|
51
|
+
# boot sequence, and its ubuntu base has sh and cp. `--user root` because the
|
|
52
|
+
# image's own user cannot write the destination volume; `cp -a` preserves the
|
|
53
|
+
# uid, which the rename leaves at 1001.
|
|
54
|
+
# The guard is negated and leads the chain, with `|| true` last, because
|
|
55
|
+
# shell `&&` and `||` share precedence and associate left: written as
|
|
56
|
+
# `exists || legacy_exists && create && copy` it would parse as
|
|
57
|
+
# `((exists || legacy_exists) && create) && copy` and re-copy the legacy
|
|
58
|
+
# volume over live state on every deploy. Leading with `! exists` makes the
|
|
59
|
+
# whole chain a single left-associative AND, which short-circuits correctly.
|
|
60
|
+
def copy_legacy_config_volume(volume: Dash::Configuration::Proxy::CONFIG_VOLUME, legacy: Dash::Configuration::Proxy::LEGACY_CONFIG_VOLUME)
|
|
61
|
+
any \
|
|
62
|
+
combine(
|
|
63
|
+
negate(volume_exists(volume)),
|
|
64
|
+
volume_exists(legacy),
|
|
65
|
+
docker(:volume, :create, volume),
|
|
66
|
+
copy_between_volumes(legacy, volume)
|
|
67
|
+
),
|
|
68
|
+
[ :true ]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Stops and removes a pre-rename proxy container so the renamed one can claim
|
|
72
|
+
# ports 80/443. No port-holder handoff spans two container names, which is
|
|
73
|
+
# why this stage accepts a brief outage per host.
|
|
74
|
+
def remove_legacy_container(timeout: nil)
|
|
75
|
+
any \
|
|
76
|
+
combine(
|
|
77
|
+
container_exists(Dash::Configuration::Proxy::LEGACY_CONTAINER_NAME),
|
|
78
|
+
docker(:container, :stop, *("--time=#{timeout}" if timeout), Dash::Configuration::Proxy::LEGACY_CONTAINER_NAME),
|
|
79
|
+
docker(:container, :rm, Dash::Configuration::Proxy::LEGACY_CONTAINER_NAME)
|
|
80
|
+
),
|
|
81
|
+
[ :true ]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def remove_legacy_holder_container
|
|
85
|
+
any \
|
|
86
|
+
combine(
|
|
87
|
+
container_exists(Dash::Configuration::Proxy::LEGACY_HOLDER_CONTAINER_NAME),
|
|
88
|
+
docker(:container, :rm, "--force", Dash::Configuration::Proxy::LEGACY_HOLDER_CONTAINER_NAME)
|
|
89
|
+
),
|
|
90
|
+
[ :true ]
|
|
91
|
+
end
|
|
92
|
+
|
|
41
93
|
def start
|
|
42
94
|
docker :container, :start, container_name
|
|
43
95
|
end
|
|
@@ -77,15 +129,15 @@ class Dash::Commands::Proxy < Dash::Commands::Base
|
|
|
77
129
|
end
|
|
78
130
|
|
|
79
131
|
def list(name: container_name, json: false)
|
|
80
|
-
docker :exec, name, "
|
|
132
|
+
docker :exec, name, "dash-proxy", :list, *("--json" if json)
|
|
81
133
|
end
|
|
82
134
|
|
|
83
135
|
def cache_stats(count: false, json: false)
|
|
84
|
-
docker :exec, container_name, "
|
|
136
|
+
docker :exec, container_name, "dash-proxy", :cache, :stats, *optionize({ count: count || nil, json: json || nil }.compact)
|
|
85
137
|
end
|
|
86
138
|
|
|
87
139
|
def cache_purge(service, path_prefix: nil)
|
|
88
|
-
docker :exec, container_name, "
|
|
140
|
+
docker :exec, container_name, "dash-proxy", :cache, :purge, service, *optionize({ "path-prefix": path_prefix }.compact)
|
|
89
141
|
end
|
|
90
142
|
|
|
91
143
|
# One mount destination per line - what the running container was actually
|
|
@@ -108,12 +160,12 @@ class Dash::Commands::Proxy < Dash::Commands::Base
|
|
|
108
160
|
docker \
|
|
109
161
|
:run,
|
|
110
162
|
"--name", proxy_run_config.holder_container_name,
|
|
111
|
-
"--network", "
|
|
163
|
+
"--network", "dash",
|
|
112
164
|
"--detach",
|
|
113
165
|
"--restart", "unless-stopped",
|
|
114
166
|
*proxy_run_config.holder_docker_args,
|
|
115
167
|
*proxy_run_config.image,
|
|
116
|
-
"
|
|
168
|
+
"dash-proxy", "hold"
|
|
117
169
|
end
|
|
118
170
|
|
|
119
171
|
def start_holder_or_run
|
|
@@ -131,7 +183,7 @@ class Dash::Commands::Proxy < Dash::Commands::Base
|
|
|
131
183
|
end
|
|
132
184
|
|
|
133
185
|
def drain(timeout: nil)
|
|
134
|
-
docker :exec, container_name, "
|
|
186
|
+
docker :exec, container_name, "dash-proxy", :drain, *("--drain-timeout=#{timeout}s" if timeout)
|
|
135
187
|
end
|
|
136
188
|
|
|
137
189
|
def wait_for_exit(name: container_name)
|
|
@@ -161,15 +213,24 @@ class Dash::Commands::Proxy < Dash::Commands::Base
|
|
|
161
213
|
|
|
162
214
|
# `retry` takes a host, or --all; the rest take no arguments.
|
|
163
215
|
def domains(subcommand, *args)
|
|
164
|
-
docker :exec, container_name, "
|
|
216
|
+
docker :exec, container_name, "dash-proxy", "domains", subcommand, *args
|
|
165
217
|
end
|
|
166
218
|
|
|
219
|
+
# Docker ANDs multiple `--filter label=` values, so matching both the current
|
|
220
|
+
# and the pre-rename image title takes two commands rather than one filter
|
|
221
|
+
# with two values. Without the legacy pass, `dash proxy remove` on a host that
|
|
222
|
+
# has not yet been through the rename silently leaves the old container and
|
|
223
|
+
# image behind. Stage 3d drops the legacy half.
|
|
167
224
|
def remove_container
|
|
168
|
-
|
|
225
|
+
combine \
|
|
226
|
+
prune_containers_titled(Dash::Configuration::Proxy::IMAGE_TITLE),
|
|
227
|
+
prune_containers_titled(Dash::Configuration::Proxy::LEGACY_IMAGE_TITLE)
|
|
169
228
|
end
|
|
170
229
|
|
|
171
230
|
def remove_image
|
|
172
|
-
|
|
231
|
+
combine \
|
|
232
|
+
prune_images_titled(Dash::Configuration::Proxy::IMAGE_TITLE),
|
|
233
|
+
prune_images_titled(Dash::Configuration::Proxy::LEGACY_IMAGE_TITLE)
|
|
173
234
|
end
|
|
174
235
|
|
|
175
236
|
def cleanup_traefik
|
|
@@ -245,7 +306,7 @@ class Dash::Commands::Proxy < Dash::Commands::Base
|
|
|
245
306
|
end
|
|
246
307
|
|
|
247
308
|
def cert_store_volume_args
|
|
248
|
-
[ "--volume", "
|
|
309
|
+
[ "--volume", "dash-proxy-config:/home/dash-proxy/.config/dash-proxy" ]
|
|
249
310
|
end
|
|
250
311
|
|
|
251
312
|
# Same fallback as #pull: without a run config the image comes from the
|
|
@@ -262,14 +323,52 @@ class Dash::Commands::Proxy < Dash::Commands::Base
|
|
|
262
323
|
[ "--label", "#{CONFIG_DIGEST_LABEL}=#{digest}" ] if digest
|
|
263
324
|
end
|
|
264
325
|
|
|
326
|
+
def negate(command)
|
|
327
|
+
[ "!", *command ]
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def volume_exists(name)
|
|
331
|
+
docker :volume, :inspect, name, ">", "/dev/null", "2>&1"
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def container_exists(name)
|
|
335
|
+
docker :container, :inspect, name, ">", "/dev/null", "2>&1"
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def copy_between_volumes(from, to)
|
|
339
|
+
docker \
|
|
340
|
+
:run, "--rm", "--user", "root", "--entrypoint", "sh",
|
|
341
|
+
"--volume", "#{from}:/from",
|
|
342
|
+
"--volume", "#{to}:/to",
|
|
343
|
+
proxy_image,
|
|
344
|
+
"-c", "'cp -a /from/. /to/'"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# The image the volume copy borrows. The proxy this gem is pinned to is
|
|
348
|
+
# already pulled by the time the copy runs, and `rake release` gates on
|
|
349
|
+
# MINIMUM_VERSION being published, so this is always resolvable — unlike
|
|
350
|
+
# the legacy boot path's image, which is read from a file on the host.
|
|
351
|
+
def proxy_image
|
|
352
|
+
proxy_run_config&.image ||
|
|
353
|
+
"#{config.proxy_boot.image_default}:#{Dash::Configuration::Proxy::Run::MINIMUM_VERSION}"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def prune_containers_titled(title)
|
|
357
|
+
docker :container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=#{title}"
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def prune_images_titled(title)
|
|
361
|
+
docker :image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=#{title}"
|
|
362
|
+
end
|
|
363
|
+
|
|
265
364
|
def docker_run(digest: nil)
|
|
266
365
|
docker \
|
|
267
366
|
:run,
|
|
268
367
|
"--name", container_name,
|
|
269
|
-
"--network", "
|
|
368
|
+
"--network", "dash",
|
|
270
369
|
"--detach",
|
|
271
370
|
"--restart", "unless-stopped",
|
|
272
|
-
"--volume", "
|
|
371
|
+
"--volume", "dash-proxy-config:/home/dash-proxy/.config/dash-proxy",
|
|
273
372
|
*config_digest_label_args(digest),
|
|
274
373
|
*config.proxy_boot.apps_volume.docker_args
|
|
275
374
|
end
|
data/lib/dash/commands/prune.rb
CHANGED
|
@@ -15,7 +15,7 @@ class Dash::Commands::Prune < Dash::Commands::Base
|
|
|
15
15
|
|
|
16
16
|
# Scoped to one role so a busy sibling role cannot push another role's newest
|
|
17
17
|
# container past the retain window. That matters beyond disk hygiene: a
|
|
18
|
-
# container
|
|
18
|
+
# container dash-proxy has put to sleep is `exited`, so it is a removal
|
|
19
19
|
# candidate, and once it is gone every wake 404s. With `retain >= 1` a role's
|
|
20
20
|
# newest container always survives, and the slept one is always the newest —
|
|
21
21
|
# sleeping happens to the current release.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
class Dash::Configuration::Accessory
|
|
2
2
|
include Dash::Configuration::Validation
|
|
3
3
|
|
|
4
|
-
DEFAULT_NETWORK = "
|
|
4
|
+
DEFAULT_NETWORK = "dash"
|
|
5
5
|
|
|
6
6
|
delegate :argumentize, :optionize, to: Dash::Utils
|
|
7
7
|
|
|
@@ -129,7 +129,7 @@ class Dash::Configuration::Accessory
|
|
|
129
129
|
|
|
130
130
|
# The load balancer fans the app's own service out to role targets only
|
|
131
131
|
# (see Dash::Cli::Proxy#loadbalancer), so an accessory is never behind it
|
|
132
|
-
# and must keep the host/TLS it registers with
|
|
132
|
+
# and must keep the host/TLS it registers with dash-proxy directly.
|
|
133
133
|
def initialize_proxy
|
|
134
134
|
Dash::Configuration::Proxy.new \
|
|
135
135
|
config: config,
|
|
@@ -150,7 +150,7 @@ accessories:
|
|
|
150
150
|
#
|
|
151
151
|
# The network the accessory will be attached to.
|
|
152
152
|
#
|
|
153
|
-
# Defaults to
|
|
153
|
+
# Defaults to dash:
|
|
154
154
|
network: custom
|
|
155
155
|
|
|
156
156
|
# Proxy
|
|
@@ -160,12 +160,12 @@ accessories:
|
|
|
160
160
|
# app-level `proxy:` (see `dash docs proxy`), though root-only keys such as
|
|
161
161
|
# `loadbalancer` and `reboot_on_deploy` have no effect inside an accessory.
|
|
162
162
|
# Declaring a proxy here makes the accessory's hosts proxy hosts:
|
|
163
|
-
# `dash proxy boot` (and `dash deploy`) will run
|
|
163
|
+
# `dash proxy boot` (and `dash deploy`) will run dash-proxy on them
|
|
164
164
|
# automatically.
|
|
165
165
|
#
|
|
166
166
|
# Accessories are never load balanced. The loadbalancer fans the app's own
|
|
167
167
|
# service out to its role targets, so an accessory always registers its
|
|
168
|
-
# hostname and TLS with the
|
|
168
|
+
# hostname and TLS with the dash-proxy on its own host — whether or not the
|
|
169
169
|
# loadbalancer is active for the app. Point the accessory's DNS record at the
|
|
170
170
|
# accessory host, not at the loadbalancer.
|
|
171
171
|
#
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Proxy
|
|
2
2
|
#
|
|
3
|
-
# Kamal uses [
|
|
3
|
+
# Kamal uses [dash-proxy](https://github.com/basecamp/dash-proxy) to provide
|
|
4
4
|
# gapless deployments. It runs on ports 80 and 443 and forwards requests to the
|
|
5
5
|
# application container.
|
|
6
6
|
#
|
|
@@ -74,13 +74,13 @@ proxy:
|
|
|
74
74
|
# Sharing one loadbalancer between several dash apps
|
|
75
75
|
#
|
|
76
76
|
# More than one dash app may point `loadbalancer:` at the same host. Each app
|
|
77
|
-
# registers its own service on the shared
|
|
77
|
+
# registers its own service on the shared dash-proxy and keeps its own
|
|
78
78
|
# deploy.yml; the load balancer multiplexes them by hostname.
|
|
79
79
|
#
|
|
80
80
|
# The rules dash enforces for that topology:
|
|
81
81
|
#
|
|
82
|
-
# * Service state survives a reboot.
|
|
83
|
-
# `
|
|
82
|
+
# * Service state survives a reboot. dash-proxy persists its services in the
|
|
83
|
+
# `dash-loadbalancer-config` volume (or `dash-proxy-config` when the
|
|
84
84
|
# loadbalancer shares a proxy host), and `dash proxy reboot` only replaces
|
|
85
85
|
# the container. Rebooting from app A does not drop app B's routes — the
|
|
86
86
|
# surviving service list is printed after the restart.
|
|
@@ -104,7 +104,7 @@ proxy:
|
|
|
104
104
|
|
|
105
105
|
# Automatic proxy reboot on deploy
|
|
106
106
|
#
|
|
107
|
-
# When `dash deploy` detects that the running
|
|
107
|
+
# When `dash deploy` detects that the running dash-proxy container was
|
|
108
108
|
# started with a different image, version or run options than the current
|
|
109
109
|
# configuration, it reboots the proxy automatically — one host at a time —
|
|
110
110
|
# before booting the app.
|
|
@@ -126,13 +126,13 @@ proxy:
|
|
|
126
126
|
|
|
127
127
|
# SSL
|
|
128
128
|
#
|
|
129
|
-
#
|
|
129
|
+
# dash-proxy can provide automatic HTTPS for your application via Let's Encrypt.
|
|
130
130
|
#
|
|
131
131
|
# This requires that we are deploying to one server and the host option is set.
|
|
132
132
|
# The host value must point to the server we are deploying to, and port 443 must be
|
|
133
133
|
# open for the Let's Encrypt challenge to succeed.
|
|
134
134
|
#
|
|
135
|
-
# If you set `ssl` to `true`, `
|
|
135
|
+
# If you set `ssl` to `true`, `dash-proxy` will stop forwarding headers to your app,
|
|
136
136
|
# unless you explicitly set `forward_headers: true`
|
|
137
137
|
#
|
|
138
138
|
# Defaults to `false`:
|
|
@@ -156,7 +156,7 @@ proxy:
|
|
|
156
156
|
# absolute http(s) URL is called directly. Answer 2xx to approve. On-demand TLS
|
|
157
157
|
# replaces the static hostname list, so it cannot be combined with
|
|
158
158
|
# `host`/`hosts`, with `certificate_pem`, or with `ssl_domains` — dash rejects
|
|
159
|
-
# those at config time, because
|
|
159
|
+
# those at config time, because dash-proxy would reject the deploy and there
|
|
160
160
|
# is no sensible winner to pick.
|
|
161
161
|
#
|
|
162
162
|
# `client_ca_pem` requires mutual TLS: clients must present a certificate
|
|
@@ -176,7 +176,7 @@ proxy:
|
|
|
176
176
|
|
|
177
177
|
# SSL redirect
|
|
178
178
|
#
|
|
179
|
-
# By default,
|
|
179
|
+
# By default, dash-proxy will redirect all HTTP requests to HTTPS when SSL is enabled.
|
|
180
180
|
# If you prefer that HTTP traffic is passed through to your application (along with
|
|
181
181
|
# HTTPS traffic), you can disable this redirect by setting `ssl_redirect: false`:
|
|
182
182
|
ssl_redirect: false
|
|
@@ -244,7 +244,7 @@ proxy:
|
|
|
244
244
|
#
|
|
245
245
|
# If you are behind a trusted proxy, you can set this to `true` to forward the headers.
|
|
246
246
|
#
|
|
247
|
-
# By default,
|
|
247
|
+
# By default, dash-proxy will not forward the headers if the `ssl` option is set to `true`, and
|
|
248
248
|
# will forward them if it is set to `false`.
|
|
249
249
|
forward_headers: true
|
|
250
250
|
|
|
@@ -278,7 +278,7 @@ proxy:
|
|
|
278
278
|
set:
|
|
279
279
|
Strict-Transport-Security: max-age=31536000
|
|
280
280
|
add:
|
|
281
|
-
X-Served-By:
|
|
281
|
+
X-Served-By: dash-proxy
|
|
282
282
|
remove:
|
|
283
283
|
- Server
|
|
284
284
|
|
|
@@ -329,7 +329,7 @@ proxy:
|
|
|
329
329
|
#
|
|
330
330
|
# Authentication tokens live in the PROXY's environment, not the app's: polls
|
|
331
331
|
# send `KAMAL_PROXY_REDIRECTS_TOKEN` as a bearer token when set, and
|
|
332
|
-
# `POST /.
|
|
332
|
+
# `POST /.dash-proxy/redirects/refresh` nudges an immediate re-poll when
|
|
333
333
|
# authenticated with `KAMAL_PROXY_REFRESH_TOKEN`. Set both via
|
|
334
334
|
# `proxy.run.options.env`, next to `KAMAL_PROXY_DOMAINS_TOKEN` — never as
|
|
335
335
|
# deploy flags, which leak into process listings and audit logs.
|
|
@@ -397,19 +397,19 @@ proxy:
|
|
|
397
397
|
password_secret: WEB_BASIC_AUTH_PASSWORD
|
|
398
398
|
#
|
|
399
399
|
# ### Notes
|
|
400
|
-
# - Requires a
|
|
400
|
+
# - Requires a dash-proxy that supports `--basic-auth`. This is newer than
|
|
401
401
|
# the current `MINIMUM_VERSION`, so make sure your proxy is up to date
|
|
402
402
|
# before enabling it — an older proxy fails the deploy on an unknown flag.
|
|
403
403
|
# - Basic credentials are replayable and are sent on every request. Use this
|
|
404
404
|
# with `ssl: true`, or terminate TLS in front of the proxy.
|
|
405
|
-
# -
|
|
405
|
+
# - dash-proxy removes the `Authorization` header before forwarding, so a
|
|
406
406
|
# service behind basic auth cannot also pass credentials through to its
|
|
407
407
|
# target. When load balancing, the credentials are enforced by the load
|
|
408
408
|
# balancer only.
|
|
409
409
|
|
|
410
410
|
# Dynamic TLS domains
|
|
411
411
|
#
|
|
412
|
-
#
|
|
412
|
+
# dash-proxy can learn TLS hostnames from your application at runtime
|
|
413
413
|
# instead of fixing them at deploy time. It polls `source` — a path
|
|
414
414
|
# (resolved against a healthy app target) or an absolute http(s) URL —
|
|
415
415
|
# for the domain list and manages Let's Encrypt certificates for it
|
|
@@ -434,21 +434,21 @@ proxy:
|
|
|
434
434
|
# Who the client is
|
|
435
435
|
#
|
|
436
436
|
# Rate limiting and IP allow lists are both only as correct as the address they
|
|
437
|
-
# key on, so configure this first if anything sits in front of
|
|
437
|
+
# key on, so configure this first if anything sits in front of dash-proxy.
|
|
438
438
|
#
|
|
439
439
|
# With no `trusted_proxies`, the client is always the address that opened the
|
|
440
440
|
# connection — nothing a client sends can influence it, which is what makes the
|
|
441
441
|
# allow list meaningful.
|
|
442
442
|
#
|
|
443
443
|
# Once you declare `trusted_proxies`, and only when the connecting address is
|
|
444
|
-
# one of them,
|
|
444
|
+
# one of them, dash-proxy reads the forwarded chain instead: it walks the
|
|
445
445
|
# chain from the nearest hop backwards past every proxy you declared, and the
|
|
446
446
|
# first address none of your proxies wrote is the client. **List every hop**,
|
|
447
|
-
# not only the one that connects to
|
|
447
|
+
# not only the one that connects to dash-proxy — a chain it cannot resolve
|
|
448
448
|
# denies the request rather than falling back to the connecting address.
|
|
449
449
|
#
|
|
450
450
|
# `header` names the header carrying the original client IP (`CF-Connecting-IP`
|
|
451
|
-
# behind Cloudflare, `True-Client-IP` behind some others);
|
|
451
|
+
# behind Cloudflare, `True-Client-IP` behind some others); dash-proxy reads it
|
|
452
452
|
# instead of `X-Forwarded-For`. It is only honoured when `trusted_proxies` is
|
|
453
453
|
# set, because otherwise it is just something the client wrote — dash rejects
|
|
454
454
|
# that combination rather than appearing to honour it.
|
|
@@ -501,7 +501,7 @@ proxy:
|
|
|
501
501
|
#
|
|
502
502
|
# Refuse requests whose full User-Agent matches one of these RE2 patterns,
|
|
503
503
|
# checked after the IP rules. A missing User-Agent only matches an explicit
|
|
504
|
-
# '^$' pattern. Patterns are matched by
|
|
504
|
+
# '^$' pattern. Patterns are matched by dash-proxy (Go RE2), so dash checks
|
|
505
505
|
# only their shape, not their syntax.
|
|
506
506
|
deny_user_agents:
|
|
507
507
|
- 'BadBot/.*'
|
|
@@ -661,7 +661,7 @@ proxy:
|
|
|
661
661
|
# This block is the *policy*, and it is per service. Where the entries live is
|
|
662
662
|
# proxy-wide and set under `run/cache` below.
|
|
663
663
|
#
|
|
664
|
-
# Only `enabled` is required. Everything else keeps
|
|
664
|
+
# Only `enabled` is required. Everything else keeps dash-proxy's own default
|
|
665
665
|
# until you set it.
|
|
666
666
|
#
|
|
667
667
|
# `max_ttl` caps the lifetime the app asks for, in seconds, so one mistaken
|
|
@@ -690,7 +690,7 @@ proxy:
|
|
|
690
690
|
# ### When it is not caching
|
|
691
691
|
#
|
|
692
692
|
# A cache that quietly stores nothing is the usual first surprise. Start with
|
|
693
|
-
# `dash proxy cache stats`;
|
|
693
|
+
# `dash proxy cache stats`; dash-proxy also explains every refusal — check
|
|
694
694
|
# `dash proxy logs` for the reason, and the `cache_refusals_total` metric
|
|
695
695
|
# (by `reason`) if you run with `metrics_port`.
|
|
696
696
|
# The common reasons are a missing `Cache-Control: public, max-age=...` on the
|
|
@@ -739,7 +739,7 @@ proxy:
|
|
|
739
739
|
|
|
740
740
|
# Read-only targets
|
|
741
741
|
#
|
|
742
|
-
#
|
|
742
|
+
# dash-proxy can split traffic between the deployed (writer) targets and a
|
|
743
743
|
# set of read-only targets, e.g. app instances backed by database replicas.
|
|
744
744
|
# Read requests are routed to the read targets; write requests always go to
|
|
745
745
|
# the writers.
|
|
@@ -771,7 +771,7 @@ proxy:
|
|
|
771
771
|
# re-pinned, so a deploy does not strand anybody. Reads served by a
|
|
772
772
|
# `read_targets` replica are never pinned.
|
|
773
773
|
#
|
|
774
|
-
# `cookie` renames the pin cookie;
|
|
774
|
+
# `cookie` renames the pin cookie; dash-proxy picks a sensible default.
|
|
775
775
|
session_affinity:
|
|
776
776
|
enabled: true
|
|
777
777
|
cookie: _kamal_affinity
|
|
@@ -827,19 +827,19 @@ proxy:
|
|
|
827
827
|
publish: false # Publish ports to the host (default: true)
|
|
828
828
|
bind_ips: # List of IPs to bind to when publishing ports
|
|
829
829
|
- 0.0.0.0
|
|
830
|
-
registry: registry:4443 # Extra registry prefix for the
|
|
830
|
+
registry: registry:4443 # Extra registry prefix for the dash-proxy image
|
|
831
831
|
# (default: none). If you set this, also override
|
|
832
832
|
# `repository` to a host-less path (e.g.
|
|
833
|
-
# myfork/
|
|
833
|
+
# myfork/dash-proxy) - the default repository
|
|
834
834
|
# below already embeds its ghcr.io host
|
|
835
835
|
repository: ghcr.io/zoolutions/dash-proxy # Container repository for the
|
|
836
|
-
#
|
|
837
|
-
version: v1.0.0
|
|
836
|
+
# dash-proxy image (this is the default)
|
|
837
|
+
version: v1.1.0.0 # Version tag of the dash-proxy image to use.
|
|
838
838
|
# Defaults to the minimum version this gem
|
|
839
839
|
# requires - only pin it to roll forward early,
|
|
840
840
|
# never below the default
|
|
841
841
|
port_holder: true # Zero-downtime proxy reboots (default: false).
|
|
842
|
-
# Runs a minimal long-lived
|
|
842
|
+
# Runs a minimal long-lived dash-proxy-net container that
|
|
843
843
|
# owns the published ports; proxy generations join its
|
|
844
844
|
# network namespace and overlap during a reboot, so config
|
|
845
845
|
# and version changes apply without dropping requests.
|
|
@@ -857,7 +857,7 @@ proxy:
|
|
|
857
857
|
# DNS-01 on their own. `auto` is an explicit opt-in that picks the provider
|
|
858
858
|
# from the credentials it can see and logs at boot which one it armed;
|
|
859
859
|
# `none` says "no DNS-01" explicitly. An unsupported name is rejected here,
|
|
860
|
-
# at config time -
|
|
860
|
+
# at config time - dash-proxy would only log a warning and then never
|
|
861
861
|
# issue a certificate.
|
|
862
862
|
#
|
|
863
863
|
# A plain string (`dns_provider: cloudflare`) uses one provider for every
|
|
@@ -866,7 +866,7 @@ proxy:
|
|
|
866
866
|
# spread across registrars. Each provider's API credentials must be present
|
|
867
867
|
# under `credentials` for its challenges to succeed.
|
|
868
868
|
#
|
|
869
|
-
#
|
|
869
|
+
# dash-proxy also accepts short aliases for the canonical names - `cf`
|
|
870
870
|
# (cloudflare), `do` (digitalocean), `gcp`/`google`/`googledns` (gcloud),
|
|
871
871
|
# `gd` (godaddy), `hz` (hetzner), `nc` (namecheap), `aws`/`r53` (route53)
|
|
872
872
|
# and `vr` (vultr). Prefer the canonical name; the aliases exist so a
|
|
@@ -1018,15 +1018,15 @@ proxy:
|
|
|
1018
1018
|
# Defaults to false:
|
|
1019
1019
|
reuse_port: true
|
|
1020
1020
|
|
|
1021
|
-
# Escape hatch for `
|
|
1021
|
+
# Escape hatch for `dash-proxy run`
|
|
1022
1022
|
#
|
|
1023
|
-
# Anything
|
|
1023
|
+
# Anything dash-proxy accepts that has no key above. Note the difference
|
|
1024
1024
|
# from `options` below, which many people expect to do this: `flags` goes to
|
|
1025
|
-
# `
|
|
1025
|
+
# `dash-proxy run`, `options` goes to `docker run`.
|
|
1026
1026
|
#
|
|
1027
1027
|
# `true` renders a bare flag; anything else renders `--flag value`. No
|
|
1028
1028
|
# translation is applied, so write Go durations and lists the way
|
|
1029
|
-
#
|
|
1029
|
+
# dash-proxy wants them.
|
|
1030
1030
|
#
|
|
1031
1031
|
# A key here that a named setting already emits is rejected rather than
|
|
1032
1032
|
# passed twice — set one or the other.
|
|
@@ -1037,7 +1037,7 @@ proxy:
|
|
|
1037
1037
|
#
|
|
1038
1038
|
# Path to the container runtime socket, which is what `proxy/sleep` needs in
|
|
1039
1039
|
# order to stop and start containers. Setting it does two things: it passes
|
|
1040
|
-
# the path to
|
|
1040
|
+
# the path to dash-proxy, and it mounts that path into the proxy container,
|
|
1041
1041
|
# since the flag alone only says where to look.
|
|
1042
1042
|
#
|
|
1043
1043
|
# **Reaching this socket is root-equivalent on the host.** It is a separate,
|
|
@@ -1073,11 +1073,11 @@ proxy:
|
|
|
1073
1073
|
store_timeout: 2
|
|
1074
1074
|
memory_size: 134_217_728
|
|
1075
1075
|
# `options` are `docker run` options for the proxy container - resource
|
|
1076
|
-
# limits, labels, extra mounts - NOT
|
|
1076
|
+
# limits, labels, extra mounts - NOT dash-proxy flags. A dash-proxy flag
|
|
1077
1077
|
# the gem has no key for goes in `flags` above instead.
|
|
1078
1078
|
options: # Additional options to pass to `docker run`
|
|
1079
1079
|
label:
|
|
1080
|
-
- custom.label=
|
|
1080
|
+
- custom.label=dash-proxy
|
|
1081
1081
|
memory: 512m
|
|
1082
1082
|
cpus: 0.5
|
|
1083
1083
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class Dash::Configuration::Loadbalancer < Dash::Configuration::Proxy
|
|
2
2
|
CONTAINER_NAME = "load-balancer".freeze
|
|
3
|
-
SHARED_CONTAINER_NAME = "
|
|
3
|
+
SHARED_CONTAINER_NAME = "dash-proxy".freeze
|
|
4
4
|
|
|
5
5
|
def self.validation_config_key
|
|
6
6
|
"proxy"
|
|
@@ -34,7 +34,7 @@ class Dash::Configuration::Loadbalancer < Dash::Configuration::Proxy
|
|
|
34
34
|
File.join config.run_directory, "loadbalancer"
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
# The load balancer is a
|
|
37
|
+
# The load balancer is a dash-proxy container, so proxy/run applies to it
|
|
38
38
|
# exactly as it does to the per-host proxies. Without a run block it still
|
|
39
39
|
# needs the default surface (published ports, log rotation, the apps-config
|
|
40
40
|
# mount and the run command) to boot from, so fall back to an empty config
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# ACME configuration for the
|
|
1
|
+
# ACME configuration for the dash-proxy container, including the DNS-01
|
|
2
2
|
# challenge credentials.
|
|
3
3
|
#
|
|
4
|
-
# Everything here except the credentials becomes a `
|
|
4
|
+
# Everything here except the credentials becomes a `dash-proxy run` flag, so it
|
|
5
5
|
# lands in the run command and therefore in Proxy::Run#config_digest — changing
|
|
6
6
|
# the block reboots the proxy on the next deploy.
|
|
7
7
|
#
|
|
@@ -9,14 +9,14 @@
|
|
|
9
9
|
# API token can rewrite your zone; `docker run --env TOKEN=...` would put it in
|
|
10
10
|
# the host's process listing and in kamal's own audit log.
|
|
11
11
|
class Dash::Configuration::Proxy::Acme
|
|
12
|
-
# The canonical provider names
|
|
12
|
+
# The canonical provider names dash-proxy MINIMUM_VERSION advertises. Kept in
|
|
13
13
|
# step with the proxy by test/proxy_flag_coverage_test.rb, which compares this
|
|
14
14
|
# list against the manifest bin/sync-proxy-flags generates from the image.
|
|
15
15
|
DNS_PROVIDERS = %w[
|
|
16
16
|
auto cloudflare digitalocean gcloud godaddy hetzner namecheap none route53 vultr
|
|
17
17
|
].freeze
|
|
18
18
|
|
|
19
|
-
# Short forms
|
|
19
|
+
# Short forms dash-proxy's ParseProviderName accepts but does not advertise,
|
|
20
20
|
# so they cannot be generated from --help and are not drift-checked.
|
|
21
21
|
DNS_PROVIDER_ALIASES = %w[ cf do gcp google googledns gd hz nc aws r53 vr ].freeze
|
|
22
22
|
|
|
@@ -70,7 +70,7 @@ class Dash::Configuration::Proxy::Acme
|
|
|
70
70
|
|
|
71
71
|
private
|
|
72
72
|
# The hash form pins zones to the DNS host that serves them; `default`
|
|
73
|
-
# covers unmatched zones.
|
|
73
|
+
# covers unmatched zones. dash-proxy takes repeatable --acme-dns-provider
|
|
74
74
|
# entries — zone=provider pairs plus at most one bare default — so the hash
|
|
75
75
|
# becomes an array and Utils.optionize repeats the flag. The string form
|
|
76
76
|
# passes through untouched and keeps meaning what it always has.
|
|
@@ -41,10 +41,8 @@ class Dash::Configuration::Proxy::Boot
|
|
|
41
41
|
"#{repository_name}/#{image_name}"
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
# Stays kamal-proxy until the server-artifact rename ships a migration
|
|
45
|
-
# bridge — every deployed host has a running container by this name.
|
|
46
44
|
def container_name
|
|
47
|
-
|
|
45
|
+
Dash::Configuration::Proxy::CONTAINER_NAME
|
|
48
46
|
end
|
|
49
47
|
|
|
50
48
|
def host_directory
|
|
@@ -72,7 +70,7 @@ class Dash::Configuration::Proxy::Boot
|
|
|
72
70
|
end
|
|
73
71
|
|
|
74
72
|
def apps_container_directory
|
|
75
|
-
"/home/
|
|
73
|
+
"/home/dash-proxy/.apps-config"
|
|
76
74
|
end
|
|
77
75
|
|
|
78
76
|
def apps_volume
|