Skip to content

Commit fee3fc7

Browse files
authored
Disable MySQL TLS peer verification when no ca_cert is set (#683)
MariaDB Connector/C 3.4.x (shipped since capi-release 1.242.0) verifies the server certificate by default. When Cloud Controller connects to a MySQL database without ccdb.ca_cert - as use-external-dbs.yml does after removing the CA, e.g. against GCP Cloud SQL's self-signed CA - there is no CA to verify against, so the connection fails with "unable to get local issuer certificate". Set MARIADB_TLS_DISABLE_PEER_VERIFICATION=1 for every path that opens a ccdb connection, but only when the scheme is mysql and no ca_cert is configured: - the bpm environment of the long-running CC processes (cloud_controller_ng and its local_worker, cloud_controller_worker, cloud_controller_clock, cc_deployment_updater); - the cloud_controller_ng pre-start hook, which runs DB migrations, seeding and encryption-key validation outside bpm (chpst preserves the exported variable for those scripts); - the rotate_cc_database_key and blobstore_benchmark errands, which boot CC and connect to the database. This restores the pre-3.4.x encrypt-without-verify behavior for that path. When a ca_cert is set, verification stays on. The variable is only set for MySQL; Postgres uses libpq and is unaffected. Add template rendering tests covering the three paths (mysql without ca_cert, mysql with ca_cert, postgres) for the bpm templates of all four jobs, the cloud_controller_ng pre-start hook, and the rotate_cc_database_key and blobstore_benchmark errands.
1 parent 890e0bb commit fee3fc7

14 files changed

Lines changed: 412 additions & 1 deletion

File tree

jobs/blobstore_benchmark/templates/bin/run.erb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ export LD_PRELOAD=/var/vcap/packages/jemalloc/lib/libjemalloc.so
1111

1212
perform_blobstore_benchmarks() {
1313
export CLOUD_CONTROLLER_NG_CONFIG=/var/vcap/jobs/blobstore_benchmark/config/cloud_controller_ng.yml
14+
<%
15+
# This errand boots CCNG and opens a ccdb connection. Disable MySQL TLS peer
16+
# verification when no ccdb.ca_cert is set. See the cloud_controller_ng job's
17+
# config/bpm.yml template for the rationale. DB config comes from the
18+
# cloud_controller_db link.
19+
ca_cert_configured = false
20+
link("cloud_controller_db").if_p("ccdb.ca_cert") { |ca_cert| ca_cert_configured = !ca_cert.to_s.strip.empty? }
21+
if link("cloud_controller_db").p("ccdb.db_scheme") == "mysql" && !ca_cert_configured
22+
%>
23+
export MARIADB_TLS_DISABLE_PEER_VERIFICATION="1"
24+
<% end %>
1425
source "${JOB_DIR}/bin/ruby_version.sh"
1526
cd /var/vcap/packages/cloud_controller_ng/cloud_controller_ng
1627

jobs/cc_deployment_updater/templates/bpm.yml.erb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,12 @@ processes:
99
LANG: en_US.UTF-8
1010
LIBRARY_PATH: /var/vcap/packages/libpq/lib
1111
NEWRELIC_ENABLE: false
12+
<%
13+
# Disable MySQL TLS peer verification when no ccdb.ca_cert is set. See the
14+
# cloud_controller_ng job for the rationale.
15+
ca_cert_configured = false
16+
if_p("ccdb.ca_cert") { |ca_cert| ca_cert_configured = !ca_cert.to_s.strip.empty? }
17+
if p("ccdb.db_scheme") == "mysql" && !ca_cert_configured
18+
%>
19+
MARIADB_TLS_DISABLE_PEER_VERIFICATION: "1"
20+
<% end %>

jobs/cloud_controller_clock/templates/bpm.yml.erb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,12 @@ processes:
99
LANG: en_US.UTF-8
1010
LIBRARY_PATH: /var/vcap/packages/libpq/lib
1111
NEWRELIC_ENABLE: false
12+
<%
13+
# Disable MySQL TLS peer verification when no ccdb.ca_cert is set. See the
14+
# cloud_controller_ng job for the rationale.
15+
ca_cert_configured = false
16+
if_p("ccdb.ca_cert") { |ca_cert| ca_cert_configured = !ca_cert.to_s.strip.empty? }
17+
if p("ccdb.db_scheme") == "mysql" && !ca_cert_configured
18+
%>
19+
MARIADB_TLS_DISABLE_PEER_VERIFICATION: "1"
20+
<% end %>

jobs/cloud_controller_ng/templates/bpm.yml.erb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ def mount_valkey_volume!(config)
2020
}
2121
end
2222

23+
# MariaDB Connector/C 3.4.x verifies the server certificate by default. When
24+
# CC talks to a MySQL database without a ccdb.ca_cert (e.g. use-external-dbs.yml
25+
# strips it, as with GCP Cloud SQL's self-signed CA), there is no CA to verify
26+
# against, so the connection can only fail. Disable peer verification in that
27+
# case to restore the pre-3.4.x encrypt-without-verify behavior. When a ca_cert
28+
# is configured, verification stays on. Only set for MySQL; Postgres uses libpq,
29+
# which this variable does not affect.
30+
def disable_mysql_tls_peer_verification!(config)
31+
return unless p("ccdb.db_scheme") == "mysql"
32+
ca_cert_configured = false
33+
if_p("ccdb.ca_cert") { |ca_cert| ca_cert_configured = !ca_cert.to_s.strip.empty? }
34+
return if ca_cert_configured
35+
config["env"]["MARIADB_TLS_DISABLE_PEER_VERIFICATION"] = "1"
36+
end
37+
2338
cloud_controller_ng_config = {
2439
"name" => "cloud_controller_ng",
2540
"executable" => "/var/vcap/jobs/cloud_controller_ng/bin/cloud_controller_ng",
@@ -59,6 +74,7 @@ if properties.env
5974
end
6075
mount_nfs_volume!(cloud_controller_ng_config)
6176
mount_valkey_volume!(cloud_controller_ng_config)
77+
disable_mysql_tls_peer_verification!(cloud_controller_ng_config)
6278

6379
nginx_config = {
6480
"name" => "nginx",
@@ -114,12 +130,12 @@ config = {
114130
}
115131
}
116132
mount_nfs_volume!(local_worker_config)
133+
disable_mysql_tls_peer_verification!(local_worker_config)
117134

118135
if !!properties.cc.newrelic.license_key
119136
local_worker_config["env"]["NEWRELIC_ENABLE"] = true
120137
end
121138

122-
123139
config["processes"] << local_worker_config
124140
end
125141

jobs/cloud_controller_ng/templates/pre-start.sh.erb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ CC_JOB_DIR="/var/vcap/jobs/cloud_controller_ng"
1010
CONFIG_DIR="${CC_JOB_DIR}/config"
1111
export CLOUD_CONTROLLER_NG_CONFIG="${CONFIG_DIR}/cloud_controller_ng.yml"
1212

13+
<%
14+
# DB migrations, seeding and encryption-key validation run here in pre-start
15+
# (outside bpm) and open a ccdb connection. Disable MySQL TLS peer
16+
# verification when no ccdb.ca_cert is set. See the config/bpm.yml template
17+
# for the rationale. chpst preserves this exported env for the migrate/seed/
18+
# validate scripts.
19+
ca_cert_configured = false
20+
if_p("ccdb.ca_cert") { |ca_cert| ca_cert_configured = !ca_cert.to_s.strip.empty? }
21+
if p("ccdb.db_scheme") == "mysql" && !ca_cert_configured
22+
%>
23+
export MARIADB_TLS_DISABLE_PEER_VERIFICATION="1"
24+
<% end %>
25+
1326
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
1427
source "${SCRIPT_DIR}/ruby_version.sh"
1528

jobs/cloud_controller_worker/templates/bpm.yml.erb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
config = { "processes" => [] }
44

5+
# Disable MySQL TLS peer verification when no ccdb.ca_cert is set. See the
6+
# cloud_controller_ng job for the rationale.
7+
def disable_mysql_tls_peer_verification!(config)
8+
return unless p("ccdb.db_scheme") == "mysql"
9+
ca_cert_configured = false
10+
if_p("ccdb.ca_cert") { |ca_cert| ca_cert_configured = !ca_cert.to_s.strip.empty? }
11+
return if ca_cert_configured
12+
config["env"]["MARIADB_TLS_DISABLE_PEER_VERIFICATION"] = "1"
13+
end
14+
515
(1..(p("cc.jobs.generic.number_of_workers"))).each do |index|
616
worker_config = {
717
"name" => "worker_#{index}",
@@ -30,6 +40,8 @@ config = { "processes" => [] }
3040
]
3141
end
3242

43+
disable_mysql_tls_peer_verification!(worker_config)
44+
3345
if !!properties.cc.newrelic.license_key
3446
worker_config["env"]["NEWRELIC_ENABLE"] = true
3547
end

jobs/rotate_cc_database_key/templates/bin/run.erb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ export LD_PRELOAD=/var/vcap/packages/jemalloc/lib/libjemalloc.so
66

77
rotate() {
88
export CLOUD_CONTROLLER_NG_CONFIG=/var/vcap/jobs/rotate_cc_database_key/config/cloud_controller_ng.yml
9+
<%
10+
# This errand opens a ccdb connection to rotate the encryption key. Disable
11+
# MySQL TLS peer verification when no ccdb.ca_cert is set. See the
12+
# cloud_controller_ng job's config/bpm.yml template for the rationale. DB
13+
# config comes from the cloud_controller_db link.
14+
ca_cert_configured = false
15+
link("cloud_controller_db").if_p("ccdb.ca_cert") { |ca_cert| ca_cert_configured = !ca_cert.to_s.strip.empty? }
16+
if link("cloud_controller_db").p("ccdb.db_scheme") == "mysql" && !ca_cert_configured
17+
%>
18+
export MARIADB_TLS_DISABLE_PEER_VERIFICATION="1"
19+
<% end %>
920
source /var/vcap/jobs/rotate_cc_database_key/bin/ruby_version.sh
1021
cd /var/vcap/packages/cloud_controller_ng/cloud_controller_ng
1122
exec bundle exec rake rotate_cc_database_key:perform

spec/blobstore_benchmark/blobstore_benchmark_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,43 @@ module Test
177177
end
178178
end
179179
end
180+
181+
describe 'bin/run' do
182+
let(:template) { job.template('bin/run') }
183+
184+
def disables_peer_verification?(db_scheme:, ca_cert:)
185+
ccdb = {
186+
'db_scheme' => db_scheme,
187+
'databases' => [{ 'tag' => 'cc', 'name' => 'ccdb' }],
188+
'roles' => [{ 'tag' => 'admin', 'name' => 'u', 'password' => 'p' }],
189+
'port' => 3306
190+
}
191+
ccdb['ca_cert'] = ca_cert unless ca_cert.nil?
192+
db_link = Link.new(name: 'cloud_controller_db', properties: { 'ccdb' => ccdb },
193+
instances: [LinkInstance.new(address: 'ccdb')])
194+
rendered = template.render({ 'blobstore_benchmark' => { 'mode' => 'all' } },
195+
consumes: [cloud_controller_internal_link, db_link, database_link])
196+
rendered.include?('export MARIADB_TLS_DISABLE_PEER_VERIFICATION="1"')
197+
end
198+
199+
context 'when the database is mysql and no ca_cert is configured' do
200+
it 'disables peer verification' do
201+
expect(disables_peer_verification?(db_scheme: 'mysql', ca_cert: nil)).to be(true)
202+
end
203+
end
204+
205+
context 'when the database is mysql and a ca_cert is configured' do
206+
it 'does not disable peer verification' do
207+
expect(disables_peer_verification?(db_scheme: 'mysql', ca_cert: 'a-ca-cert')).to be(false)
208+
end
209+
end
210+
211+
context 'when the database is postgres' do
212+
it 'does not disable peer verification' do
213+
expect(disables_peer_verification?(db_scheme: 'postgres', ca_cert: nil)).to be(false)
214+
end
215+
end
216+
end
180217
end
181218
end
182219
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# frozen_string_literal: true
2+
3+
require 'rspec'
4+
require 'bosh/template/test'
5+
require 'yaml'
6+
7+
module Bosh
8+
module Template
9+
module Test
10+
describe 'cc_deployment_updater bpm job template rendering' do
11+
let(:release_path) { File.join(File.dirname(__FILE__), '../..') }
12+
let(:release) { ReleaseDir.new(release_path) }
13+
let(:job) { release.job('cc_deployment_updater') }
14+
15+
describe 'config/bpm.yml' do
16+
let(:template) { job.template('config/bpm.yml') }
17+
18+
describe 'MySQL TLS peer verification' do
19+
def updater_env(properties)
20+
template_hash = YAML.safe_load(template.render(properties, consumes: {}))
21+
updater = template_hash['processes'].find { |p| p['name'] == 'cc_deployment_updater' }
22+
expect(updater).not_to be_nil
23+
updater['env']
24+
end
25+
26+
context 'when the database is mysql and no ca_cert is configured' do
27+
let(:properties) { { 'ccdb' => { 'db_scheme' => 'mysql' } } }
28+
29+
it 'disables peer verification' do
30+
expect(updater_env(properties)['MARIADB_TLS_DISABLE_PEER_VERIFICATION']).to eq('1')
31+
end
32+
end
33+
34+
context 'when the database is mysql and a ca_cert is configured' do
35+
let(:properties) { { 'ccdb' => { 'db_scheme' => 'mysql', 'ca_cert' => 'a-ca-cert' } } }
36+
37+
it 'does not disable peer verification' do
38+
expect(updater_env(properties)).not_to have_key('MARIADB_TLS_DISABLE_PEER_VERIFICATION')
39+
end
40+
end
41+
42+
context 'when the database is postgres' do
43+
let(:properties) { { 'ccdb' => { 'db_scheme' => 'postgres' } } }
44+
45+
it 'does not disable peer verification' do
46+
expect(updater_env(properties)).not_to have_key('MARIADB_TLS_DISABLE_PEER_VERIFICATION')
47+
end
48+
end
49+
end
50+
end
51+
end
52+
end
53+
end
54+
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# frozen_string_literal: true
2+
3+
require 'rspec'
4+
require 'bosh/template/test'
5+
require 'yaml'
6+
7+
module Bosh
8+
module Template
9+
module Test
10+
describe 'cloud_controller_clock bpm job template rendering' do
11+
let(:release_path) { File.join(File.dirname(__FILE__), '../..') }
12+
let(:release) { ReleaseDir.new(release_path) }
13+
let(:job) { release.job('cloud_controller_clock') }
14+
15+
describe 'config/bpm.yml' do
16+
let(:template) { job.template('config/bpm.yml') }
17+
18+
describe 'MySQL TLS peer verification' do
19+
def clock_env(properties)
20+
template_hash = YAML.safe_load(template.render(properties, consumes: {}))
21+
clock = template_hash['processes'].find { |p| p['name'] == 'cloud_controller_clock' }
22+
expect(clock).not_to be_nil
23+
clock['env']
24+
end
25+
26+
context 'when the database is mysql and no ca_cert is configured' do
27+
let(:properties) { { 'ccdb' => { 'db_scheme' => 'mysql' } } }
28+
29+
it 'disables peer verification' do
30+
expect(clock_env(properties)['MARIADB_TLS_DISABLE_PEER_VERIFICATION']).to eq('1')
31+
end
32+
end
33+
34+
context 'when the database is mysql and a ca_cert is configured' do
35+
let(:properties) { { 'ccdb' => { 'db_scheme' => 'mysql', 'ca_cert' => 'a-ca-cert' } } }
36+
37+
it 'does not disable peer verification' do
38+
expect(clock_env(properties)).not_to have_key('MARIADB_TLS_DISABLE_PEER_VERIFICATION')
39+
end
40+
end
41+
42+
context 'when the database is postgres' do
43+
let(:properties) { { 'ccdb' => { 'db_scheme' => 'postgres' } } }
44+
45+
it 'does not disable peer verification' do
46+
expect(clock_env(properties)).not_to have_key('MARIADB_TLS_DISABLE_PEER_VERIFICATION')
47+
end
48+
end
49+
end
50+
end
51+
end
52+
end
53+
end
54+
end

0 commit comments

Comments
 (0)