allow defining certificates directly within ssl hash instead of at the proxy root level

This commit is contained in:
acidtib
2025-04-28 00:34:24 -06:00
parent 045410368d
commit a525d45b4d
5 changed files with 59 additions and 23 deletions

View File

@@ -49,7 +49,7 @@ proxy:
# unless you explicitly set `forward_headers: true` # unless you explicitly set `forward_headers: true`
# #
# Defaults to `false`: # Defaults to `false`:
ssl: true ssl: ...
# Custom SSL certificate # Custom SSL certificate
# #
@@ -59,8 +59,13 @@ proxy:
# #
# A reference to a secret (in this case, `CERTIFICATE_PEM` and `PRIVATE_KEY_PEM`) will look up the secret # A reference to a secret (in this case, `CERTIFICATE_PEM` and `PRIVATE_KEY_PEM`) will look up the secret
# in the local environment: # in the local environment:
certificate_pem: CERTIFICATE_PEM #
private_key_pem: PRIVATE_KEY_PEM # Examples:
# ssl: true # Enable SSL with Let's Encrypt
# ssl: false # Disable SSL
# ssl: # Enable custom SSL
# certificate_pem: CERTIFICATE_PEM
# private_key_pem: PRIVATE_KEY_PEM
# SSL redirect # SSL redirect
# #

View File

@@ -28,15 +28,21 @@ class Kamal::Configuration::Proxy
end end
def custom_ssl_certificate? def custom_ssl_certificate?
proxy_config["certificate_pem"].present? && proxy_config["private_key_pem"].present? ssl = proxy_config["ssl"]
return false unless ssl.is_a?(Hash)
ssl["certificate_pem"].present? && ssl["private_key_pem"].present?
end end
def certificate_pem_content def certificate_pem_content
secrets[proxy_config["certificate_pem"]] ssl = proxy_config["ssl"]
return nil unless ssl.is_a?(Hash)
secrets[ssl["certificate_pem"]]
end end
def private_key_pem_content def private_key_pem_content
secrets[proxy_config["private_key_pem"]] ssl = proxy_config["ssl"]
return nil unless ssl.is_a?(Hash)
secrets[ssl["private_key_pem"]]
end end
def certificate_pem def certificate_pem
@@ -54,7 +60,7 @@ class Kamal::Configuration::Proxy
def deploy_options def deploy_options
{ {
host: hosts, host: hosts,
tls: proxy_config["ssl"].presence, tls: ssl? ? true : nil,
"tls-certificate-path": certificate_pem, "tls-certificate-path": certificate_pem,
"tls-private-key-path": private_key_pem, "tls-private-key-path": private_key_pem,
"deploy-timeout": seconds_duration(config.deploy_timeout), "deploy-timeout": seconds_duration(config.deploy_timeout),

View File

@@ -24,7 +24,9 @@ class Kamal::Configuration::Validator
example_value = example[key] example_value = example[key]
if example_value == "..." if example_value == "..."
unless key.to_s == "proxy" && boolean?(value.class) if key.to_s == "ssl"
validate_type! value, TrueClass, FalseClass, Hash
elsif key.to_s != "proxy" || !boolean?(value.class)
validate_type! value, *(Array if key == :servers), Hash validate_type! value, *(Array if key == :servers), Hash
end end
elsif key == "hosts" elsif key == "hosts"

View File

@@ -11,13 +11,15 @@ class Kamal::Configuration::Validator::Proxy < Kamal::Configuration::Validator
error "Specify one of 'host' or 'hosts', not both" error "Specify one of 'host' or 'hosts', not both"
end end
if config["certificate_pem"].present? && config["private_key_pem"].blank? if config["ssl"].is_a?(Hash)
if config["ssl"]["certificate_pem"].present? && config["ssl"]["private_key_pem"].blank?
error "Missing private_key_pem setting (required when certificate_pem is present)" error "Missing private_key_pem setting (required when certificate_pem is present)"
end end
if config["private_key_pem"].present? && config["certificate_pem"].blank? if config["ssl"]["private_key_pem"].present? && config["ssl"]["certificate_pem"].blank?
error "Missing certificate_pem setting (required when private_key_pem is present)" error "Missing certificate_pem setting (required when private_key_pem is present)"
end end
end end
end end
end
end end

View File

@@ -48,10 +48,11 @@ class ConfigurationProxyTest < ActiveSupport::TestCase
test "ssl with certificate and private key from secrets" do test "ssl with certificate and private key from secrets" do
with_test_secrets("secrets" => "CERT_PEM=certificate\nKEY_PEM=private_key") do with_test_secrets("secrets" => "CERT_PEM=certificate\nKEY_PEM=private_key") do
@deploy[:proxy] = { @deploy[:proxy] = {
"ssl" => true, "ssl" => {
"host" => "example.com",
"certificate_pem" => "CERT_PEM", "certificate_pem" => "CERT_PEM",
"private_key_pem" => "KEY_PEM" "private_key_pem" => "KEY_PEM"
},
"host" => "example.com"
} }
proxy = config.proxy proxy = config.proxy
@@ -60,12 +61,31 @@ class ConfigurationProxyTest < ActiveSupport::TestCase
end end
end end
test "deploy options with custom ssl certificates" do
with_test_secrets("secrets" => "CERT_PEM=certificate\nKEY_PEM=private_key") do
@deploy[:proxy] = {
"ssl" => {
"certificate_pem" => "CERT_PEM",
"private_key_pem" => "KEY_PEM"
},
"host" => "example.com"
}
proxy = config.proxy
options = proxy.deploy_options
assert_equal true, options[:tls]
assert_equal "/home/kamal-proxy/.apps-config/app/tls/cert.pem", options[:"tls-certificate-path"]
assert_equal "/home/kamal-proxy/.apps-config/app/tls/key.pem", options[:"tls-private-key-path"]
end
end
test "ssl with certificate and no private key" do test "ssl with certificate and no private key" do
with_test_secrets("secrets" => "CERT_PEM=certificate") do with_test_secrets("secrets" => "CERT_PEM=certificate") do
@deploy[:proxy] = { @deploy[:proxy] = {
"ssl" => true, "ssl" => {
"host" => "example.com",
"certificate_pem" => "CERT_PEM" "certificate_pem" => "CERT_PEM"
},
"host" => "example.com"
} }
assert_raises(Kamal::ConfigurationError) { config.proxy.ssl? } assert_raises(Kamal::ConfigurationError) { config.proxy.ssl? }
end end
@@ -74,9 +94,10 @@ class ConfigurationProxyTest < ActiveSupport::TestCase
test "ssl with private key and no certificate" do test "ssl with private key and no certificate" do
with_test_secrets("secrets" => "KEY_PEM=private_key") do with_test_secrets("secrets" => "KEY_PEM=private_key") do
@deploy[:proxy] = { @deploy[:proxy] = {
"ssl" => true, "ssl" => {
"host" => "example.com",
"private_key_pem" => "KEY_PEM" "private_key_pem" => "KEY_PEM"
},
"host" => "example.com"
} }
assert_raises(Kamal::ConfigurationError) { config.proxy.ssl? } assert_raises(Kamal::ConfigurationError) { config.proxy.ssl? }
end end