Custom certs per role

- Upload the cert with `sshkit.upload!`
- Use the role name to create a directory for each role's certs
- Add an integration test for the custom certs
This commit is contained in:
Donal McBreen
2025-06-16 17:01:27 +01:00
parent 8a7260d1e9
commit ccbcbbc8c5
16 changed files with 185 additions and 33 deletions

View File

@@ -1,6 +1,6 @@
class Kamal::Cli::App::SslCertificates
attr_reader :host, :role, :sshkit
delegate :execute, :info, to: :sshkit
delegate :execute, :info, :upload!, to: :sshkit
def initialize(host, role, sshkit)
@host = host
@@ -13,10 +13,10 @@ class Kamal::Cli::App::SslCertificates
info "Writing SSL certificates for #{role.name} on #{host}"
execute *app.create_ssl_directory
if cert_content = role.proxy.certificate_pem_content
execute *app.write_certificate_file(cert_content)
upload!(StringIO.new(cert_content), role.proxy.host_tls_cert, mode: "0644")
end
if key_content = role.proxy.private_key_pem_content
execute *app.write_private_key_file(key_content)
upload!(StringIO.new(key_content), role.proxy.host_tls_key, mode: "0644")
end
end
end

View File

@@ -22,15 +22,7 @@ module Kamal::Commands::App::Proxy
end
def create_ssl_directory
make_directory(config.proxy_boot.tls_directory)
end
def write_certificate_file(content)
[ :sh, "-c", Kamal::Utils.sensitive("cat > #{config.proxy_boot.tls_directory}/cert.pem << 'KAMAL_CERT_EOF'\n#{content}\nKAMAL_CERT_EOF", redaction: "cat > #{config.proxy_boot.tls_directory}/cert.pem << 'KAMAL_CERT_EOF'\n[CERTIFICATE CONTENT REDACTED]\nKAMAL_CERT_EOF") ]
end
def write_private_key_file(content)
[ :sh, "-c", Kamal::Utils.sensitive("cat > #{config.proxy_boot.tls_directory}/key.pem << 'KAMAL_KEY_EOF'\n#{content}\nKAMAL_KEY_EOF", redaction: "cat > #{config.proxy_boot.tls_directory}/key.pem << 'KAMAL_KEY_EOF'\n[PRIVATE KEY CONTENT REDACTED]\nKAMAL_KEY_EOF") ]
make_directory(File.join(config.proxy_boot.tls_directory, role.name))
end
private

View File

@@ -6,12 +6,13 @@ class Kamal::Configuration::Proxy
delegate :argumentize, :optionize, to: Kamal::Utils
attr_reader :config, :proxy_config, :secrets
attr_reader :config, :proxy_config, :role_name, :secrets
def initialize(config:, proxy_config:, secrets:, context: "proxy")
def initialize(config:, proxy_config:, role_name: nil, secrets:, context: "proxy")
@config = config
@proxy_config = proxy_config
@proxy_config = {} if @proxy_config.nil?
@role_name = role_name
@secrets = secrets
validate! @proxy_config, with: Kamal::Configuration::Validator::Proxy, context: context
end
@@ -46,24 +47,28 @@ class Kamal::Configuration::Proxy
secrets[ssl["private_key_pem"]]
end
def certificate_pem
tls_file_path("cert.pem")
def host_tls_cert
tls_path(config.proxy_boot.tls_directory, "cert.pem")
end
def private_key_pem
tls_file_path("key.pem")
def host_tls_key
tls_path(config.proxy_boot.tls_directory, "key.pem")
end
def tls_file_path(filename)
File.join(config.proxy_boot.tls_container_directory, filename) if custom_ssl_certificate?
def container_tls_cert
tls_path(config.proxy_boot.tls_container_directory, "cert.pem")
end
def container_tls_key
tls_path(config.proxy_boot.tls_container_directory, "key.pem") if custom_ssl_certificate?
end
def deploy_options
{
host: hosts,
tls: ssl? ? true : nil,
"tls-certificate-path": certificate_pem,
"tls-private-key-path": private_key_pem,
"tls-certificate-path": container_tls_cert,
"tls-private-key-path": container_tls_key,
"deploy-timeout": seconds_duration(config.deploy_timeout),
"drain-timeout": seconds_duration(config.drain_timeout),
"health-check-interval": seconds_duration(proxy_config.dig("healthcheck", "interval")),
@@ -101,10 +106,14 @@ class Kamal::Configuration::Proxy
end
def merge(other)
self.class.new config: config, proxy_config: proxy_config.deep_merge(other.proxy_config), secrets: secrets
self.class.new config: config, proxy_config: other.proxy_config.deep_merge(proxy_config), role_name: role_name, secrets: secrets
end
private
def tls_path(directory, filename)
File.join([ directory, role_name, filename ].compact) if custom_ssl_certificate?
end
def seconds_duration(value)
value ? "#{value}s" : nil
end

View File

@@ -68,7 +68,7 @@ class Kamal::Configuration::Role
end
def proxy
@proxy ||= config.proxy.merge(specialized_proxy) if running_proxy?
@proxy ||= specialized_proxy.merge(config.proxy) if running_proxy?
end
def running_proxy?
@@ -174,6 +174,7 @@ class Kamal::Configuration::Role
config: config,
proxy_config: proxy_config,
secrets: config.secrets,
role_name: name,
context: "servers/#{name}/proxy"
end
end