From c074dfc56e3026f9b05bb79219cfdd949155e194 Mon Sep 17 00:00:00 2001 From: Dmytro Shteflyuk Date: Tue, 24 Sep 2024 14:39:52 -0400 Subject: [PATCH] Added support for custom SSL certificate --- lib/kamal/configuration/docs/proxy.yml | 16 ++++++++++++++++ lib/kamal/configuration/proxy.rb | 2 ++ lib/kamal/configuration/validator/proxy.rb | 8 ++++++++ test/commands/app_test.rb | 8 ++++++++ test/configuration/proxy_test.rb | 10 ++++++++++ 5 files changed, 44 insertions(+) diff --git a/lib/kamal/configuration/docs/proxy.yml b/lib/kamal/configuration/docs/proxy.yml index 04eea4fc..0a7a0056 100644 --- a/lib/kamal/configuration/docs/proxy.yml +++ b/lib/kamal/configuration/docs/proxy.yml @@ -98,3 +98,19 @@ proxy: # By default kamal-proxy will not forward the headers the ssl option is set to true, and # will forward them if it is set to false. forward_headers: true + + # SSL certificate path + # + # The path to the custom SSL certificate for the host when not using Let's Encrypt. + # The certificate must be in PEM format and contain the full chain. + # + # SSL private key path must also be set. + ssl_certificate_path: /data/cert/foo.example.com/fullchain.pem + + # SSL private key path + # + # The path to the custom SSL private key for the host when not using Let's Encrypt. + # The key must be in PEM format. + # + # SSL certificate path must also be set. + ssl_private_key_path: /data/cert/foo.example.com/privkey.pem diff --git a/lib/kamal/configuration/proxy.rb b/lib/kamal/configuration/proxy.rb index af09e2c7..9b83f6c7 100644 --- a/lib/kamal/configuration/proxy.rb +++ b/lib/kamal/configuration/proxy.rb @@ -30,6 +30,8 @@ def deploy_options { host: proxy_config["host"], tls: proxy_config["ssl"], + "tls-certificate-path": proxy_config["ssl_certificate_path"], + "tls-private-key-path": proxy_config["ssl_private_key_path"], "deploy-timeout": seconds_duration(config.deploy_timeout), "drain-timeout": seconds_duration(config.drain_timeout), "health-check-interval": seconds_duration(proxy_config.dig("healthcheck", "interval")), diff --git a/lib/kamal/configuration/validator/proxy.rb b/lib/kamal/configuration/validator/proxy.rb index bf2e5e9e..0201a241 100644 --- a/lib/kamal/configuration/validator/proxy.rb +++ b/lib/kamal/configuration/validator/proxy.rb @@ -6,6 +6,14 @@ def validate! if config["host"].blank? && config["ssl"] error "Must set a host to enable automatic SSL" end + + if config["ssl_certificate_path"].present? && config["ssl_private_key_path"].blank? + error "Must set a private key path to use a custom SSL certificate" + end + + if config["ssl_private_key_path"].present? && config["ssl_certificate_path"].blank? + error "Must set a certificate path to use a custom SSL private key" + end end end end diff --git a/test/commands/app_test.rb b/test/commands/app_test.rb index 6704adb6..637009e7 100644 --- a/test/commands/app_test.rb +++ b/test/commands/app_test.rb @@ -119,6 +119,14 @@ class CommandsAppTest < ActiveSupport::TestCase new_command.deploy(target: "172.1.0.2").join(" ") end + test "deploy with custom SSL certificate" do + @config[:proxy] = { "ssl" => true, "host" => "example.com", "ssl_certificate_path" => "/path/to/cert.pem", "ssl_private_key_path" => "/path/to/key.pem" } + + assert_equal \ + "docker exec kamal-proxy kamal-proxy deploy app-web --target \"172.1.0.2:80\" --host \"example.com\" --tls --tls-certificate-path \"/path/to/cert.pem\" --tls-private-key-path \"/path/to/key.pem\" --deploy-timeout \"30s\" --drain-timeout \"30s\" --buffer-requests --buffer-responses --log-request-header \"Cache-Control\" --log-request-header \"Last-Modified\" --log-request-header \"User-Agent\"", + new_command.deploy(target: "172.1.0.2").join(" ") + end + test "remove" do assert_equal \ "docker exec kamal-proxy kamal-proxy remove app-web --target \"172.1.0.2:80\"", diff --git a/test/configuration/proxy_test.rb b/test/configuration/proxy_test.rb index 3aa3f85e..4685b756 100644 --- a/test/configuration/proxy_test.rb +++ b/test/configuration/proxy_test.rb @@ -18,6 +18,16 @@ class ConfigurationEnvTest < ActiveSupport::TestCase assert_raises(Kamal::ConfigurationError) { config.proxy.ssl? } end + test "ssl with certificate path and no private key path" do + @deploy[:proxy] = { "ssl" => true, "ssl_certificate_path" => "/path/to/cert.pem" } + assert_raises(Kamal::ConfigurationError) { config.proxy.ssl? } + end + + test "ssl with private key path and no certificate path" do + @deploy[:proxy] = { "ssl" => true, "ssl_private_key_path" => "/path/to/key.pem" } + assert_raises(Kamal::ConfigurationError) { config.proxy.ssl? } + end + private def config Kamal::Configuration.new(@deploy)