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`
#
# Defaults to `false`:
ssl: true
ssl: ...
# 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
# 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
#

View File

@@ -28,15 +28,21 @@ class Kamal::Configuration::Proxy
end
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
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
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
def certificate_pem
@@ -54,7 +60,7 @@ class Kamal::Configuration::Proxy
def deploy_options
{
host: hosts,
tls: proxy_config["ssl"].presence,
tls: ssl? ? true : nil,
"tls-certificate-path": certificate_pem,
"tls-private-key-path": private_key_pem,
"deploy-timeout": seconds_duration(config.deploy_timeout),

View File

@@ -24,7 +24,9 @@ class Kamal::Configuration::Validator
example_value = example[key]
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
end
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"
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)"
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)"
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
with_test_secrets("secrets" => "CERT_PEM=certificate\nKEY_PEM=private_key") do
@deploy[:proxy] = {
"ssl" => true,
"host" => "example.com",
"ssl" => {
"certificate_pem" => "CERT_PEM",
"private_key_pem" => "KEY_PEM"
},
"host" => "example.com"
}
proxy = config.proxy
@@ -60,12 +61,31 @@ class ConfigurationProxyTest < ActiveSupport::TestCase
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
with_test_secrets("secrets" => "CERT_PEM=certificate") do
@deploy[:proxy] = {
"ssl" => true,
"host" => "example.com",
"ssl" => {
"certificate_pem" => "CERT_PEM"
},
"host" => "example.com"
}
assert_raises(Kamal::ConfigurationError) { config.proxy.ssl? }
end
@@ -74,9 +94,10 @@ class ConfigurationProxyTest < ActiveSupport::TestCase
test "ssl with private key and no certificate" do
with_test_secrets("secrets" => "KEY_PEM=private_key") do
@deploy[:proxy] = {
"ssl" => true,
"host" => "example.com",
"ssl" => {
"private_key_pem" => "KEY_PEM"
},
"host" => "example.com"
}
assert_raises(Kamal::ConfigurationError) { config.proxy.ssl? }
end