dash 3.2.1 → 4.0.1
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 +29 -8
- 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 +117 -15
- 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
|
@@ -8,7 +8,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
8
8
|
# provides the hostnames at runtime.
|
|
9
9
|
# On-demand TLS joins loadbalancer and ssl_domains as a source of hostnames
|
|
10
10
|
# that only exist at handshake time - demanding a static host here would
|
|
11
|
-
# reject the one shape
|
|
11
|
+
# reject the one shape dash-proxy requires for it.
|
|
12
12
|
if config["host"].blank? && config["hosts"].blank? && config["ssl"] && config["loadbalancer"].blank? &&
|
|
13
13
|
config.dig("ssl_domains", "source").blank? && ssl_config["on_demand_url"].blank?
|
|
14
14
|
error "Must set a host to enable automatic SSL"
|
|
@@ -113,7 +113,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
-
# Same source shape as ssl_domains. The interval minimum is
|
|
116
|
+
# Same source shape as ssl_domains. The interval minimum is dash-proxy's
|
|
117
117
|
# own (MinRedirectsInterval, 10s) - it rejects a smaller one after the SSH
|
|
118
118
|
# round trip, so catch it here at config time.
|
|
119
119
|
def validate_redirects_source!(redirects_source)
|
|
@@ -132,7 +132,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
132
132
|
end
|
|
133
133
|
end
|
|
134
134
|
|
|
135
|
-
#
|
|
135
|
+
# dash-proxy only logs a warning for a DNS provider it does not recognise
|
|
136
136
|
# and then carries on with no provider at all, so the symptom is a
|
|
137
137
|
# certificate that never issues rather than a failed boot. Catch it here,
|
|
138
138
|
# before any host is contacted.
|
|
@@ -185,7 +185,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
185
185
|
end
|
|
186
186
|
|
|
187
187
|
# The whole TLS surface lives in the one `ssl` hash: certificate material,
|
|
188
|
-
# on-demand issuance and the mTLS client CA.
|
|
188
|
+
# on-demand issuance and the mTLS client CA. dash-proxy rejects the
|
|
189
189
|
# on-demand combinations outright rather than picking a winner
|
|
190
190
|
# (ServiceOptions.Validate), so there is no precedence to document - only
|
|
191
191
|
# a deploy that would fail after the SSH round-trip. Fail here.
|
|
@@ -231,13 +231,13 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
231
231
|
|
|
232
232
|
# An RFC 9110 token, which is both a valid header field name and a valid
|
|
233
233
|
# cookie name. For a header, a space or a colon would also break the
|
|
234
|
-
# '<name>: <value>' encoding
|
|
234
|
+
# '<name>: <value>' encoding dash-proxy cuts at the first colon.
|
|
235
235
|
TOKEN = /\A[!#$%&'*+\-.^_`|~0-9A-Za-z]+\z/
|
|
236
236
|
|
|
237
237
|
POOL_KEYS = %w[ max_conns max_idle_conns idle_conn_timeout dial_timeout try_duration try_interval ].freeze
|
|
238
238
|
|
|
239
239
|
# A zero is meaningful for most of these, so only negatives are refused.
|
|
240
|
-
#
|
|
240
|
+
# dash-proxy clamps them rather than failing when it reads saved state, and
|
|
241
241
|
# rejects them from a client — this is the client.
|
|
242
242
|
def validate_tuning!
|
|
243
243
|
target = config["target"] || {}
|
|
@@ -258,13 +258,13 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
258
258
|
with_context("request_timeout") { error "cannot be negative" }
|
|
259
259
|
end
|
|
260
260
|
|
|
261
|
-
# Sibling consistency:
|
|
261
|
+
# Sibling consistency: dash-proxy clamps a negative --target-timeout as
|
|
262
262
|
# silently as a negative --request-timeout.
|
|
263
263
|
if config["response_timeout"].to_f.negative?
|
|
264
264
|
with_context("response_timeout") { error "cannot be negative" }
|
|
265
265
|
end
|
|
266
266
|
|
|
267
|
-
# Both maps reach
|
|
267
|
+
# Both maps reach dash-proxy's one parsePathTimeouts, which refuses a
|
|
268
268
|
# negative duration.
|
|
269
269
|
%w[ path_response_timeouts path_request_timeouts ].each do |key|
|
|
270
270
|
with_context(key) do
|
|
@@ -312,7 +312,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
312
312
|
end
|
|
313
313
|
end
|
|
314
314
|
|
|
315
|
-
#
|
|
315
|
+
# dash-proxy rejects canonical_host + on-demand TLS after the SSH round
|
|
316
316
|
# trip (canonical redirection needs a fixed host, on-demand TLS has none),
|
|
317
317
|
# and a canonical host outside the host list redirects every request to a
|
|
318
318
|
# hostname this service does not serve.
|
|
@@ -393,7 +393,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
393
393
|
end
|
|
394
394
|
|
|
395
395
|
# Go carries the request host in Request.Host rather than the header map,
|
|
396
|
-
# so
|
|
396
|
+
# so dash-proxy refuses the rule instead of silently doing nothing.
|
|
397
397
|
if direction == "request" && name.to_s.downcase == "host"
|
|
398
398
|
return error "cannot rewrite the Host header"
|
|
399
399
|
end
|
|
@@ -408,7 +408,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
408
408
|
|
|
409
409
|
# The pattern is deliberately not compiled: Go's RE2 and Ruby's Onigmo differ
|
|
410
410
|
# at the edges (Ruby rejects Go's `(?P<name>...)`), and refusing a pattern
|
|
411
|
-
#
|
|
411
|
+
# dash-proxy would have accepted is worse than finding out at deploy time.
|
|
412
412
|
def validate_path_rules!(key, redirect:)
|
|
413
413
|
Array(config[key]).each_with_index do |rule, index|
|
|
414
414
|
with_context(key) do
|
|
@@ -419,7 +419,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
419
419
|
next error "needs both from and to"
|
|
420
420
|
end
|
|
421
421
|
|
|
422
|
-
# The wire format is '<from>=<to>' and
|
|
422
|
+
# The wire format is '<from>=<to>' and dash-proxy cuts at the
|
|
423
423
|
# FIRST '=', so an '=' inside the pattern silently builds a rule
|
|
424
424
|
# for a different path.
|
|
425
425
|
if rule["from"].to_s.include?("=")
|
|
@@ -443,7 +443,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
443
443
|
end
|
|
444
444
|
end
|
|
445
445
|
|
|
446
|
-
#
|
|
446
|
+
# dash-proxy's --rate-limit is a Float64, so half a request per second is a
|
|
447
447
|
# legal rate. The docs example can only demonstrate one numeric type, and an
|
|
448
448
|
# Integer there would reject 0.5 — so the shape is checked by hand instead.
|
|
449
449
|
def validate_key_override!(key, value)
|
|
@@ -488,7 +488,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
488
488
|
end
|
|
489
489
|
|
|
490
490
|
# Rate limiting and the IP allow list are only as correct as the address they
|
|
491
|
-
# key on, so
|
|
491
|
+
# key on, so dash-proxy ties the three settings together with rules that
|
|
492
492
|
# otherwise surface only once the deploy has reached a host.
|
|
493
493
|
def validate_access_control!
|
|
494
494
|
allow_ips = Array(config["allow_ips"])
|
|
@@ -531,7 +531,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
531
531
|
error "trusted_proxies has no effect without allow_ips, deny_ips or rate_limit"
|
|
532
532
|
end
|
|
533
533
|
|
|
534
|
-
# Unconditional: even without allow_ips/rate_limit,
|
|
534
|
+
# Unconditional: even without allow_ips/rate_limit, dash-proxy rewrites
|
|
535
535
|
# the client address (and X-Forwarded-For) from this header, so honoring
|
|
536
536
|
# it from untrusted peers is a client-spoofable identity.
|
|
537
537
|
if client_ip["header"].present? && trusted_proxies.empty?
|
|
@@ -549,7 +549,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
549
549
|
end
|
|
550
550
|
end
|
|
551
551
|
|
|
552
|
-
#
|
|
552
|
+
# dash-proxy serves the health check path without an address check and
|
|
553
553
|
# without a rate limit so deploys keep working, which makes '/' a hole
|
|
554
554
|
# straight through all three features.
|
|
555
555
|
if (allow_ips.any? || deny_ips.any? || rate_limited) && config.dig("healthcheck", "path") == "/"
|
|
@@ -618,7 +618,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
618
618
|
|
|
619
619
|
Array(compress["content_types"]).each do |content_type|
|
|
620
620
|
# A main-type wildcard would match everything the proxy cannot see
|
|
621
|
-
# inside, so
|
|
621
|
+
# inside, so dash-proxy allows a wildcard only in the subtype.
|
|
622
622
|
unless content_type.to_s.match?(%r{\A[^/*\s]+/[^/\s]+\z})
|
|
623
623
|
error "content_types entry '#{content_type}' must be a media type such as text/html or text/*"
|
|
624
624
|
end
|
|
@@ -632,7 +632,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
632
632
|
REFUSED_TLS_VERSIONS = %w[ 1.0 1.1 ].freeze
|
|
633
633
|
RUN_TIMEOUTS = %w[ read_header_timeout read_timeout write_timeout idle_timeout shutdown_timeout ].freeze
|
|
634
634
|
|
|
635
|
-
#
|
|
635
|
+
# dash-proxy validates these three in preRun, so a typo does not fail the
|
|
636
636
|
# deploy - it stops the proxy container from booting at all.
|
|
637
637
|
def validate_run_server_options!(run_config)
|
|
638
638
|
with_context("run") do
|
|
@@ -656,7 +656,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
656
656
|
end
|
|
657
657
|
end
|
|
658
658
|
|
|
659
|
-
# Accepts the spellings
|
|
659
|
+
# Accepts the spellings dash-proxy's normalizeTLSVersion reduces - a `tls`
|
|
660
660
|
# or `tlsv` prefix, and `_` for `.` - so a config written for upstream is not
|
|
661
661
|
# rejected here and accepted there.
|
|
662
662
|
def validate_min_tls!(value)
|
|
@@ -673,7 +673,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
673
673
|
end
|
|
674
674
|
end
|
|
675
675
|
|
|
676
|
-
#
|
|
676
|
+
# dash-proxy reads the cache policy only when --cache is set and ignores it
|
|
677
677
|
# otherwise, so a tuned block with no `enabled` is a cache that silently
|
|
678
678
|
# never caches - this issue's own predicted first support question.
|
|
679
679
|
def validate_cache!(cache)
|
|
@@ -682,12 +682,12 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
682
682
|
with_context("cache") do
|
|
683
683
|
if (orphaned = cache.keys - [ "enabled" ]).any?
|
|
684
684
|
error "#{orphaned.first} has no effect without enabled: true - " \
|
|
685
|
-
"
|
|
685
|
+
"dash-proxy ignores the cache policy entirely when --cache is absent"
|
|
686
686
|
end
|
|
687
687
|
end
|
|
688
688
|
end
|
|
689
689
|
|
|
690
|
-
# An unsupported store is rejected in
|
|
690
|
+
# An unsupported store is rejected in dash-proxy's preRun, which means the
|
|
691
691
|
# proxy container exits at boot rather than a deploy failing. Catch it before
|
|
692
692
|
# the fleet loses its proxy.
|
|
693
693
|
def validate_cache_store!(cache)
|
|
@@ -708,7 +708,7 @@ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
|
|
|
708
708
|
if basic_auth["username"].blank?
|
|
709
709
|
error "Missing username setting (required when basic_auth is set)"
|
|
710
710
|
elsif basic_auth["username"].to_s.include?(":")
|
|
711
|
-
#
|
|
711
|
+
# dash-proxy cuts <username>:<password> at the first colon, so a
|
|
712
712
|
# colon in the username would silently truncate it.
|
|
713
713
|
error "Invalid username: cannot contain a colon"
|
|
714
714
|
end
|
data/lib/dash/configuration.rb
CHANGED
|
@@ -478,7 +478,7 @@ class Dash::Configuration
|
|
|
478
478
|
return true if offenders.empty?
|
|
479
479
|
|
|
480
480
|
warn "Host(s) #{offenders.sort.join(", ")}: proxy_protocol is enabled without proxy_protocol_allow_ips, " \
|
|
481
|
-
"so
|
|
481
|
+
"so dash-proxy honours a PROXY header from any peer that can reach the port. That header sets the client " \
|
|
482
482
|
"address `allow_ips`, `rate_limit` and the access log all use. Name the load balancers you actually run."
|
|
483
483
|
|
|
484
484
|
true
|
|
@@ -503,11 +503,11 @@ class Dash::Configuration
|
|
|
503
503
|
return true if offenders.empty?
|
|
504
504
|
|
|
505
505
|
raise Dash::ConfigurationError, "Role(s) #{offenders.map(&:name).join(", ")}: " \
|
|
506
|
-
"proxy/sleep requires proxy/run/docker_socket -
|
|
506
|
+
"proxy/sleep requires proxy/run/docker_socket - dash-proxy can only stop and start containers " \
|
|
507
507
|
"through the container runtime socket, and it is not mounted into the proxy without it"
|
|
508
508
|
end
|
|
509
509
|
|
|
510
|
-
#
|
|
510
|
+
# dash-proxy resolves a zero max_idle_conns to its default of 100
|
|
511
511
|
# (target_pool.go resolves defaults from zeros), so the one value an
|
|
512
512
|
# operator writes to mean "keep none" is the one value that cannot mean it.
|
|
513
513
|
# Legal, so warn rather than raise.
|
|
@@ -518,14 +518,14 @@ class Dash::Configuration
|
|
|
518
518
|
return true if offenders.empty?
|
|
519
519
|
|
|
520
520
|
warn "Role(s) #{offenders.map(&:name).join(", ")}: target/max_idle_conns: 0 means the proxy default of 100, " \
|
|
521
|
-
"not \"keep none\" -
|
|
521
|
+
"not \"keep none\" - dash-proxy resolves its defaults from zeros. Leave it unset for the default, " \
|
|
522
522
|
"or set 1 for the practical minimum."
|
|
523
523
|
|
|
524
524
|
true
|
|
525
525
|
end
|
|
526
526
|
|
|
527
527
|
# `intercept_errors` discards the app's error body and renders the proxy's own
|
|
528
|
-
# page instead. With no pages to render,
|
|
528
|
+
# page instead. With no pages to render, dash-proxy falls back to a bare
|
|
529
529
|
# plaintext status line — so the app's error page is thrown away and replaced
|
|
530
530
|
# by the words "Bad Gateway". Legal, and almost never the intent.
|
|
531
531
|
def ensure_intercepted_errors_have_pages
|
|
@@ -535,7 +535,7 @@ class Dash::Configuration
|
|
|
535
535
|
return true if offenders.empty?
|
|
536
536
|
|
|
537
537
|
warn "Role(s) #{offenders.map(&:name).join(", ")}: intercept_errors is set but no error_pages_path is " \
|
|
538
|
-
"configured, so
|
|
538
|
+
"configured, so dash-proxy replaces the app's error page with a bare plaintext status line. " \
|
|
539
539
|
"Set `error_pages_path:` to serve your own pages, or remove `intercept_errors` to let the app's through."
|
|
540
540
|
|
|
541
541
|
true
|
|
@@ -543,7 +543,7 @@ class Dash::Configuration
|
|
|
543
543
|
|
|
544
544
|
# Rate limiting and IP deny rules are only as correct as the address they key
|
|
545
545
|
# on. `forward_headers: true` says something sits in front of the proxy, and
|
|
546
|
-
# without `trusted_proxies`
|
|
546
|
+
# without `trusted_proxies` dash-proxy keys on that something's address
|
|
547
547
|
# rather than the client's — so the limiter throttles the whole world as one
|
|
548
548
|
# client (or nobody), and a deny list denies nobody it was written for. Warn
|
|
549
549
|
# rather than raise: the config is legal, just almost certainly not what was
|
|
@@ -568,7 +568,7 @@ class Dash::Configuration
|
|
|
568
568
|
|
|
569
569
|
offenders.each do |role_name, features|
|
|
570
570
|
warn "Role #{role_name}: #{features.join(" and ")} is set with forward_headers, " \
|
|
571
|
-
"but no proxy/client_ip/trusted_proxies.
|
|
571
|
+
"but no proxy/client_ip/trusted_proxies. dash-proxy will key on the address of " \
|
|
572
572
|
"whatever sits in front of it, not on the client's — so the rules apply to every " \
|
|
573
573
|
"visitor as one client, or to none of them. Declare the proxies in front with " \
|
|
574
574
|
"`client_ip: trusted_proxies:`."
|
data/lib/dash/utils.rb
CHANGED
|
@@ -45,7 +45,7 @@ module Dash::Utils
|
|
|
45
45
|
options.flatten.compact
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# dash-proxy takes durations as Go duration strings; deploy.yml takes plain
|
|
49
49
|
# seconds. Zero is a real value (it disables the timeout), so only nil drops out.
|
|
50
50
|
#
|
|
51
51
|
# A value that already carries a unit is passed through untouched. Appending
|
data/lib/dash/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dash
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -203,6 +203,7 @@ files:
|
|
|
203
203
|
- lib/dash/cli/main/migrate.rb
|
|
204
204
|
- lib/dash/cli/proxy.rb
|
|
205
205
|
- lib/dash/cli/proxy/drift.rb
|
|
206
|
+
- lib/dash/cli/proxy/legacy_rename.rb
|
|
206
207
|
- lib/dash/cli/proxy/loadbalancer_claim.rb
|
|
207
208
|
- lib/dash/cli/proxy/loadbalancer_reboot.rb
|
|
208
209
|
- lib/dash/cli/proxy/reboot.rb
|