From 8b905aad18be7e9834c1fd2ca8574e1a4d9459fc Mon Sep 17 00:00:00 2001 From: Simon Hoenscheid Date: Thu, 25 Jan 2024 14:25:47 +0100 Subject: [PATCH] value of ssl_mode should be used for connection string * add spec tests for connect function from puppet-icinga * update connect function with code from puppet-icinga * fix spec tests * patch function, add tests to use given ssl_mode * update reference.md * update reference.md, fix enum values, fix condition, add test --- REFERENCE.md | 19 +++--- functions/db/connect.pp | 39 +++++++----- manifests/feature/idopgsql.pp | 5 +- spec/classes/idomysql_spec.rb | 6 +- spec/classes/idopgsql_spec.rb | 56 +++++++++++++++- spec/functions/db_connect_spec.rb | 102 ++++++++++++++++++++++++++++++ 6 files changed, 194 insertions(+), 33 deletions(-) create mode 100644 spec/functions/db_connect_spec.rb diff --git a/REFERENCE.md b/REFERENCE.md index 141be7dd..15708cd4 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1695,12 +1695,7 @@ Default value: `'icinga'` ##### `ssl_mode` -Data type: - -```puppet -Optional[Enum['disable', 'allow', 'prefer', - 'verify-full', 'verify-ca', 'require']] -``` +Data type: `Optional[Enum['verify-full', 'verify-ca']]` Enable SSL connection mode. @@ -5603,7 +5598,7 @@ with or without TLS information. database => String, username => String, password => Optional[Variant[String, Sensitive[String]]], - }] $db, Hash[String, Any] $tls, Optional[Boolean] $use_tls = undef)` + }] $db, Hash[String, Any] $tls, Optional[Boolean] $use_tls = undef, Optional[Enum['verify-full', 'verify-ca']] $ssl_mode = undef)` The icinga2::db::connect function. @@ -5624,19 +5619,25 @@ Struct[{ }] ``` - +Data hash with database information. ##### `tls` Data type: `Hash[String, Any]` - +Data hash with TLS connection information. ##### `use_tls` Data type: `Optional[Boolean]` +Wether or not to use TLS encryption. + +##### `ssl_mode` +Data type: `Optional[Enum['verify-full', 'verify-ca']]` + +Enable SSL connection mode. ### `icinga2::icinga2_attributes` diff --git a/functions/db/connect.pp b/functions/db/connect.pp index 8191246c..e6ffc33b 100644 --- a/functions/db/connect.pp +++ b/functions/db/connect.pp @@ -5,6 +5,18 @@ # @return # Connection string to connect database. # +# @param db +# Data hash with database information. +# +# @param tls +# Data hash with TLS connection information. +# +# @param use_tls +# Wether or not to use TLS encryption. +# +# @param ssl_mode +# Enable SSL connection mode. +# function icinga2::db::connect( Struct[{ type => Enum['pgsql','mysql','mariadb'], @@ -16,21 +28,14 @@ function icinga2::db::connect( }] $db, Hash[String, Any] $tls, Optional[Boolean] $use_tls = undef, + Optional[Enum['verify-full', 'verify-ca']] $ssl_mode = undef, ) >> String { - # @param db - # Data hash with database information. - # - # @param tls - # Data hash with TLS connection information. - # - # @param use_tls - # Wether or not to use TLS encryption. - # if $use_tls { case $db['type'] { 'pgsql': { + $real_ssl_mode = if $ssl_mode { $ssl_mode } else { 'verify-full' } $tls_options = regsubst(join(any2array(delete_undef_values({ - 'sslmode=' => if $tls['noverify'] { 'require' } else { 'verify-full' }, + 'sslmode=' => if $tls['noverify'] { 'require' } else { $real_ssl_mode }, 'sslcert=' => $tls['cert_file'], 'sslkey=' => $tls['key_file'], 'sslrootcert=' => $tls['cacert_file'], @@ -39,20 +44,20 @@ function icinga2::db::connect( 'mariadb': { $tls_options = join(any2array(delete_undef_values({ '--ssl' => '', - '--ssl-ca' => $tls['cacert_file'], + '--ssl-ca' => if $tls['noverify'] { undef } else { $tls['cacert_file'] }, '--ssl-cert' => $tls['cert_file'], '--ssl-key' => $tls['key_file'], - '--ssl-capath' => $tls['capath'], + '--ssl-capath' => if $tls['noverify'] { undef } else { $tls['capath'] }, '--ssl-cipher' => $tls['cipher'], })), ' ') } 'mysql': { $tls_options = join(any2array(delete_undef_values({ - '--ssl-mode' => 'required', - '--ssl-ca' => $tls['cacert_file'], + '--ssl-mode' => if $tls['noverify'] { 'REQUIRED' } else { 'VERIFY_CA' }, + '--ssl-ca' => if $tls['noverify'] { undef } else { $tls['cacert_file'] }, '--ssl-cert' => $tls['cert_file'], '--ssl-key' => $tls['key_file'], - '--ssl-capath' => $tls['capath'], + '--ssl-capath' => if $tls['noverify'] { undef } else { $tls['capath'] }, '--ssl-cipher' => $tls['cipher'], })), ' ') } @@ -80,10 +85,10 @@ function icinga2::db::connect( }, '-P' => $db['port'], '-u' => $db['username'], - "-p'${_password}'" => '', + "-p'${_password}'" => if $db['password'] { '' } else { undef }, '-D' => $db['database'], })), ' ') } - "${options} ${tls_options}" + strip(regsubst("${options} ${tls_options}", '\s{2,}', ' ')) } diff --git a/manifests/feature/idopgsql.pp b/manifests/feature/idopgsql.pp index 224c2f03..a81be922 100644 --- a/manifests/feature/idopgsql.pp +++ b/manifests/feature/idopgsql.pp @@ -88,8 +88,7 @@ Optional[Stdlib::Port::Unprivileged] $port = undef, String $user = 'icinga', String $database = 'icinga', - Optional[Enum['disable', 'allow', 'prefer', - 'verify-full', 'verify-ca', 'require']] $ssl_mode = undef, + Optional[Enum['verify-full', 'verify-ca']] $ssl_mode = undef, Optional[Stdlib::Absolutepath] $ssl_key_path = undef, Optional[Stdlib::Absolutepath] $ssl_cert_path = undef, Optional[Stdlib::Absolutepath] $ssl_cacert_path = undef, @@ -212,7 +211,7 @@ 'port' => $port, 'database' => $database, 'username' => $user, - }, $cert, $enable_ssl) + }, $cert, $enable_ssl, $ssl_mode) exec { 'idopgsql-import-schema': user => 'root', diff --git a/spec/classes/idomysql_spec.rb b/spec/classes/idomysql_spec.rb index 2009cbc8..616f136e 100644 --- a/spec/classes/idomysql_spec.rb +++ b/spec/classes/idomysql_spec.rb @@ -115,7 +115,7 @@ is_expected.to contain_exec('idomysql-import-schema').with( { user: 'root', - command: "mysql -u icinga -p'foo' -D icinga < \"#{ido_mysql_schema_dir}/mysql.sql\"", + command: "mysql -u icinga -p'foo' -D icinga < \"#{ido_mysql_schema_dir}/mysql.sql\"", }, ) } @@ -186,7 +186,7 @@ is_expected.to contain_exec('idomysql-import-schema').with( { 'user' => 'root', - 'command' => "mysql -h 127.0.0.1 -P 3306 -u icinga -p'foo' -D icinga --ssl --ssl-ca #{icinga2_pki_dir}/IdoMysqlConnection_ido-mysql_ca.crt" \ + 'command' => "mysql -h 127.0.0.1 -P 3306 -u icinga -p'foo' -D icinga --ssl --ssl-ca #{icinga2_pki_dir}/IdoMysqlConnection_ido-mysql_ca.crt" \ " --ssl-cert #{icinga2_pki_dir}/IdoMysqlConnection_ido-mysql.crt --ssl-key #{icinga2_pki_dir}/IdoMysqlConnection_ido-mysql.key < \"#{ido_mysql_schema_dir}/mysql.sql\"", }, ) @@ -227,7 +227,7 @@ is_expected.to contain_exec('idomysql-import-schema').with( { 'user' => 'root', - 'command' => "mysql -u icinga -p'foo' -D icinga --ssl --ssl-ca #{icinga2_pki_dir}/IdoMysqlConnection_ido-mysql_ca.crt" \ + 'command' => "mysql -u icinga -p'foo' -D icinga --ssl --ssl-ca #{icinga2_pki_dir}/IdoMysqlConnection_ido-mysql_ca.crt" \ " --ssl-cert #{icinga2_pki_dir}/IdoMysqlConnection_ido-mysql.crt --ssl-key #{icinga2_pki_dir}/IdoMysqlConnection_ido-mysql.key < \"#{ido_mysql_schema_dir}/mysql.sql\"", }, ) diff --git a/spec/classes/idopgsql_spec.rb b/spec/classes/idopgsql_spec.rb index e7b822b7..57995b0d 100644 --- a/spec/classes/idopgsql_spec.rb +++ b/spec/classes/idopgsql_spec.rb @@ -99,7 +99,7 @@ { 'user' => 'root', 'environment' => ['PGPASSWORD=foo'], - 'command' => "psql 'host=localhost user=icinga dbname=icinga ' -w -f '#{ido_pgsql_schema_dir}/pgsql.sql'", + 'command' => "psql 'host=localhost user=icinga dbname=icinga' -w -f '#{ido_pgsql_schema_dir}/pgsql.sql'", }, ) } @@ -177,6 +177,60 @@ ) } end + + context "with ssl_mode => verify-ca, host => '192.168.0.1', port => 5432, import_schema => true, ssl_key => 'foo', ssl_cert => 'bar', ssl_cacert => 'baz'" do + let(:params) do + { + ssl_mode: 'verify-ca', + ssl_key: 'foo', + ssl_cert: 'bar', + ssl_cacert: 'baz', + host: '192.168.0.1', + port: 5432, + import_schema: true, + password: 'foo', + } + end + + it { + is_expected.to contain_file("#{icinga2_pki_dir}/IdoPgsqlConnection_ido-pgsql.key").with( + { + 'mode' => icinga2_sslkey_mode, + 'owner' => icinga2_user, + 'group' => icinga2_group, + }, + ).with_content(%r{^foo}) + } + + it { + is_expected.to contain_file("#{icinga2_pki_dir}/IdoPgsqlConnection_ido-pgsql.crt").with( + { + 'owner' => icinga2_user, + 'group' => icinga2_group, + }, + ).with_content(%r{^bar$}) + } + + it { + is_expected.to contain_file("#{icinga2_pki_dir}/IdoPgsqlConnection_ido-pgsql_ca.crt").with( + { + 'owner' => icinga2_user, + 'group' => icinga2_group, + }, + ).with_content(%r{^baz$}) + } + + it { + is_expected.to contain_exec('idopgsql-import-schema').with( + { + 'user' => 'root', + 'command' => "psql 'host=192.168.0.1 user=icinga port=5432 dbname=icinga sslmode=verify-ca sslcert=#{icinga2_pki_dir}/IdoPgsqlConnection_ido-pgsql.crt " \ + "sslkey=#{icinga2_pki_dir}/IdoPgsqlConnection_ido-pgsql.key sslrootcert=#{icinga2_pki_dir}/IdoPgsqlConnection_ido-pgsql_ca.crt' " \ + "-w -f '#{ido_pgsql_schema_dir}/pgsql.sql'", + }, + ) + } + end end end end diff --git a/spec/functions/db_connect_spec.rb b/spec/functions/db_connect_spec.rb new file mode 100644 index 00000000..4bb4496d --- /dev/null +++ b/spec/functions/db_connect_spec.rb @@ -0,0 +1,102 @@ +require 'spec_helper' + +describe 'icinga2::db::connect' do + it { is_expected.not_to eq(nil) } + + it 'with MySQL/MariaDB' do + is_expected.to run.with_params( + { 'type' => 'mysql', 'host' => 'localhost', 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, {} + ).and_return("-u bar -p'supersecret' -D foo") + end + + it 'with MySQL/MariaDB on db.example.org and port 4711' do + is_expected.to run.with_params( + { 'type' => 'mysql', 'host' => 'db.example.org', 'port' => 4711, 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, {} + ).and_return("-h db.example.org -P 4711 -u bar -p'supersecret' -D foo") + end + + it 'with MariaDB TLS on db.example.org and password' do + is_expected.to run.with_params( + { 'type' => 'mariadb', 'host' => 'db.example.org', 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, + { 'cacert_file' => '/cacert.file' }, + true, + ).and_return("-h db.example.org -u bar -p'supersecret' -D foo --ssl --ssl-ca /cacert.file") + end + + it "with MariaDB TLS and noverify 'true' on db.example.org and password" do + is_expected.to run.with_params( + { 'type' => 'mariadb', 'host' => 'db.example.org', 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, + { 'noverify' => true, 'cacert_file' => '/cacert.file' }, + true, + ).and_return("-h db.example.org -u bar -p'supersecret' -D foo --ssl") + end + + it 'with MariaDB client TLS cert on db.example.org' do + is_expected.to run.with_params( + { 'type' => 'mariadb', 'host' => 'db.example.org', 'database' => 'foo', 'username' => 'bar' }, + { 'key_file' => '/key.file', 'cert_file' => '/cert.file', 'cacert_file' => '/cacert.file' }, + true, + ).and_return('-h db.example.org -u bar -D foo --ssl --ssl-ca /cacert.file --ssl-cert /cert.file --ssl-key /key.file') + end + + it 'with MySQL client TLS cert on db.example.org' do + is_expected.to run.with_params( + { 'type' => 'mysql', 'host' => 'db.example.org', 'database' => 'foo', 'username' => 'bar' }, + { 'key_file' => '/key.file', 'cert_file' => '/cert.file', 'cacert_file' => '/cacert.file' }, + true, + ).and_return('-h db.example.org -u bar -D foo --ssl-mode VERIFY_CA --ssl-ca /cacert.file --ssl-cert /cert.file --ssl-key /key.file') + end + + it "with MySQL TLS and noverify 'true' on db.example.org and password" do + is_expected.to run.with_params( + { 'type' => 'mysql', 'host' => 'db.example.org', 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, + { 'noverify' => true, 'cacert_file' => '/cacert.file' }, + true, + ).and_return("-h db.example.org -u bar -p'supersecret' -D foo --ssl-mode REQUIRED") + end + + it 'with PostgreSQL' do + is_expected.to run.with_params( + { 'type' => 'pgsql', 'host' => 'localhost', 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, {} + ).and_return('host=localhost user=bar dbname=foo') + end + + it 'with PostgreSQL on db.example.org and port 4711' do + is_expected.to run.with_params( + { 'type' => 'pgsql', 'host' => 'db.example.org', 'port' => 4711, 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, {} + ).and_return('host=db.example.org user=bar port=4711 dbname=foo') + end + + it 'with PostgreSQL TLS on db.example.org and password' do + is_expected.to run.with_params( + { 'type' => 'pgsql', 'host' => 'db.example.org', 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, + { 'cacert_file' => '/cacert.file' }, + true, + ).and_return('host=db.example.org user=bar dbname=foo sslmode=verify-full sslrootcert=/cacert.file') + end + + it 'with PostgreSQL TLS on 192.168.0.1 and password' do + is_expected.to run.with_params( + { 'type' => 'pgsql', 'host' => '192.168.0.1', 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, + { 'cacert_file' => '/etc/pki/ca-trust/source/anchors/mycacert.crt' }, + true, + 'verify-ca', + ).and_return('host=192.168.0.1 user=bar dbname=foo sslmode=verify-ca sslrootcert=/etc/pki/ca-trust/source/anchors/mycacert.crt') + end + + it 'with PostgreSQL TLS (insecure) on db.example.org and password' do + is_expected.to run.with_params( + { 'type' => 'pgsql', 'host' => 'db.example.org', 'database' => 'foo', 'username' => 'bar', 'password' => 'supersecret' }, + { 'noverify' => true }, + true, + ).and_return('host=db.example.org user=bar dbname=foo sslmode=require') + end + + it 'with PostgreSQL client TLS cert on db.example.org' do + is_expected.to run.with_params( + { 'type' => 'pgsql', 'host' => 'db.example.org', 'database' => 'foo', 'username' => 'bar' }, + { 'key_file' => '/key.file', 'cert_file' => '/cert.file', 'cacert_file' => '/cacert.file' }, + true, + ).and_return('host=db.example.org user=bar dbname=foo sslmode=verify-full sslcert=/cert.file sslkey=/key.file sslrootcert=/cacert.file') + end +end