Skip to content

Commit 614efad

Browse files
committed
Fixes #31049 - Introduce server CA file setting
1 parent ccbc96d commit 614efad

File tree

6 files changed

+126
-24
lines changed

6 files changed

+126
-24
lines changed

app/models/setting.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Setting < ApplicationRecord
1515
FROZEN_ATTRS = %w{name category}
1616
NONZERO_ATTRS = %w{puppet_interval idle_timeout entries_per_page outofsync_interval}
1717
BLANK_ATTRS = %w{ host_owner trusted_hosts login_delegation_logout_url root_pass default_location default_organization websockets_ssl_key websockets_ssl_cert oauth_consumer_key oauth_consumer_secret login_text oidc_audience oidc_issuer oidc_algorithm
18-
smtp_address smtp_domain smtp_user_name smtp_password smtp_openssl_verify_mode smtp_authentication sendmail_arguments sendmail_location http_proxy http_proxy_except_list default_locale default_timezone ssl_certificate ssl_ca_file ssl_priv_key default_pxe_item_global default_pxe_item_local oidc_jwks_url instance_title }
18+
smtp_address smtp_domain smtp_user_name smtp_password smtp_openssl_verify_mode smtp_authentication sendmail_arguments sendmail_location http_proxy http_proxy_except_list default_locale default_timezone ssl_certificate ssl_ca_file server_ca_file ssl_priv_key default_pxe_item_global default_pxe_item_local oidc_jwks_url instance_title }
1919
ARRAY_HOSTNAMES = %w{trusted_hosts}
2020
URI_ATTRS = %w{foreman_url unattended_url}
2121
URI_BLANK_ATTRS = %w{login_delegation_logout_url}

app/models/setting/auth.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def self.default_settings
1515
set('ssl_client_dn_env', N_('Environment variable containing the subject DN from a client SSL certificate'), 'SSL_CLIENT_S_DN', N_('SSL client DN env')),
1616
set('ssl_client_verify_env', N_('Environment variable containing the verification status of a client SSL certificate'), 'SSL_CLIENT_VERIFY', N_('SSL client verify env')),
1717
set('ssl_client_cert_env', N_("Environment variable containing a client's SSL certificate"), 'SSL_CLIENT_CERT', N_('SSL client cert env')),
18+
set('server_ca_file', N_("SSL CA file that will be used in templates (to verify the connection to Foreman)"), nil, N_('Server CA file')),
1819
set('websockets_ssl_key', N_("Private key file that Foreman will use to encrypt websockets "), nil, N_('Websockets SSL key')),
1920
set('websockets_ssl_cert', N_("Certificate that Foreman will use to encrypt websockets "), nil, N_('Websockets SSL certificate')),
2021
# websockets_encrypt depends on key/cert when true, so initialize it last

lib/foreman/renderer/scope/macros/base.rb

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -334,20 +334,31 @@ def gem_version_compare(first, second)
334334
Gem::Version.new(first.to_s) <=> Gem::Version.new(second.to_s)
335335
end
336336

337-
apipie :method, "Returns content of 'SSL CA file' configured in Settings > Authentication" do
337+
apipie :method, "Returns a combination of 'Server CA file' and 'SSL CA file' configured in Settings > Authentication" do
338338
example "SSL_CA_CERT=$(mktemp)
339339
cat > $SSL_CA_CERT <<CA_CONTENT
340340
<%= foreman_server_ca_cert %>
341341
CA_CONTENT
342-
curl --cacert $SSL_CA_CERT https://smart-proxy.example.com:8443"
343-
end
344-
def foreman_server_ca_cert
345-
if File.exist?(Setting[:ssl_ca_file])
346-
File.read(Setting[:ssl_ca_file])
347-
else
348-
msg = N_("SSL CA file not found, check the 'SSL CA file' in Settings > Authentication")
349-
raise Foreman::Exception.new(msg)
342+
curl --cacert $SSL_CA_CERT https://foreman.example.com:8443"
343+
end
344+
def foreman_server_ca_cert(server_ca_file_enabled: true, ssl_ca_file_enabled: true)
345+
setting_values = []
346+
setting_values << Setting[:server_ca_file] if server_ca_file_enabled
347+
setting_values << Setting[:ssl_ca_file] if ssl_ca_file_enabled
348+
files_content = setting_values.uniq.compact.map do |setting_value|
349+
File.read(setting_value)
350+
rescue StandardError => e
351+
Foreman::Logging.logger('templates').warn("Failed to read CA file: #{e}")
352+
353+
nil
350354
end
355+
356+
result = files_content.compact.join("\n")
357+
358+
msg = N_("SSL CA file not found, check the 'Server CA file' and 'SSL CA file' in Settings > Authentication")
359+
raise Foreman::Exception.new(msg) unless result.present?
360+
361+
result
351362
end
352363

353364
private

test/fixtures/settings.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ attributes4:
2222
description: Enable safe mode config templates rendinging(recommended)
2323
attributes5:
2424
name: ssl_certificate
25-
category: Setting::Provisioning
25+
category: Setting::Auth
2626
default: /var/lib/puppet/ssl/certs/some.host.fqdn
2727
description: SSL Certificate path that foreman would use to communicate with its proxies
2828
attributes6:
2929
name: ssl_ca_file
30-
category: Setting::Provisioning
30+
category: Setting::Auth
3131
default: /var/lib/puppet/ssl/certs/ca.pem
3232
description: SSL CA file that foreman would use to communicate with its proxies
3333
attributes7:
3434
name: ssl_priv_key
35-
category: Setting::Provisioning
35+
category: Setting::Auth
3636
default: /var/lib/puppet/ssl/private_keys/super.some.host.fqdn.pem
3737
description: SSL Private Key file that foreman would use to communicate with its proxies
3838
attributes8:
@@ -428,3 +428,8 @@ attribute94:
428428
category: Setting::Provisioning
429429
default: 'Global Registration'
430430
description: "Default Global registration template"
431+
attributes95:
432+
name: server_ca_file
433+
category: Setting::Auth
434+
default: /var/lib/puppet/ssl/certs/ca.pem
435+
description: SSL CA file that will be used in templates (to verify the connection to Foreman)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
-----BEGIN CERTIFICATE-----
2+
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
32+
SBq3Hxpl4HptdDg+JyI1RIg=
33+
-----END CERTIFICATE-----

test/unit/foreman/renderer/renderers_shared_tests.rb

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -205,18 +205,70 @@ module RenderersSharedTests
205205
assert_equal(renderer.render(source, @scope), '-1')
206206
end
207207

208-
test "foreman_server_ca_cert - existing file" do
209-
cert_path = Rails.root.join('test/static_fixtures/certificates/example.com.crt')
210-
Setting[:ssl_ca_file] = cert_path
211-
source = OpenStruct.new(content: '<%= foreman_server_ca_cert %>')
212-
assert_equal(renderer.render(source, @scope), File.read(cert_path))
213-
end
208+
describe '#foreman_server_ca_cert' do
209+
subject { renderer.render(source, @scope) }
214210

215-
test "foreman_server_ca_cert - not existing file" do
216-
Setting[:ssl_ca_file] = 'not-existing-file'
217-
source = OpenStruct.new(content: '<%= foreman_server_ca_cert %>')
218-
assert_raise Foreman::Exception do
219-
renderer.render(source, @scope)
211+
let(:source) { OpenStruct.new(content: '<%= foreman_server_ca_cert %>') }
212+
let(:cert_path) { Rails.root.join('test/static_fixtures/certificates/example.com.crt') }
213+
let(:cert_2_path) { Rails.root.join('test/static_fixtures/certificates/example2.com.crt') }
214+
let(:cert_file_content) { File.read(cert_path) }
215+
let(:cert_2_file_content) { File.read(cert_2_path) }
216+
217+
test "load server_ca_file" do
218+
Setting[:server_ca_file] = cert_path
219+
Setting[:ssl_ca_file] = 'not-existing-file'
220+
221+
assert_equal subject, cert_file_content
222+
end
223+
224+
test "load ssl_ca_file" do
225+
Setting[:server_ca_file] = 'not-existing-file'
226+
Setting[:ssl_ca_file] = cert_path
227+
228+
assert_equal subject, cert_file_content
229+
end
230+
231+
test "load server_ca_file and ssl_ca_file" do
232+
Setting[:server_ca_file] = cert_path
233+
Setting[:ssl_ca_file] = cert_2_path
234+
235+
expected = "#{cert_file_content}\n#{cert_2_file_content}"
236+
assert_equal subject, expected
237+
end
238+
239+
test "do not load any files and raise exception" do
240+
Setting[:server_ca_file] = 'not-existing-file'
241+
Setting[:ssl_ca_file] = 'not-existing-file'
242+
243+
assert_raise Foreman::Exception do
244+
subject
245+
end
246+
end
247+
248+
context 'when server_ca_file is disabled' do
249+
let(:source) { OpenStruct.new(content: '<%= foreman_server_ca_cert(server_ca_file_enabled: false) %>') }
250+
251+
test "do not load server_ca_file and raise exception" do
252+
Setting[:server_ca_file] = cert_path
253+
Setting[:ssl_ca_file] = 'not-existing-file'
254+
255+
assert_raise Foreman::Exception do
256+
subject
257+
end
258+
end
259+
end
260+
261+
context 'when ssl_ca_file is disabled' do
262+
let(:source) { OpenStruct.new(content: '<%= foreman_server_ca_cert(ssl_ca_file_enabled: false) %>') }
263+
264+
test "do not load ssl_ca_file and raise exception" do
265+
Setting[:server_ca_file] = 'not-existing-file'
266+
Setting[:ssl_ca_file] = cert_path
267+
268+
assert_raise Foreman::Exception do
269+
subject
270+
end
271+
end
220272
end
221273
end
222274

0 commit comments

Comments
 (0)