diff --git a/manifests/database/postgresql.pp b/manifests/database/postgresql.pp index 7470ea9..060fff3 100644 --- a/manifests/database/postgresql.pp +++ b/manifests/database/postgresql.pp @@ -1,92 +1,103 @@ # Class for creating the PuppetDB postgresql database. See README.md for more # information. class puppetdb::database::postgresql ( $listen_addresses = $puppetdb::params::database_host, $puppetdb_server = $puppetdb::params::puppetdb_server, $database_name = $puppetdb::params::database_name, $database_username = $puppetdb::params::database_username, $database_password = $puppetdb::params::database_password, $database_port = $puppetdb::params::database_port, $manage_database = $puppetdb::params::manage_database, $manage_server = $puppetdb::params::manage_dbserver, $manage_package_repo = $puppetdb::params::manage_pg_repo, $postgres_version = $puppetdb::params::postgres_version, $postgresql_ssl_on = $puppetdb::params::postgresql_ssl_on, $postgresql_ssl_key_path = $puppetdb::params::postgresql_ssl_key_path, $postgresql_ssl_cert_path = $puppetdb::params::postgresql_ssl_cert_path, $postgresql_ssl_ca_cert_path = $puppetdb::params::postgresql_ssl_ca_cert_path, $read_database_username = $puppetdb::params::read_database_username, - $read_database_password = $puppetdb::params::read_database_password + $read_database_password = $puppetdb::params::read_database_password, + $read_database_host = $puppetdb::params::read_database_host ) inherits puppetdb::params { if $manage_server { class { '::postgresql::globals': manage_package_repo => $manage_package_repo, version => $postgres_version, } # get the pg server up and running class { '::postgresql::server': ip_mask_allow_all_users => '0.0.0.0/0', listen_addresses => $listen_addresses, port => scanf($database_port, '%i')[0], } + # We need to create the ssl connection for the read user, when + # manage_database is set to true, or when read_database_host is defined. + # Otherwise we don't create it. + if $manage_database or $read_database_host != undef{ + $create_read_user_rule = true + } else { + $create_read_user_rule = false + } + # configure PostgreSQL communication with Puppet Agent SSL certificates if # postgresql_ssl_on is set to true if $postgresql_ssl_on { class { 'puppetdb::database::ssl_configuration': database_name => $database_name, database_username => $database_username, read_database_username => $read_database_username, puppetdb_server => $puppetdb_server, postgresql_ssl_key_path => $postgresql_ssl_key_path, postgresql_ssl_cert_path => $postgresql_ssl_cert_path, - postgresql_ssl_ca_cert_path => $postgresql_ssl_ca_cert_path + postgresql_ssl_ca_cert_path => $postgresql_ssl_ca_cert_path, + create_read_user_rule => $create_read_user_rule } } # Only install pg_trgm extension, if database it is actually managed by the module if $manage_database { # get the pg contrib to use pg_trgm extension class { '::postgresql::server::contrib': } postgresql::server::extension { 'pg_trgm': database => $database_name, require => Postgresql::Server::Db[$database_name], } } } if $manage_database { # create the puppetdb database postgresql::server::db { $database_name: user => $database_username, password => $database_password, grant => 'all', } -> postgresql_psql { 'revoke all access on public schema': db => $database_name, command => 'REVOKE CREATE ON SCHEMA public FROM public', unless => "SELECT * FROM (SELECT has_schema_privilege('public', 'public', 'create') can_create) privs WHERE privs.can_create=false", } -> postgresql_psql { "grant all permissions to ${database_username}": db => $database_name, command => "GRANT CREATE ON SCHEMA public TO \"${database_username}\"", unless => "SELECT * FROM (SELECT has_schema_privilege('${database_username}', 'public', 'create') can_create) privs WHERE privs.can_create=true", } -> puppetdb::database::read_only_user { $read_database_username: read_database_username => $read_database_username, database_name => $database_name, password_hash => postgresql::postgresql_password($read_database_username, $read_database_password), database_owner => $database_username } } } diff --git a/manifests/database/ssl_configuration.pp b/manifests/database/ssl_configuration.pp index 0953007..0d2473c 100644 --- a/manifests/database/ssl_configuration.pp +++ b/manifests/database/ssl_configuration.pp @@ -1,64 +1,68 @@ # Class for configuring SSL connection for the PuppetDB postgresql database. See README.md for more # information. class puppetdb::database::ssl_configuration ( $database_name = $puppetdb::params::database_name, $database_username = $puppetdb::params::database_username, $read_database_username = $puppetdb::params::read_database_username, + $read_database_host = $puppetdb::params::read_database_host, $puppetdb_server = $puppetdb::params::puppetdb_server, $postgresql_ssl_key_path = $puppetdb::params::postgresql_ssl_key_path, $postgresql_ssl_cert_path = $puppetdb::params::postgresql_ssl_cert_path, - $postgresql_ssl_ca_cert_path = $puppetdb::params::postgresql_ssl_ca_cert_path + $postgresql_ssl_ca_cert_path = $puppetdb::params::postgresql_ssl_ca_cert_path, + $create_read_user_rule = false, ) inherits puppetdb::params { File { ensure => present, owner => 'postgres', mode => '0600', require => Package['postgresql-server'], } file { 'postgres private key': path => "${postgresql::server::datadir}/server.key", source => $postgresql_ssl_key_path, } file { 'postgres public key': path => "${postgresql::server::datadir}/server.crt", source => $postgresql_ssl_cert_path, } postgresql::server::config_entry { 'ssl': ensure => present, value => 'on', require => [File['postgres private key'], File['postgres public key']] } postgresql::server::config_entry { 'ssl_cert_file': ensure => present, value => "${postgresql::server::datadir}/server.crt", require => [File['postgres private key'], File['postgres public key']] } postgresql::server::config_entry { 'ssl_key_file': ensure => present, value => "${postgresql::server::datadir}/server.key", require => [File['postgres private key'], File['postgres public key']] } postgresql::server::config_entry { 'ssl_ca_file': ensure => present, value => $postgresql_ssl_ca_cert_path, require => [File['postgres private key'], File['postgres public key']] } puppetdb::database::postgresql_ssl_rules { "Configure postgresql ssl rules for ${database_username}": database_name => $database_name, database_username => $database_username, puppetdb_server => $puppetdb_server, } - puppetdb::database::postgresql_ssl_rules { "Configure postgresql ssl rules for ${read_database_username}": - database_name => $database_name, - database_username => $read_database_username, - puppetdb_server => $puppetdb_server, + if $create_read_user_rule { + puppetdb::database::postgresql_ssl_rules { "Configure postgresql ssl rules for ${read_database_username}": + database_name => $database_name, + database_username => $read_database_username, + puppetdb_server => $puppetdb_server, + } } } diff --git a/manifests/init.pp b/manifests/init.pp index 0c55255..c999441 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,206 +1,207 @@ # All in one class for setting up a PuppetDB instance. See README.md for more # details. class puppetdb ( $listen_address = $puppetdb::params::listen_address, $listen_port = $puppetdb::params::listen_port, $disable_cleartext = $puppetdb::params::disable_cleartext, $open_listen_port = $puppetdb::params::open_listen_port, $ssl_listen_address = $puppetdb::params::ssl_listen_address, $ssl_listen_port = $puppetdb::params::ssl_listen_port, $disable_ssl = $puppetdb::params::disable_ssl, $open_ssl_listen_port = $puppetdb::params::open_ssl_listen_port, $ssl_dir = $puppetdb::params::ssl_dir, $ssl_set_cert_paths = $puppetdb::params::ssl_set_cert_paths, $ssl_cert_path = $puppetdb::params::ssl_cert_path, $ssl_key_path = $puppetdb::params::ssl_key_path, $ssl_key_pk8_path = $puppetdb::params::ssl_key_pk8_path, $ssl_ca_cert_path = $puppetdb::params::ssl_ca_cert_path, $ssl_deploy_certs = $puppetdb::params::ssl_deploy_certs, $ssl_key = $puppetdb::params::ssl_key, $ssl_cert = $puppetdb::params::ssl_cert, $ssl_ca_cert = $puppetdb::params::ssl_ca_cert, $ssl_protocols = $puppetdb::params::ssl_protocols, $postgresql_ssl_on = $puppetdb::params::postgresql_ssl_on, $postgresql_ssl_folder = $puppetdb::params::postgresql_ssl_folder, $postgresql_ssl_cert_path = $puppetdb::params::postgresql_ssl_cert_path, $postgresql_ssl_key_path = $puppetdb::params::postgresql_ssl_key_path, $postgresql_ssl_ca_cert_path = $puppetdb::params::postgresql_ssl_ca_cert_path, $cipher_suites = $puppetdb::params::cipher_suites, $migrate = $puppetdb::params::migrate, $manage_dbserver = $puppetdb::params::manage_dbserver, $manage_database = $puppetdb::params::manage_database, $manage_package_repo = $puppetdb::params::manage_pg_repo, $postgres_version = $puppetdb::params::postgres_version, $database = $puppetdb::params::database, $database_host = $puppetdb::params::database_host, $database_port = $puppetdb::params::database_port, $database_username = $puppetdb::params::database_username, $database_password = $puppetdb::params::database_password, $database_name = $puppetdb::params::database_name, $manage_db_password = $puppetdb::params::manage_db_password, $jdbc_ssl_properties = $puppetdb::params::jdbc_ssl_properties, $database_listen_address = $puppetdb::params::postgres_listen_addresses, $database_validate = $puppetdb::params::database_validate, $database_embedded_path = $puppetdb::params::database_embedded_path, $node_ttl = $puppetdb::params::node_ttl, $node_purge_ttl = $puppetdb::params::node_purge_ttl, $report_ttl = $puppetdb::params::report_ttl, Optional[Array] $facts_blacklist = $puppetdb::params::facts_blacklist, $gc_interval = $puppetdb::params::gc_interval, $node_purge_gc_batch_limit = $puppetdb::params::node_purge_gc_batch_limit, $log_slow_statements = $puppetdb::params::log_slow_statements, $conn_max_age = $puppetdb::params::conn_max_age, $conn_keep_alive = $puppetdb::params::conn_keep_alive, $conn_lifetime = $puppetdb::params::conn_lifetime, $puppetdb_package = $puppetdb::params::puppetdb_package, $puppetdb_service = $puppetdb::params::puppetdb_service, $puppetdb_service_status = $puppetdb::params::puppetdb_service_status, $puppetdb_user = $puppetdb::params::puppetdb_user, $puppetdb_group = $puppetdb::params::puppetdb_group, $puppetdb_server = $puppetdb::params::puppetdb_server, $read_database = $puppetdb::params::read_database, $read_database_host = $puppetdb::params::read_database_host, $read_database_port = $puppetdb::params::read_database_port, $read_database_username = $puppetdb::params::read_database_username, $read_database_password = $puppetdb::params::read_database_password, $read_database_name = $puppetdb::params::read_database_name, $manage_read_db_password = $puppetdb::params::manage_read_db_password, $read_database_jdbc_ssl_properties = $puppetdb::params::read_database_jdbc_ssl_properties, $read_database_validate = $puppetdb::params::read_database_validate, $read_log_slow_statements = $puppetdb::params::read_log_slow_statements, $read_conn_max_age = $puppetdb::params::read_conn_max_age, $read_conn_keep_alive = $puppetdb::params::read_conn_keep_alive, $read_conn_lifetime = $puppetdb::params::read_conn_lifetime, $confdir = $puppetdb::params::confdir, $vardir = $puppetdb::params::vardir, $manage_firewall = $puppetdb::params::manage_firewall, $java_args = $puppetdb::params::java_args, $merge_default_java_args = $puppetdb::params::merge_default_java_args, $max_threads = $puppetdb::params::max_threads, $command_threads = $puppetdb::params::command_threads, $concurrent_writes = $puppetdb::params::concurrent_writes, $store_usage = $puppetdb::params::store_usage, $temp_usage = $puppetdb::params::temp_usage, $disable_update_checking = $puppetdb::params::disable_update_checking, $certificate_whitelist_file = $puppetdb::params::certificate_whitelist_file, $certificate_whitelist = $puppetdb::params::certificate_whitelist, $database_max_pool_size = $puppetdb::params::database_max_pool_size, $read_database_max_pool_size = $puppetdb::params::read_database_max_pool_size, Boolean $automatic_dlo_cleanup = $puppetdb::params::automatic_dlo_cleanup, String[1] $cleanup_timer_interval = $puppetdb::params::cleanup_timer_interval, Integer[1] $dlo_max_age = $puppetdb::params::dlo_max_age, Optional[Stdlib::Absolutepath] $java_bin = $puppetdb::params::java_bin, ) inherits puppetdb::params { class { '::puppetdb::server': listen_address => $listen_address, listen_port => $listen_port, disable_cleartext => $disable_cleartext, open_listen_port => $open_listen_port, ssl_listen_address => $ssl_listen_address, ssl_listen_port => $ssl_listen_port, disable_ssl => $disable_ssl, open_ssl_listen_port => $open_ssl_listen_port, ssl_dir => $ssl_dir, ssl_set_cert_paths => $ssl_set_cert_paths, ssl_cert_path => $ssl_cert_path, ssl_key_path => $ssl_key_path, ssl_key_pk8_path => $ssl_key_pk8_path, ssl_ca_cert_path => $ssl_ca_cert_path, ssl_deploy_certs => $ssl_deploy_certs, ssl_key => $ssl_key, ssl_cert => $ssl_cert, ssl_ca_cert => $ssl_ca_cert, ssl_protocols => $ssl_protocols, postgresql_ssl_on => $postgresql_ssl_on, cipher_suites => $cipher_suites, migrate => $migrate, database => $database, database_host => $database_host, database_port => $database_port, database_username => $database_username, database_password => $database_password, database_name => $database_name, manage_db_password => $manage_db_password, jdbc_ssl_properties => $jdbc_ssl_properties, database_validate => $database_validate, database_embedded_path => $database_embedded_path, node_ttl => $node_ttl, node_purge_ttl => $node_purge_ttl, report_ttl => $report_ttl, facts_blacklist => $facts_blacklist, gc_interval => $gc_interval, node_purge_gc_batch_limit => $node_purge_gc_batch_limit, log_slow_statements => $log_slow_statements, conn_max_age => $conn_max_age, conn_keep_alive => $conn_keep_alive, conn_lifetime => $conn_lifetime, puppetdb_package => $puppetdb_package, puppetdb_service => $puppetdb_service, puppetdb_service_status => $puppetdb_service_status, confdir => $confdir, vardir => $vardir, java_args => $java_args, merge_default_java_args => $merge_default_java_args, max_threads => $max_threads, read_database => $read_database, read_database_host => $read_database_host, read_database_port => $read_database_port, read_database_username => $read_database_username, read_database_password => $read_database_password, read_database_name => $read_database_name, manage_read_db_password => $manage_read_db_password, read_database_jdbc_ssl_properties => $read_database_jdbc_ssl_properties, read_database_validate => $read_database_validate, read_log_slow_statements => $read_log_slow_statements, read_conn_max_age => $read_conn_max_age, read_conn_keep_alive => $read_conn_keep_alive, read_conn_lifetime => $read_conn_lifetime, puppetdb_user => $puppetdb_user, puppetdb_group => $puppetdb_group, manage_firewall => $manage_firewall, manage_database => $manage_database, command_threads => $command_threads, concurrent_writes => $concurrent_writes, store_usage => $store_usage, temp_usage => $temp_usage, disable_update_checking => $disable_update_checking, certificate_whitelist_file => $certificate_whitelist_file, certificate_whitelist => $certificate_whitelist, database_max_pool_size => $database_max_pool_size, read_database_max_pool_size => $read_database_max_pool_size, automatic_dlo_cleanup => $automatic_dlo_cleanup, cleanup_timer_interval => $cleanup_timer_interval, dlo_max_age => $dlo_max_age, java_bin => $java_bin, } if ($database == 'postgres') { $database_before = str2bool($database_validate) ? { false => Class['::puppetdb::server'], default => [Class['::puppetdb::server'], Class['::puppetdb::server::validate_db']], } class { '::puppetdb::database::postgresql': listen_addresses => $database_listen_address, database_name => $database_name, puppetdb_server => $puppetdb_server, database_username => $database_username, database_password => $database_password, database_port => $database_port, manage_server => $manage_dbserver, manage_database => $manage_database, manage_package_repo => $manage_package_repo, postgres_version => $postgres_version, postgresql_ssl_on => $postgresql_ssl_on, postgresql_ssl_key_path => $postgresql_ssl_key_path, postgresql_ssl_cert_path => $postgresql_ssl_cert_path, postgresql_ssl_ca_cert_path => $postgresql_ssl_ca_cert_path, read_database_username => $read_database_username, read_database_password => $read_database_password, + read_database_host => $read_database_host, before => $database_before } } } diff --git a/spec/unit/classes/database/ssl_configuration_spec.rb b/spec/unit/classes/database/ssl_configuration_spec.rb index 4a7e5d8..b5620df 100644 --- a/spec/unit/classes/database/ssl_configuration_spec.rb +++ b/spec/unit/classes/database/ssl_configuration_spec.rb @@ -1,170 +1,192 @@ require 'spec_helper' describe 'puppetdb::database::ssl_configuration', type: :class do context 'on a supported platform' do let(:facts) do { osfamily: 'RedHat', operatingsystem: 'RedHat', puppetversion: Puppet.version, operatingsystemrelease: '7.0', kernel: 'Linux', selinux: true, os: { family: 'RedHat', name: 'RedHat', release: { 'full' => '7.0', 'major' => '7' }, selinux: { 'enabled' => true }, }, fqdn: 'cheery-rime@puppet', } end let(:params) do { postgresql_ssl_key_path: '/puppet/ssl/key.pem', postgresql_ssl_cert_path: '/puppet/ssl/cert.pem', postgresql_ssl_ca_cert_path: '/puppet/cert/ca.pem', database_name: 'puppetdb', database_username: 'puppetdb', read_database_username: 'puppetdb-read', } end let(:identity_map) { "#{params[:database_name]}-#{params[:database_username]}-map" } let(:read_identity_map) { "#{params[:database_name]}-#{params[:read_database_username]}-map" } let(:datadir_path) { '/var/lib/data' } let(:pre_condition) do "class { 'postgresql::server': datadir => '/var/lib/data'} " end it { is_expected.to contain_class('puppetdb::database::ssl_configuration') } it { is_expected.to compile.with_all_deps } it 'has server.key file' do is_expected.to contain_file('postgres private key') .with( ensure: 'present', owner: 'postgres', mode: '0600', path: "#{datadir_path}/server.key", ) .that_requires('Package[postgresql-server]') end it 'has server.crt file' do is_expected.to contain_file('postgres public key') .with( ensure: 'present', owner: 'postgres', mode: '0600', path: "#{datadir_path}/server.crt", ) .that_requires('Package[postgresql-server]') end it 'has ssl config attribute' do is_expected.to contain_postgresql__server__config_entry('ssl') .with_value('on').with_ensure('present') .that_requires('File[postgres private key]') .that_requires('File[postgres public key]') end it 'has ssl_cert_file config attribute' do is_expected.to contain_postgresql__server__config_entry('ssl_cert_file') .with_value("#{datadir_path}/server.crt").with_ensure('present') .that_requires('File[postgres private key]') .that_requires('File[postgres public key]') end it 'has ssl_key_file config attribute' do is_expected.to contain_postgresql__server__config_entry('ssl_key_file') .with_value("#{datadir_path}/server.key").with_ensure('present') .that_requires('File[postgres private key]') .that_requires('File[postgres public key]') end it 'has ssl_ca_file config attribute' do is_expected.to contain_postgresql__server__config_entry('ssl_ca_file') .with_value(params[:postgresql_ssl_ca_cert_path]).with_ensure('present') .that_requires('File[postgres private key]') .that_requires('File[postgres public key]') end it 'has hba rule for puppetdb user ipv4' do is_expected.to contain_postgresql__server__pg_hba_rule("Allow certificate mapped connections to #{params[:database_name]} as #{params[:database_username]} (ipv4)") .with_type('hostssl') .with_database(params[:database_name]) .with_user(params[:database_username]) .with_address('0.0.0.0/0') .with_auth_method('cert') .with_order(0) .with_auth_option("map=#{identity_map} clientcert=1") end - it 'has hba rule for puppetdb-read user ipv4' do - is_expected.to contain_postgresql__server__pg_hba_rule("Allow certificate mapped connections to #{params[:database_name]} as #{params[:read_database_username]} (ipv4)") - .with_type('hostssl') - .with_database(params[:database_name]) - .with_user(params[:read_database_username]) - .with_address('0.0.0.0/0') - .with_auth_method('cert') - .with_order(0) - .with_auth_option("map=#{read_identity_map} clientcert=1") + it 'does not create hba rule for puppetdb-read user ipv4' do + is_expected.not_to contain_postgresql__server__pg_hba_rule("Allow certificate mapped connections to #{params[:database_name]} as #{params[:read_database_username]} (ipv4)") end it 'has hba rule for puppetdb user ipv6' do is_expected.to contain_postgresql__server__pg_hba_rule("Allow certificate mapped connections to #{params[:database_name]} as #{params[:database_username]} (ipv6)") .with_type('hostssl') .with_database(params[:database_name]) .with_user(params[:database_username]) .with_address('::0/0') .with_auth_method('cert') .with_order(0) .with_auth_option("map=#{identity_map} clientcert=1") end - it 'has hba rule for puppetdb-read user ipv6' do - is_expected.to contain_postgresql__server__pg_hba_rule("Allow certificate mapped connections to #{params[:database_name]} as #{params[:read_database_username]} (ipv6)") - .with_type('hostssl') - .with_database(params[:database_name]) - .with_user(params[:read_database_username]) - .with_address('::0/0') - .with_auth_method('cert') - .with_order(0) - .with_auth_option("map=#{read_identity_map} clientcert=1") + it 'does not create hba rule for puppetdb-read user ipv6' do + is_expected.not_to contain_postgresql__server__pg_hba_rule("Allow certificate mapped connections to #{params[:database_name]} as #{params[:read_database_username]} (ipv6)") end it 'has ident rule' do is_expected.to contain_postgresql__server__pg_ident_rule("Map the SSL certificate of the server as a #{params[:database_username]} user") .with_map_name(identity_map) .with_system_username(facts[:fqdn]) .with_database_username(params[:database_name]) end - it 'has read ident rule' do - is_expected.to contain_postgresql__server__pg_ident_rule("Map the SSL certificate of the server as a #{params[:read_database_username]} user") - .with_map_name(read_identity_map) - .with_system_username(facts[:fqdn]) - .with_database_username(params[:read_database_username]) + it 'does not create read ident rule' do + is_expected.not_to contain_postgresql__server__pg_ident_rule("Map the SSL certificate of the server as a #{params[:read_database_username]} user") end context 'when the puppetdb_server is set' do let(:params) do { puppetdb_server: 'puppetdb_fqdn', database_name: 'puppetdb', database_username: 'puppetdb', } end it 'has ident rule with the specified puppetdb_server host' do is_expected.to contain_postgresql__server__pg_ident_rule("Map the SSL certificate of the server as a #{params[:database_username]} user") .with_map_name(identity_map) .with_system_username(params[:puppetdb_server]) .with_database_username(params[:database_name]) end end + + context 'when the create_read_user_rule is set to true' do + let(:params) do + { + database_name: 'puppetdb', + read_database_username: 'puppetdb-read', + create_read_user_rule: true, + } + end + + it 'has hba rule for puppetdb-read user ipv4' do + is_expected.to contain_postgresql__server__pg_hba_rule("Allow certificate mapped connections to #{params[:database_name]} as #{params[:read_database_username]} (ipv4)") + .with_type('hostssl') + .with_database(params[:database_name]) + .with_user(params[:read_database_username]) + .with_address('0.0.0.0/0') + .with_auth_method('cert') + .with_order(0) + .with_auth_option("map=#{read_identity_map} clientcert=1") + end + + it 'has hba rule for puppetdb-read user ipv6' do + is_expected.to contain_postgresql__server__pg_hba_rule("Allow certificate mapped connections to #{params[:database_name]} as #{params[:read_database_username]} (ipv6)") + .with_type('hostssl') + .with_database(params[:database_name]) + .with_user(params[:read_database_username]) + .with_address('::0/0') + .with_auth_method('cert') + .with_order(0) + .with_auth_option("map=#{read_identity_map} clientcert=1") + end + + it 'has read ident rule' do + is_expected.to contain_postgresql__server__pg_ident_rule("Map the SSL certificate of the server as a #{params[:read_database_username]} user") + .with_map_name(read_identity_map) + .with_system_username(facts[:fqdn]) + .with_database_username(params[:read_database_username]) + end + end end end