Add validations for host/ssl roles

Roles with SSL can only have one server.
Two roles with SSL can't use the same host.
This commit is contained in:
Donal McBreen
2024-09-18 17:42:45 +01:00
parent fd0cdc1ca1
commit 63f854ea18
6 changed files with 64 additions and 3 deletions

View File

@@ -75,6 +75,8 @@ class Kamal::Configuration
ensure_retain_containers_valid ensure_retain_containers_valid
ensure_valid_service_name ensure_valid_service_name
ensure_no_traefik_reboot_hooks ensure_no_traefik_reboot_hooks
ensure_one_host_for_ssl_roles
ensure_unique_hosts_for_ssl_roles
end end
@@ -349,6 +351,20 @@ class Kamal::Configuration
true true
end end
def ensure_one_host_for_ssl_roles
roles.each(&:ensure_one_host_for_ssl)
true
end
def ensure_unique_hosts_for_ssl_roles
hosts = roles.select(&:ssl?).map { |role| role.proxy.host }
duplicates = hosts.tally.filter_map { |host, count| host if count > 1 }
raise Kamal::ConfigurationError, "Different roles can't share the same host for SSL: #{duplicates.join(", ")}" if duplicates.any?
true
end
def role_names def role_names
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort

View File

@@ -22,6 +22,10 @@ class Kamal::Configuration::Proxy
proxy_config.fetch("ssl", false) proxy_config.fetch("ssl", false)
end end
def host
proxy_config["host"]
end
def deploy_options def deploy_options
{ {
host: proxy_config["host"], host: proxy_config["host"],

View File

@@ -75,6 +75,10 @@ class Kamal::Configuration::Role
@running_proxy @running_proxy
end end
def ssl?
running_proxy? && proxy.ssl?
end
def stop_args def stop_args
# When deploying with the proxy, kamal-proxy will drain request before returning so we don't need to wait. # When deploying with the proxy, kamal-proxy will drain request before returning so we don't need to wait.
timeout = running_proxy? ? nil : config.drain_timeout timeout = running_proxy? ? nil : config.drain_timeout
@@ -145,6 +149,12 @@ class Kamal::Configuration::Role
File.join config.assets_directory, "volumes", [ name, version ].join("-") File.join config.assets_directory, "volumes", [ name, version ].join("-")
end end
def ensure_one_host_for_ssl
if running_proxy? && proxy.ssl? && hosts.size > 1
raise Kamal::ConfigurationError, "SSL is only supported on a single server, found #{hosts.size} servers for role #{name}"
end
end
private private
def initialize_specialized_proxy def initialize_specialized_proxy
proxy_specializations = specializations["proxy"] proxy_specializations = specializations["proxy"]

View File

@@ -346,4 +346,35 @@ class ConfigurationTest < ActiveSupport::TestCase
end end
end end
end end
test "proxy ssl roles with no host" do
@deploy_with_roles[:servers]["workers"]["proxy"] = { "ssl" => true }
exception = assert_raises(Kamal::ConfigurationError) do
Kamal::Configuration.new(@deploy_with_roles)
end
assert_equal "servers/workers/proxy: Must set a host to enable automatic SSL", exception.message
end
test "proxy ssl roles with multiple servers" do
@deploy_with_roles[:servers]["workers"]["proxy"] = { "ssl" => true, "host" => "foo.example.com" }
exception = assert_raises(Kamal::ConfigurationError) do
Kamal::Configuration.new(@deploy_with_roles)
end
assert_equal "SSL is only supported on a single server, found 2 servers for role workers", exception.message
end
test "two proxy ssl roles with same host" do
@deploy_with_roles[:servers]["web"] = { "hosts" => [ "1.1.1.1" ], "proxy" => { "ssl" => true, "host" => "foo.example.com" } }
@deploy_with_roles[:servers]["workers"] = { "hosts" => [ "1.1.1.1" ], "proxy" => { "ssl" => true, "host" => "foo.example.com" } }
exception = assert_raises(Kamal::ConfigurationError) do
Kamal::Configuration.new(@deploy_with_roles)
end
assert_equal "Different roles can't share the same host for SSL: foo.example.com", exception.message
end
end end

View File

@@ -28,7 +28,7 @@ class MainTest < IntegrationTest
assert_match /Proxy Host: vm2/, details assert_match /Proxy Host: vm2/, details
assert_match /App Host: vm1/, details assert_match /App Host: vm1/, details
assert_match /App Host: vm2/, details assert_match /App Host: vm2/, details
assert_match /basecamp\/kamal-proxy:#{Kamal::Configuration::Proxy::MINIMUM_VERSION}/, details assert_match /basecamp\/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION}/, details
assert_match /registry:4443\/app:#{first_version}/, details assert_match /registry:4443\/app:#{first_version}/, details
audit = kamal :audit, capture: true audit = kamal :audit, capture: true

View File

@@ -53,11 +53,11 @@ class ProxyTest < IntegrationTest
private private
def assert_proxy_running def assert_proxy_running
assert_match /basecamp\/kamal-proxy:#{Kamal::Configuration::Proxy::MINIMUM_VERSION} \"kamal-proxy run\"/, proxy_details assert_match /basecamp\/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION} \"kamal-proxy run\"/, proxy_details
end end
def assert_proxy_not_running def assert_proxy_not_running
assert_no_match /basecamp\/kamal-proxy:#{Kamal::Configuration::Proxy::MINIMUM_VERSION} \"kamal-proxy run\"/, proxy_details assert_no_match /basecamp\/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION} \"kamal-proxy run\"/, proxy_details
end end
def proxy_details def proxy_details