From 35117969cb2750fb955d8e5de5a98a86712f06cd Mon Sep 17 00:00:00 2001
From: Stan Hu <stanhu@gmail.com>
Date: Wed, 31 May 2023 10:32:06 -0700
Subject: [PATCH] Support Puma key_password_command for SSL key decryption

This commit adds support for `puma['ssl_key_password_command']` to
make it possible to store encrypted SSL keys on disk.

This requires Puma v6.3.0 (https://github.com/puma/puma/pull/3133) to
work: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122200.

Relates to https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/7799

Changelog: added
---
 files/gitlab-config-template/gitlab.rb.template  |  1 +
 .../gitlab/attributes/default.rb                 |  1 +
 files/gitlab-cookbooks/gitlab/recipes/puma.rb    |  2 ++
 .../gitlab/resources/puma_config.rb              |  1 +
 .../gitlab/templates/default/puma.rb.erb         |  3 +++
 spec/chef/cookbooks/gitlab/recipes/puma_spec.rb  | 16 +++++++++++++---
 6 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/files/gitlab-config-template/gitlab.rb.template b/files/gitlab-config-template/gitlab.rb.template
index 58e8b1ed35..a6f18e3659 100644
--- a/files/gitlab-config-template/gitlab.rb.template
+++ b/files/gitlab-config-template/gitlab.rb.template
@@ -1083,6 +1083,7 @@ external_url 'GENERATED_EXTERNAL_URL'
 # puma['ssl_certificate_key'] = nil
 # puma['ssl_client_certificate'] = nil
 # puma['ssl_cipher_filter'] = nil
+# puma['ssl_key_password_command'] = nil
 # puma['ssl_verify_mode'] = 'none'
 
 # puma['pidfile'] = '/opt/gitlab/var/puma/puma.pid'
diff --git a/files/gitlab-cookbooks/gitlab/attributes/default.rb b/files/gitlab-cookbooks/gitlab/attributes/default.rb
index 719a7192a2..832d27a47f 100644
--- a/files/gitlab-cookbooks/gitlab/attributes/default.rb
+++ b/files/gitlab-cookbooks/gitlab/attributes/default.rb
@@ -569,6 +569,7 @@ default['gitlab']['puma']['ssl_certificate'] = nil
 default['gitlab']['puma']['ssl_certificate_key'] = nil
 default['gitlab']['puma']['ssl_client_certificate'] = nil
 default['gitlab']['puma']['ssl_cipher_filter'] = nil
+default['gitlab']['puma']['ssl_key_password_command'] = nil
 default['gitlab']['puma']['ssl_verify_mode'] = 'none'
 default['gitlab']['puma']['prometheus_scrape_scheme'] = 'http'
 default['gitlab']['puma']['prometheus_scrape_tls_server_name'] = nil
diff --git a/files/gitlab-cookbooks/gitlab/recipes/puma.rb b/files/gitlab-cookbooks/gitlab/recipes/puma.rb
index f2daefc987..f60b9abb17 100644
--- a/files/gitlab-cookbooks/gitlab/recipes/puma.rb
+++ b/files/gitlab-cookbooks/gitlab/recipes/puma.rb
@@ -38,6 +38,7 @@ puma_listen_ssl_host = node['gitlab'][svc]['ssl_listen']
 puma_ssl_port = node['gitlab'][svc]['ssl_port']
 puma_ssl_cert = node['gitlab'][svc]['ssl_certificate']
 puma_ssl_key = node['gitlab'][svc]['ssl_certificate_key']
+puma_ssl_key_password_command = node['gitlab'][svc]['ssl_key_password_command']
 puma_ssl_client_cert = node['gitlab'][svc]['ssl_client_certificate']
 puma_ssl_cipher_filter = node['gitlab'][svc]['ssl_cipher_filter']
 puma_ssl_verify_mode = node['gitlab'][svc]['ssl_verify_mode']
@@ -85,6 +86,7 @@ puma_config puma_rb do
   ssl_certificate_key puma_ssl_key
   ssl_cipher_filter puma_ssl_cipher_filter
   ssl_client_certificate puma_ssl_client_cert
+  ssl_key_password_command puma_ssl_key_password_command
   ssl_verify_mode puma_ssl_verify_mode
   worker_timeout node['gitlab'][svc]['worker_timeout']
   per_worker_max_memory_mb node['gitlab'][svc]['per_worker_max_memory_mb']
diff --git a/files/gitlab-cookbooks/gitlab/resources/puma_config.rb b/files/gitlab-cookbooks/gitlab/resources/puma_config.rb
index 36d92ef506..879e10a8d6 100644
--- a/files/gitlab-cookbooks/gitlab/resources/puma_config.rb
+++ b/files/gitlab-cookbooks/gitlab/resources/puma_config.rb
@@ -16,6 +16,7 @@ property :ssl_certificate, [String, nil], default: nil
 property :ssl_certificate_key, [String, nil], default: nil
 property :ssl_client_certificate, [String, nil], default: nil
 property :ssl_cipher_filter, [String, nil], default: nil
+property :ssl_key_password_command, [String, nil], default: nil
 property :ssl_verify_mode, String, default: 'none'
 property :working_directory, [String, nil], default: nil
 property :worker_timeout, Integer, default: 60
diff --git a/files/gitlab-cookbooks/gitlab/templates/default/puma.rb.erb b/files/gitlab-cookbooks/gitlab/templates/default/puma.rb.erb
index b9b4436f43..ad5ff412ab 100644
--- a/files/gitlab-cookbooks/gitlab/templates/default/puma.rb.erb
+++ b/files/gitlab-cookbooks/gitlab/templates/default/puma.rb.erb
@@ -50,6 +50,9 @@ ssl_bind '<%= @ssl_listen_host %>', <%= @ssl_port %>, {
   <%- if @ssl_certificate_key %>
   key: '<%= @ssl_certificate_key %>',
   <%- end %>
+  <%- if @ssl_key_password_command %>
+  key_password_command: '<%= @ssl_key_password_command %>',
+  <%- end %>
   <%- if @ssl_client_certificate %>
   ca: '<%= @ssl_client_certificate %>',
   <%- end %>
diff --git a/spec/chef/cookbooks/gitlab/recipes/puma_spec.rb b/spec/chef/cookbooks/gitlab/recipes/puma_spec.rb
index 2299449c8b..8d9b58386b 100644
--- a/spec/chef/cookbooks/gitlab/recipes/puma_spec.rb
+++ b/spec/chef/cookbooks/gitlab/recipes/puma_spec.rb
@@ -137,7 +137,8 @@ RSpec.describe 'gitlab::puma with Ubuntu 16.04' do
           ssl_listen: '192.168.0.1',
           ssl_port: 9999,
           ssl_certificate: '/tmp/test.crt',
-          ssl_certificate_key: '/tmp/test.key'
+          ssl_certificate_key: '/tmp/test.key',
+          ssl_key_password_command: 'echo mypassword'
         }
       }
     end
@@ -149,6 +150,9 @@ RSpec.describe 'gitlab::puma with Ubuntu 16.04' do
 
     it 'renders the puma.rb file' do
       expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with(
+        ssl_certificate: '/tmp/test.crt',
+        ssl_certificate_key: '/tmp/test.key',
+        ssl_key_password_command: 'echo mypassword',
         ssl_listen_host: '192.168.0.1',
         ssl_port: 9999,
         listen_socket: '/tmp/puma.socket',
@@ -159,7 +163,7 @@ RSpec.describe 'gitlab::puma with Ubuntu 16.04' do
       )
 
       expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content|
-        expect(content).to match(/ssl_bind '192.168.0.1', 9999, {\n  cert: '\/tmp\/test.crt',\n  key: '\/tmp\/test.key',\n  verify_mode: 'none'\n}/m)
+        expect(content).to match(/ssl_bind '192.168.0.1', 9999, {\n  cert: '\/tmp\/test.crt',\n  key: '\/tmp\/test.key',\n  key_password_command: 'echo mypassword',\n  verify_mode: 'none'\n}/m)
       }
     end
 
@@ -186,13 +190,17 @@ RSpec.describe 'gitlab::puma with Ubuntu 16.04' do
             ssl_listen: '192.168.0.1',
             ssl_port: 9999,
             ssl_certificate: '/tmp/test.crt',
-            ssl_certificate_key: '/tmp/test.key'
+            ssl_certificate_key: '/tmp/test.key',
+            ssl_key_password_command: nil
           }
         }
       end
 
       it 'omits the UNIX socket and TCP binds from the Puma config' do
         expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with(
+          ssl_certificate: '/tmp/test.crt',
+          ssl_certificate_key: '/tmp/test.key',
+          ssl_key_password_command: nil,
           ssl_listen_host: '192.168.0.1',
           ssl_port: 9999,
           listen_socket: '',
@@ -218,6 +226,7 @@ RSpec.describe 'gitlab::puma with Ubuntu 16.04' do
         base_params.tap do |config|
           config[:puma][:ssl_client_certificate] = client_cert
           config[:puma][:ssl_cipher_filter] = filter
+          config[:puma][:ssl_key_password_command] = nil
           config[:puma][:ssl_verify_mode] = 'peer'
         end
       end
@@ -228,6 +237,7 @@ RSpec.describe 'gitlab::puma with Ubuntu 16.04' do
           ssl_port: 9999,
           listen_socket: '/tmp/puma.socket',
           listen_tcp: '10.0.0.1:9000',
+          ssl_client_certificate: client_cert,
           ssl_cipher_filter: filter,
           worker_processes: 4,
           min_threads: 5,
-- 
GitLab