From 92adff653fe1ffffe4cd896ade972b207ef01df3 Mon Sep 17 00:00:00 2001 From: Johan De Wit Date: Mon, 17 Jul 2023 11:39:07 +0200 Subject: [PATCH] Add x509 authentication support for admin user --- REFERENCE.md | 15 +- lib/facter/is_master.rb | 61 +++--- lib/puppet/provider/mongodb.rb | 17 +- lib/puppet/provider/mongodb_user/mongodb.rb | 16 +- lib/puppet/type/mongodb_user.rb | 20 +- manifests/db.pp | 38 ++-- manifests/mongos/params.pp | 9 +- manifests/params.pp | 1 - manifests/server.pp | 184 +++++++++--------- manifests/server/config.pp | 156 ++++++++------- spec/acceptance/mongos_spec.rb | 6 +- spec/acceptance/server_spec.rb | 6 +- spec/classes/mongos_spec.rb | 6 +- spec/classes/server_spec.rb | 6 +- .../provider/mongodb_user/mongodb_spec.rb | 7 +- templates/mongodb.conf.erb | 8 + templates/mongoshrc.js.erb | 7 +- 17 files changed, 298 insertions(+), 265 deletions(-) diff --git a/REFERENCE.md b/REFERENCE.md index 432691a6f..5518add2e 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1309,6 +1309,7 @@ The following parameters are available in the `mongodb::server` class: * [`admin_username`](#-mongodb--server--admin_username) * [`admin_password`](#-mongodb--server--admin_password) * [`admin_auth_mechanism`](#-mongodb--server--admin_auth_mechanism) +* [`admin_tls_key`](#-mongodb--server--admin_tls_key) * [`admin_update_password`](#-mongodb--server--admin_update_password) * [`admin_roles`](#-mongodb--server--admin_roles) * [`handle_creds`](#-mongodb--server--handle_creds) @@ -2026,12 +2027,20 @@ Default value: `undef` ##### `admin_auth_mechanism` -Data type: `Enum['scram_sha_1', 'scram_sha_256']` +Data type: `Enum['scram_sha_1', 'scram_sha_256', 'x509']` Administrator authentication mechanism. scram_sha_256 password synchronization verification is not supported. Default value: `$mongodb::params::admin_auth_mechanism` +##### `admin_tls_key` + +Data type: `Optional[Stdlib::Absolutepath]` + +Filepath of the administrators x509 certificate. Its the user of this class that needs to manage this certificate. + +Default value: `undef` + ##### `admin_update_password` Data type: `Boolean` @@ -2137,7 +2146,7 @@ Database username. ##### `auth_mechanism` -Data type: `Enum['scram_sha_1', 'scram_sha_256']` +Data type: `Enum['scram_sha_1', 'scram_sha_256', 'x509']` - Authentication mechanism. scram_sha_256 password verification is not supported. Defaults to 'scram_sha_1'. @@ -2455,7 +2464,7 @@ The following parameters are available in the `mongodb_user` type. ##### `auth_mechanism` -Valid values: `scram_sha_256`, `scram_sha_1` +Valid values: `scram_sha_256`, `scram_sha_1`, `x509` Authentication mechanism. Password verification is not supported with SCRAM-SHA-256. diff --git a/lib/facter/is_master.rb b/lib/facter/is_master.rb index 4c843df19..29e6b8fa5 100644 --- a/lib/facter/is_master.rb +++ b/lib/facter/is_master.rb @@ -8,7 +8,23 @@ def mongod_conf_file locations.find { |location| File.exist? location } end +def mongosh_conf_file + '/root/.mongosh.yaml' if File.exist?('/root/mongosh.yaml') +end + def get_options_from_hash_config(config) + # read also the mongoshrc.yaml yaml file, to retrieve the admins certkey file + if mongosh_conf_file + mongosh_config = YAML.load_file(mongosh_conf_file) + # check which tlscert we need to use + _tlscert = if config['setParameter'] && config['setParameter']['authenticationMechanisms'] == 'MONGODB-X509' && + mongosh_config['admin'] && mongosh_config['admin']['tlsCertificateKeyFile'] + mongosh_config['admin']['tlsCertificateKeyFile'] + end + else + _tlscert = config['net.tls.certificateKeyFile'] + end + result = [] result << "--port #{config['net.port']}" unless config['net.port'].nil? @@ -23,52 +39,23 @@ def get_options_from_hash_config(config) # - tlsMode is "requireTLS" # - Parameter --tlsCertificateKeyFile is set # - Parameter --tlsCAFile is set - result << "--tls --host #{Facter.value(:fqdn)}" if config['net.tls.mode'] == 'requireTLS' || !config['net.tls.certificateKeyFile'].nil? || !config['net.tls.CAFile'].nil? - result << "--tlsCertificateKeyFile #{config['net.tls.certificateKeyFile']}" unless config['net.tls.certificateKeyFile'].nil? + result << "--tls --host #{Facter.value(:fqdn)}" if config['net.tls.mode'] == 'requireTLS' || !_tlscert.nil? || !config['net.tls.CAFile'].nil? + result << "--tlsCertificateKeyFile #{_tlscert}" unless _tlscert.nil? result << "--tlsCAFile #{config['net.tls.CAFile']}" unless config['net.tls.CAFile'].nil? - result << '--ipv6' unless config['net.ipv6'].nil? - - result.join(' ') -end - -def get_options_from_keyvalue_config(file) - config = {} - File.readlines(file).map do |line| - k, v = line.split('=') - config[k.rstrip] = v.lstrip.chomp if k && v - end - - result = [] + # use --authenticationMechanism, ---authenticationDatabase + # when + # - authenticationMechanism MONGODB-X509 + result << "--authenticationDatabase '$external' --authenticationMechanism MONGODB-X509" unless config['setParameter'].nil? || config['setParameter']['authenticationMechanisms'] != 'MONGODB-X509' - result << "--port #{config['port']}" unless config['port'].nil? - # use --ssl and --host if: - # - sslMode is "requireSSL" - # - Parameter --sslPEMKeyFile is set - # - Parameter --sslCAFile is set - result << "--ssl --host #{Facter.value(:fqdn)}" if config['ssl'] == 'requireSSL' || !config['sslcert'].nil? || !config['sslca'].nil? - result << "--sslPEMKeyFile #{config['sslcert']}" unless config['sslcert'].nil? - result << "--sslCAFile #{config['sslca']}" unless config['sslca'].nil? - # use --tls and --host if: - # - tlsMode is "requireTLS" - # - Parameter --tlsCertificateKeyFile is set - # - Parameter --tlsCAFile is set - result << "--tls --host #{Facter.value(:fqdn)}" if config['tls'] == 'requireTLS' || !config['tlscert'].nil? || !config['tlsca'].nil? - result << "--tlsCertificateKeyFile #{config['tlscert']}" unless config['tlscert'].nil? - result << "--tlsCAFile #{config['tlsca']}" unless config['tlsca'].nil? - - result << '--ipv6' unless config['ipv6'].nil? + result << '--ipv6' unless config['net.ipv6'].nil? result.join(' ') end def get_options_from_config(file) config = YAML.load_file(file) - if config.is_a?(Hash) # Using a valid YAML file for mongo 2.6 - get_options_from_hash_config(config) - else # It has to be a key-value config file - get_options_from_keyvalue_config(file) - end + get_options_from_hash_config(config) end Facter.add('mongodb_is_master') do diff --git a/lib/puppet/provider/mongodb.rb b/lib/puppet/provider/mongodb.rb index a5d70554b..f7f8d78fc 100644 --- a/lib/puppet/provider/mongodb.rb +++ b/lib/puppet/provider/mongodb.rb @@ -29,6 +29,17 @@ def self.mongod_conf_file def self.mongo_conf config = YAML.load_file(mongod_conf_file) || {} + mongosh_config = {} + mongosh_config = YAML.load_file("#{Facter.value(:root_home)}/.mongosh.yaml") if File.file?("#{Facter.value(:root_home)}/.mongosh.yaml") + # determine if we need the tls for connecion or client + _tlscert = if config['setParameter'] && config['setParameter']['authenticationMechanisms'] == 'MONGODB-X509' + if mongosh_config['admin'] && mongosh_config['admin']['tlsCertificateKeyFile'] + mongosh_config['admin']['tlsCertificateKeyFile'] + else + config['net.tls.certificateKeyFile'] + end + end + { 'bindip' => config['net.bindIp'], 'port' => config['net.port'], @@ -39,7 +50,7 @@ def self.mongo_conf 'sslca' => config['net.ssl.CAFile'], 'tlsallowInvalidHostnames' => config['net.tls.allowInvalidHostnames'], 'tls' => config['net.tls.mode'], - 'tlscert' => config['net.tls.certificateKeyFile'], + 'tlscert' => _tlscert, 'tlsca' => config['net.tls.CAFile'], 'auth' => config['security.authorization'], 'shardsvr' => config['sharding.clusterRole'], @@ -92,15 +103,15 @@ def self.mongosh_cmd(db, host, cmd) if tls_is_enabled(config) args.push('--tls') - args += ['--tlsCertificateKeyFile', config['tlscert']] tls_ca = config['tlsca'] args += ['--tlsCAFile', tls_ca] unless tls_ca.nil? + args += ['--tlsCertificateKeyFile', config['tlscert']] args.push('--tlsAllowInvalidHostnames') if tls_invalid_hostnames(config) end - args += ['--eval', cmd] + args += ['--eval', "\"#{cmd}\""] mongosh(args) end diff --git a/lib/puppet/provider/mongodb_user/mongodb.rb b/lib/puppet/provider/mongodb_user/mongodb.rb index affb217b1..6fefd9f52 100644 --- a/lib/puppet/provider/mongodb_user/mongodb.rb +++ b/lib/puppet/provider/mongodb_user/mongodb.rb @@ -60,18 +60,22 @@ def create # is this still needed / we only support verion 4 and higher if mongo_4? || mongo_5? || mongo_6? - if @resource[:auth_mechanism] == :scram_sha_256 # rubocop:disable Naming/VariableNumber + case @resource[:auth_mechanism] + when :scram_sha_256 command[:mechanisms] = ['SCRAM-SHA-256'] command[:pwd] = @resource[:password] command[:digestPassword] = true - else + when :scram_sha_1 command[:mechanisms] = ['SCRAM-SHA-1'] command[:pwd] = password_hash command[:digestPassword] = false + when :x509 + command[:mechanisms] = ['MONGODB-X509'] + else + command[:pwd] = password_hash + command[:digestPassword] = false + end - else - command[:pwd] = password_hash - command[:digestPassword] = false end mongo_eval("db.runCommand(#{command.to_json})", @resource[:database]) @@ -120,7 +124,7 @@ def password=(value) digestPassword: true } - if mongo_4? || mongo_5? + if mongo_4? || mongo_5? || mongo_6? command[:mechanisms] = @resource[:auth_mechanism] == :scram_sha_256 ? ['SCRAM-SHA-256'] : ['SCRAM-SHA-1'] # rubocop:disable Naming/VariableNumber end diff --git a/lib/puppet/type/mongodb_user.rb b/lib/puppet/type/mongodb_user.rb index c585e7002..c8e31e981 100644 --- a/lib/puppet/type/mongodb_user.rb +++ b/lib/puppet/type/mongodb_user.rb @@ -57,7 +57,9 @@ def to_s?(value) newproperty(:password_hash) do desc 'The password hash of the user. Use mongodb_password() for creating hash. Only available on MongoDB 3.0 and later. SCRAM-SHA-256 authentication mechanism is not supported.' defaultto do - raise Puppet::Error, "Property 'password_hash' must be set. Use mongodb_password() for creating hash." if @resource[:password].nil? && (provider.database == :absent) + if @resource[:auth_mechanism] != :x509 && @resource[:password].nil? + raise Puppet::Error, "Property 'password_hash' must be set. Use mongodb_password() for creating hash." if @resource[:password].nil? && (provider.database == :absent) + end end newvalue(%r{^\w+$}) @@ -97,7 +99,7 @@ def insync?(_is) newparam(:auth_mechanism) do desc 'Authentication mechanism. Password verification is not supported with SCRAM-SHA-256.' defaultto :scram_sha_1 # rubocop:disable Naming/VariableNumber - newvalues(:scram_sha_256, :scram_sha_1) # rubocop:disable Naming/VariableNumber + newvalues(:scram_sha_256, :scram_sha_1, :x509) # rubocop:disable Naming/VariableNumber end newparam(:update_password, boolean: true) do @@ -122,12 +124,14 @@ def insync?(_is) end validate do - if self[:password_hash].nil? && self[:password].nil? && provider.password.nil? && provider.password_hash.nil? - err("Either 'password_hash' or 'password' should be provided") - elsif !self[:password_hash].nil? && !self[:password].nil? - err("Only one of 'password_hash' or 'password' should be provided") - elsif !self[:password_hash].nil? && self[:auth_mechanism] == :scram_sha_256 # rubocop:disable Naming/VariableNumber - err("'password_hash' is not supported with SCRAM-SHA-256 authentication mechanism") + if self[:auth_mechanism] != :x509 + if self[:password_hash].nil? && self[:password].nil? && provider.password.nil? && provider.password_hash.nil? + err("Either 'password_hash' or 'password' should be provided") + elsif !self[:password_hash].nil? && !self[:password].nil? + err("Only one of 'password_hash' or 'password' should be provided") + elsif !self[:password_hash].nil? && self[:auth_mechanism] == :scram_sha_256 # rubocop:disable Naming/VariableNumber + err("'password_hash' is not supported with SCRAM-SHA-256 authentication mechanism") + end end raise("The parameter 'scram_credentials' is read-only and cannot be changed") if should(:scram_credentials) end diff --git a/manifests/db.pp b/manifests/db.pp index 0c9dc64a8..58786a0a3 100644 --- a/manifests/db.pp +++ b/manifests/db.pp @@ -19,7 +19,7 @@ # define mongodb::db ( String $user, - Enum['scram_sha_1', 'scram_sha_256'] $auth_mechanism = 'scram_sha_1', + Enum['scram_sha_1', 'scram_sha_256', 'x509'] $auth_mechanism = 'scram_sha_1', String $db_name = $name, Optional[Variant[String[1], Sensitive[String[1]]]] $password_hash = undef, Optional[Variant[String[1], Sensitive[String[1]]]] $password = undef, @@ -33,25 +33,29 @@ tries => $tries, } - if $password_hash =~ Sensitive[String] { - $hash = $password_hash.unwrap - } elsif $password_hash { - $hash = $password_hash - } elsif $password { - $hash = mongodb_password($user, $password) - } else { - fail("Parameter 'password_hash' or 'password' should be provided to mongodb::db.") - } + if $auth_mechanism != 'x509' { + if $password_hash =~ Sensitive[String] { + $hash = $password_hash.unwrap + } elsif $password_hash { + $hash = $password_hash + } elsif $password { + $hash = mongodb_password($user, $password) + } else { + fail("Parameter 'password_hash' or 'password' should be provided to mongodb::db.") + } - if $auth_mechanism == 'scram_sha_256' { - $password_config = { - password => $password, - update_password => $update_password, + if $auth_mechanism == 'scram_sha_256' { + $password_config = { + password => $password, + update_password => $update_password, + } + } else { + $password_config = { + password_hash => $hash, + } } } else { - $password_config = { - password_hash => $hash, - } + $password_config = {} } mongodb_user { "User ${user} on db ${db_name}": diff --git a/manifests/mongos/params.pp b/manifests/mongos/params.pp index 7be61d997..6753d202f 100644 --- a/manifests/mongos/params.pp +++ b/manifests/mongos/params.pp @@ -5,13 +5,8 @@ $version = $mongodb::globals::version $package_ensure = pick($version, 'present') - if $manage_package { - $package_name = "mongodb-${mongodb::globals::edition}-mongos" - } elsif $facts['os']['family'] in ['RedHat', 'Suse'] { - $package_name = "mongodb-${mongodb::globals::edition}-mongos" - } else { - $package_name = 'mongodb-server' - } + # from versoin 4.4 on, package name is all the same in the upstream repositories + $package_name = "mongodb-${mongodb::globals::edition}-mongos" $config_content = undef $config_template = undef diff --git a/manifests/params.pp b/manifests/params.pp index 999752dd3..f0e1e8b8a 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -27,7 +27,6 @@ $manage_package = pick($mongodb::globals::manage_package, $mongodb::globals::manage_package_repo, false) $pidfilemode = pick($mongodb::globals::pidfilemode, '0644') $manage_pidfile = pick($mongodb::globals::manage_pidfile, true) - $version = $mongodb::globals::version $config_data = undef diff --git a/manifests/server.pp b/manifests/server.pp index 1aefdc9f1..1aed76b1c 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -313,6 +313,8 @@ # @param admin_auth_mechanism # Administrator authentication mechanism. scram_sha_256 password synchronization verification is not supported. # +# @param admin_tls_key +# Filepath of the administrators x509 certificate. Its the user of this class that needs to manage this certificate. # @param admin_update_password # Update password. Used with SCRAM-SHA-256 because password verification is not supported. # @@ -327,93 +329,94 @@ # Store admin credentials in mongoshrc.js file. Uses with create_admin parameter # class mongodb::server ( - Variant[Boolean, String] $ensure = $mongodb::params::ensure, - String $user = $mongodb::params::user, - String $group = $mongodb::params::group, - Stdlib::Absolutepath $config = $mongodb::params::config, - Stdlib::Absolutepath $dbpath = $mongodb::params::dbpath, - Boolean $dbpath_fix = $mongodb::params::dbpath_fix, - Optional[Stdlib::Absolutepath] $pidfilepath = $mongodb::params::pidfilepath, - String $pidfilemode = $mongodb::params::pidfilemode, - Boolean $manage_pidfile = $mongodb::params::manage_pidfile, - String $rcfile = $mongodb::params::rcfile, - Boolean $service_manage = $mongodb::params::service_manage, - Optional[String] $service_provider = $mongodb::params::service_provider, - Optional[String] $service_name = $mongodb::params::service_name, - Boolean $service_enable = $mongodb::params::service_enable, - Enum['stopped', 'running'] $service_ensure = $mongodb::params::service_ensure, - Optional[Enum['stopped', 'running']] $service_status = $mongodb::params::service_status, - Variant[Boolean, String] $package_ensure = $mongodb::params::package_ensure, - String $package_name = $mongodb::params::server_package_name, - Variant[Boolean, Stdlib::Absolutepath] $logpath = $mongodb::params::logpath, - Array[Stdlib::IP::Address] $bind_ip = $mongodb::params::bind_ip, - Optional[Boolean] $ipv6 = undef, - Boolean $logappend = true, - Optional[String] $system_logrotate = undef, - Optional[Boolean] $fork = $mongodb::params::fork, - Optional[Integer[1, 65535]] $port = undef, - Optional[Boolean] $journal = $mongodb::params::journal, - Optional[Boolean] $nojournal = undef, - Optional[Boolean] $smallfiles = undef, - Optional[Boolean] $cpu = undef, - Boolean $auth = false, - Optional[Boolean] $noauth = undef, - Optional[Boolean] $verbose = undef, - Optional[String] $verbositylevel = undef, - Optional[Boolean] $objcheck = undef, - Optional[Boolean] $quota = undef, - Optional[Integer] $quotafiles = undef, - Optional[Integer[0, 7]] $diaglog = undef, - Optional[Boolean] $directoryperdb = undef, - $profile = undef, - Optional[Integer] $maxconns = undef, - Optional[Integer] $oplog_size = undef, - $nohints = undef, - Optional[Boolean] $nohttpinterface = undef, - Optional[Boolean] $noscripting = undef, - Optional[Boolean] $notablescan = undef, - Optional[Boolean] $noprealloc = undef, - Optional[Integer] $nssize = undef, - $mms_token = undef, - $mms_name = undef, - $mms_interval = undef, - Optional[String] $replset = undef, - Optional[Hash] $replset_config = undef, - Optional[Array] $replset_members = undef, - Optional[Boolean] $configsvr = undef, - Optional[Boolean] $shardsvr = undef, - Optional[Boolean] $rest = undef, - Optional[Boolean] $quiet = undef, - Optional[Integer] $slowms = undef, - Optional[Stdlib::Absolutepath] $keyfile = undef, - Optional[Variant[String[6], Sensitive[String[6]]]] $key = undef, - Optional[Variant[String[1], Array[String[1]]]] $set_parameter = undef, - Optional[Boolean] $syslog = undef, - $config_content = undef, - Optional[String] $config_template = undef, - Optional[Hash] $config_data = undef, - Optional[Boolean] $ssl = undef, - Optional[Stdlib::Absolutepath] $ssl_key = undef, - Optional[Stdlib::Absolutepath] $ssl_ca = undef, - Boolean $ssl_weak_cert = false, - Boolean $ssl_invalid_hostnames = false, - Enum['requireSSL', 'preferSSL', 'allowSSL'] $ssl_mode = 'requireSSL', - Boolean $tls = false, - Optional[Stdlib::Absolutepath] $tls_key = undef, - Optional[Stdlib::Absolutepath] $tls_ca = undef, - Boolean $tls_conn_without_cert = false, - Boolean $tls_invalid_hostnames = false, - Enum['requireTLS', 'preferTLS', 'allowTLS'] $tls_mode = 'requireTLS', - Boolean $restart = $mongodb::params::restart, - Optional[String] $storage_engine = undef, - Boolean $create_admin = $mongodb::params::create_admin, - String $admin_username = $mongodb::params::admin_username, - Optional[Variant[String, Sensitive[String]]] $admin_password = undef, - Enum['scram_sha_1', 'scram_sha_256'] $admin_auth_mechanism = $mongodb::params::admin_auth_mechanism, - Boolean $admin_update_password = false, - Boolean $handle_creds = $mongodb::params::handle_creds, - Boolean $store_creds = $mongodb::params::store_creds, - Array $admin_roles = $mongodb::params::admin_roles, + Variant[Boolean, String] $ensure = $mongodb::params::ensure, + String $user = $mongodb::params::user, + String $group = $mongodb::params::group, + Stdlib::Absolutepath $config = $mongodb::params::config, + Stdlib::Absolutepath $dbpath = $mongodb::params::dbpath, + Boolean $dbpath_fix = $mongodb::params::dbpath_fix, + Optional[Stdlib::Absolutepath] $pidfilepath = $mongodb::params::pidfilepath, + String $pidfilemode = $mongodb::params::pidfilemode, + Boolean $manage_pidfile = $mongodb::params::manage_pidfile, + String $rcfile = $mongodb::params::rcfile, + Boolean $service_manage = $mongodb::params::service_manage, + Optional[String] $service_provider = $mongodb::params::service_provider, + Optional[String] $service_name = $mongodb::params::service_name, + Boolean $service_enable = $mongodb::params::service_enable, + Enum['stopped', 'running'] $service_ensure = $mongodb::params::service_ensure, + Optional[Enum['stopped', 'running']] $service_status = $mongodb::params::service_status, + Variant[Boolean, String] $package_ensure = $mongodb::params::package_ensure, + String $package_name = $mongodb::params::server_package_name, + Variant[Boolean, Stdlib::Absolutepath] $logpath = $mongodb::params::logpath, + Array[Stdlib::IP::Address] $bind_ip = $mongodb::params::bind_ip, + Optional[Boolean] $ipv6 = undef, + Boolean $logappend = true, + Optional[String] $system_logrotate = undef, + Optional[Boolean] $fork = $mongodb::params::fork, + Optional[Integer[1, 65535]] $port = undef, + Optional[Boolean] $journal = $mongodb::params::journal, + Optional[Boolean] $nojournal = undef, + Optional[Boolean] $smallfiles = undef, + Optional[Boolean] $cpu = undef, + Boolean $auth = false, + Optional[Boolean] $noauth = undef, + Optional[Boolean] $verbose = undef, + Optional[String] $verbositylevel = undef, + Optional[Boolean] $objcheck = undef, + Optional[Boolean] $quota = undef, + Optional[Integer] $quotafiles = undef, + Optional[Integer[0, 7]] $diaglog = undef, + Optional[Boolean] $directoryperdb = undef, + $profile = undef, + Optional[Integer] $maxconns = undef, + Optional[Integer] $oplog_size = undef, + $nohints = undef, + Optional[Boolean] $nohttpinterface = undef, + Optional[Boolean] $noscripting = undef, + Optional[Boolean] $notablescan = undef, + Optional[Boolean] $noprealloc = undef, + Optional[Integer] $nssize = undef, + $mms_token = undef, + $mms_name = undef, + $mms_interval = undef, + Optional[String] $replset = undef, + Optional[Hash] $replset_config = undef, + Optional[Array] $replset_members = undef, + Optional[Boolean] $configsvr = undef, + Optional[Boolean] $shardsvr = undef, + Optional[Boolean] $rest = undef, + Optional[Boolean] $quiet = undef, + Optional[Integer] $slowms = undef, + Optional[Stdlib::Absolutepath] $keyfile = undef, + Optional[Variant[String[6], Sensitive[String[6]]]] $key = undef, + Optional[Variant[String[1], Array[String[1]]]] $set_parameter = undef, + Optional[Boolean] $syslog = undef, + $config_content = undef, + Optional[String] $config_template = undef, + Optional[Hash] $config_data = undef, + Optional[Boolean] $ssl = undef, + Optional[Stdlib::Absolutepath] $ssl_key = undef, + Optional[Stdlib::Absolutepath] $ssl_ca = undef, + Boolean $ssl_weak_cert = false, + Boolean $ssl_invalid_hostnames = false, + Enum['requireSSL', 'preferSSL', 'allowSSL'] $ssl_mode = 'requireSSL', + Boolean $tls = false, + Optional[Stdlib::Absolutepath] $tls_key = undef, + Optional[Stdlib::Absolutepath] $tls_ca = undef, + Boolean $tls_conn_without_cert = false, + Boolean $tls_invalid_hostnames = false, + Enum['requireTLS', 'preferTLS', 'allowTLS'] $tls_mode = 'requireTLS', + Boolean $restart = $mongodb::params::restart, + Optional[String] $storage_engine = undef, + Boolean $create_admin = $mongodb::params::create_admin, + String $admin_username = $mongodb::params::admin_username, + Optional[Variant[String, Sensitive[String]]] $admin_password = undef, + Enum['scram_sha_1', 'scram_sha_256', 'x509'] $admin_auth_mechanism = $mongodb::params::admin_auth_mechanism, + Optional[Stdlib::Absolutepath] $admin_tls_key = undef, + Boolean $admin_update_password = false, + Boolean $handle_creds = $mongodb::params::handle_creds, + Boolean $store_creds = $mongodb::params::store_creds, + Array $admin_roles = $mongodb::params::admin_roles, ) inherits mongodb::params { contain mongodb::server::install contain mongodb::server::config @@ -438,6 +441,13 @@ } else { $admin_password } + + # using x509, we need the admin clent certificate in the parameter --tlsCertificateKeyFile + # there is no way where we can set this in neither the /etc/momgosh.yaml or the /etc/mongod.conf + # The mongodb provider reads in /etc/mongod.conf setParameters.authenticationMechanisms: MONGODB-X509 settings + # to determine that a client cert authentication is used. There is no setting to set the client cert to be used. + # so we store it in a file in roots home directory. (this is done in mongodb::server::config + if $create_admin and ($service_ensure == 'running' or $service_ensure == true) { mongodb::db { 'admin': user => $admin_username, diff --git a/manifests/server/config.pp b/manifests/server/config.pp index 6521db643..e94a4374d 100644 --- a/manifests/server/config.pp +++ b/manifests/server/config.pp @@ -1,78 +1,80 @@ # PRIVATE CLASS: do not call directly class mongodb::server::config { - $ensure = $mongodb::server::ensure - $user = $mongodb::server::user - $group = $mongodb::server::group - $config = $mongodb::server::config - $config_content = $mongodb::server::config_content - $config_template = $mongodb::server::config_template - $config_data = $mongodb::server::config_data - $dbpath = $mongodb::server::dbpath - $dbpath_fix = $mongodb::server::dbpath_fix - $pidfilepath = $mongodb::server::pidfilepath - $pidfilemode = $mongodb::server::pidfilemode - $manage_pidfile = $mongodb::server::manage_pidfile - $logpath = $mongodb::server::logpath - $logappend = $mongodb::server::logappend - $system_logrotate = $mongodb::server::system_logrotate - $fork = $mongodb::server::fork - $port = $mongodb::server::port - $journal = $mongodb::server::journal - $nojournal = $mongodb::server::nojournal - $smallfiles = $mongodb::server::smallfiles - $cpu = $mongodb::server::cpu - $auth = $mongodb::server::auth - $noath = $mongodb::server::noauth - $create_admin = $mongodb::server::create_admin - $admin_username = $mongodb::server::admin_username - $admin_password = $mongodb::server::admin_password - $handle_creds = $mongodb::server::handle_creds - $store_creds = $mongodb::server::store_creds - $rcfile = $mongodb::server::rcfile - $verbose = $mongodb::server::verbose - $verbositylevel = $mongodb::server::verbositylevel - $objcheck = $mongodb::server::objcheck - $quota = $mongodb::server::quota - $quotafiles = $mongodb::server::quotafiles - $diaglog = $mongodb::server::diaglog - $oplog_size = $mongodb::server::oplog_size - $nohints = $mongodb::server::nohints - $nohttpinterface = $mongodb::server::nohttpinterface - $noscripting = $mongodb::server::noscripting - $notablescan = $mongodb::server::notablescan - $noprealloc = $mongodb::server::noprealloc - $nssize = $mongodb::server::nssize - $mms_token = $mongodb::server::mms_token - $mms_name = $mongodb::server::mms_name - $mms_interval = $mongodb::server::mms_interval - $configsvr = $mongodb::server::configsvr - $shardsvr = $mongodb::server::shardsvr - $replset = $mongodb::server::replset - $rest = $mongodb::server::rest - $quiet = $mongodb::server::quiet - $slowms = $mongodb::server::slowms - $keyfile = $mongodb::server::keyfile - $key = $mongodb::server::key - $ipv6 = $mongodb::server::ipv6 - $bind_ip = $mongodb::server::bind_ip - $directoryperdb = $mongodb::server::directoryperdb - $profile = $mongodb::server::profile - $maxconns = $mongodb::server::maxconns - $set_parameter = $mongodb::server::set_parameter - $syslog = $mongodb::server::syslog - $ssl = $mongodb::server::ssl - $ssl_key = $mongodb::server::ssl_key - $ssl_ca = $mongodb::server::ssl_ca - $ssl_weak_cert = $mongodb::server::ssl_weak_cert + $ensure = $mongodb::server::ensure + $user = $mongodb::server::user + $group = $mongodb::server::group + $config = $mongodb::server::config + $config_content = $mongodb::server::config_content + $config_template = $mongodb::server::config_template + $config_data = $mongodb::server::config_data + $dbpath = $mongodb::server::dbpath + $dbpath_fix = $mongodb::server::dbpath_fix + $pidfilepath = $mongodb::server::pidfilepath + $pidfilemode = $mongodb::server::pidfilemode + $manage_pidfile = $mongodb::server::manage_pidfile + $logpath = $mongodb::server::logpath + $logappend = $mongodb::server::logappend + $system_logrotate = $mongodb::server::system_logrotate + $fork = $mongodb::server::fork + $port = $mongodb::server::port + $journal = $mongodb::server::journal + $nojournal = $mongodb::server::nojournal + $smallfiles = $mongodb::server::smallfiles + $cpu = $mongodb::server::cpu + $auth = $mongodb::server::auth + $noath = $mongodb::server::noauth + $create_admin = $mongodb::server::create_admin + $admin_username = $mongodb::server::admin_username + $admin_password = $mongodb::server::admin_password + $admin_auth_mechanism = $mongodb::server::admin_auth_mechanism + $admin_tls_key = $mongodb::server::admin_tls_key + $handle_creds = $mongodb::server::handle_creds + $store_creds = $mongodb::server::store_creds + $rcfile = $mongodb::server::rcfile + $verbose = $mongodb::server::verbose + $verbositylevel = $mongodb::server::verbositylevel + $objcheck = $mongodb::server::objcheck + $quota = $mongodb::server::quota + $quotafiles = $mongodb::server::quotafiles + $diaglog = $mongodb::server::diaglog + $oplog_size = $mongodb::server::oplog_size + $nohints = $mongodb::server::nohints + $nohttpinterface = $mongodb::server::nohttpinterface + $noscripting = $mongodb::server::noscripting + $notablescan = $mongodb::server::notablescan + $noprealloc = $mongodb::server::noprealloc + $nssize = $mongodb::server::nssize + $mms_token = $mongodb::server::mms_token + $mms_name = $mongodb::server::mms_name + $mms_interval = $mongodb::server::mms_interval + $configsvr = $mongodb::server::configsvr + $shardsvr = $mongodb::server::shardsvr + $replset = $mongodb::server::replset + $rest = $mongodb::server::rest + $quiet = $mongodb::server::quiet + $slowms = $mongodb::server::slowms + $keyfile = $mongodb::server::keyfile + $key = $mongodb::server::key + $ipv6 = $mongodb::server::ipv6 + $bind_ip = $mongodb::server::bind_ip + $directoryperdb = $mongodb::server::directoryperdb + $profile = $mongodb::server::profile + $maxconns = $mongodb::server::maxconns + $set_parameter = $mongodb::server::set_parameter + $syslog = $mongodb::server::syslog + $ssl = $mongodb::server::ssl + $ssl_key = $mongodb::server::ssl_key + $ssl_ca = $mongodb::server::ssl_ca + $ssl_weak_cert = $mongodb::server::ssl_weak_cert $ssl_invalid_hostnames = $mongodb::server::ssl_invalid_hostnames - $ssl_mode = $mongodb::server::ssl_mode - $tls = $mongodb::server::tls - $tls_key = $mongodb::server::tls_key - $tls_ca = $mongodb::server::tls_ca + $ssl_mode = $mongodb::server::ssl_mode + $tls = $mongodb::server::tls + $tls_key = $mongodb::server::tls_key + $tls_ca = $mongodb::server::tls_ca $tls_conn_without_cert = $mongodb::server::tls_conn_without_cert $tls_invalid_hostnames = $mongodb::server::tls_invalid_hostnames - $tls_mode = $mongodb::server::tls_mode - $storage_engine = $mongodb::server::storage_engine + $tls_mode = $mongodb::server::tls_mode + $storage_engine = $mongodb::server::storage_engine File { owner => $user, @@ -126,6 +128,20 @@ mode => '0644', } + # TODO: we kind of use this file to force x509 autehntication in the providers when it exsists + # Open for suugestions how to deal with this + if $admin_auth_mechanism == 'x509' { + $_ensure = 'present' + } else { + $_ensure = 'absent' + } + + file { '/root/.mongosh.yaml': + ensure => $_ensure, + mode => '0400', + content => "---\n${admin_username}:\n tlsCertificateKeyFile: ${admin_tls_key}", + } + file { $dbpath: ensure => directory, mode => '0750', diff --git a/spec/acceptance/mongos_spec.rb b/spec/acceptance/mongos_spec.rb index 62ac7bf2f..05ec1a1e9 100644 --- a/spec/acceptance/mongos_spec.rb +++ b/spec/acceptance/mongos_spec.rb @@ -5,11 +5,7 @@ describe 'mongodb::mongos class' do case fact('osfamily') when 'Debian' - package_name = if fact('os.distro.codename') =~ %r{^(buster|bullseye)$} - 'mongodb-org-server' - else - 'mongodb-server' - end + package_name = 'mongodb-org-server' config_file = '/etc/mongodb-shard.conf' else package_name = 'mongodb-org-server' diff --git a/spec/acceptance/server_spec.rb b/spec/acceptance/server_spec.rb index cbe13778a..984b3a7b8 100644 --- a/spec/acceptance/server_spec.rb +++ b/spec/acceptance/server_spec.rb @@ -15,11 +15,7 @@ else 'mongodb' end - package_name = if fact('os.distro.codename') =~ %r{^(buster)$} - 'mongodb-org-server' - else - 'mongodb-server' - end + package_name = 'mongodb-org-server' else config_file = '/etc/mongod.conf' service_name = 'mongod' diff --git a/spec/classes/mongos_spec.rb b/spec/classes/mongos_spec.rb index 002280cf5..4cb9acd6d 100644 --- a/spec/classes/mongos_spec.rb +++ b/spec/classes/mongos_spec.rb @@ -9,11 +9,7 @@ case facts[:os]['family'] when 'Debian' - package_name = if facts[:os]['release']['major'] =~ %r{(10)} - 'mongodb-org-mongos' - else - 'mongodb-server' - end + package_name = 'mongodb-org-mongos' config_file = '/etc/mongodb-shard.conf' else package_name = 'mongodb-org-mongos' diff --git a/spec/classes/server_spec.rb b/spec/classes/server_spec.rb index ad131ccf2..f86e78298 100644 --- a/spec/classes/server_spec.rb +++ b/spec/classes/server_spec.rb @@ -46,11 +46,7 @@ describe 'with defaults' do it_behaves_like 'server classes' - if facts[:os]['family'] == 'RedHat' || facts[:os]['family'] == 'Suse' || facts[:os]['release']['major'] =~ %r{(10)} - it { is_expected.to contain_package('mongodb_server').with_ensure('present').with_name('mongodb-org-server').with_tag('mongodb_package') } - else - it { is_expected.to contain_package('mongodb_server').with_ensure('present').with_name('mongodb-server').with_tag('mongodb_package') } - end + it { is_expected.to contain_package('mongodb_server').with_ensure('present').with_name('mongodb-org-server').with_tag('mongodb_package') } it do is_expected.to contain_file(config_file). diff --git a/spec/unit/puppet/provider/mongodb_user/mongodb_spec.rb b/spec/unit/puppet/provider/mongodb_user/mongodb_spec.rb index 803a03fd7..61b318e82 100644 --- a/spec/unit/puppet/provider/mongodb_user/mongodb_spec.rb +++ b/spec/unit/puppet/provider/mongodb_user/mongodb_spec.rb @@ -7,7 +7,7 @@ describe Puppet::Type.type(:mongodb_user).provider(:mongodb) do let(:raw_users) do [ - { '_id' => 'admin.root', 'user' => 'root', 'db' => 'admin', 'credentials' => { 'MONGODB-CR' => 'pass', 'SCRAM-SHA-1' => { 'iterationCount' => 10_000, 'salt' => 'salt', 'storedKey' => 'storedKey', 'serverKey' => 'serverKey' } }, 'roles' => [{ 'role' => 'role2', 'db' => 'admin' }, { 'role' => 'role3', 'db' => 'user_database' }, { 'role' => 'role1', 'db' => 'admin' }] } + { '_id' => 'admin.root', 'user' => 'root', 'db' => 'admin', 'credentials' => { 'MONGODB-CR' => 'pass', 'SCRAM-SHA-1' => { 'iterationCount' => 10_000, 'salt' => 'salt', 'storedKey' => 'storedKey', 'serverKey' => 'serverKey' } }, 'roles' => [{ 'role' => 'role1', 'db' => 'admin' }, { 'role' => 'role3', 'db' => 'user_database' }, { 'role' => 'role2', 'db' => 'other_database' },] } ].to_json end @@ -33,7 +33,7 @@ mongodconffile = tmp.path allow(provider.class).to receive(:mongod_conf_file).and_return(mongodconffile) allow(provider.class).to receive(:mongo_eval).with('EJSON.stringify(db.system.users.find().toArray())').and_return(raw_users) - allow(provider.class).to receive(:mongo_version).and_return('2.6.x') + allow(provider.class).to receive(:mongo_version).and_return('6.0.x') allow(provider.class).to receive(:db_ismaster).and_return(true) end @@ -58,6 +58,7 @@ "createUser":"new_user", "customData":{"createdBy":"Puppet Mongodb_user['new_user']"}, "roles":[{"role":"role1","db":"new_database"},{"role":"role2","db":"other_database"}], + "mechanisms":["SCRAM-SHA-1"], "pwd":"pass", "digestPassword":false } @@ -119,7 +120,7 @@ describe 'roles' do it 'returns a sorted roles' do - expect(instance.roles).to eq(%w[role1 role2 role3@user_database]) + expect(instance.roles).to eq(%w[role1 role2@other_database role3@user_database]) end end diff --git a/templates/mongodb.conf.erb b/templates/mongodb.conf.erb index 5c846a254..cf657eabe 100644 --- a/templates/mongodb.conf.erb +++ b/templates/mongodb.conf.erb @@ -169,6 +169,14 @@ setParameter: <%= v %> <% end -%> <% end -%> +<% if @admin_auth_mechanism == 'x509' -%> +<%# setParameters.auth... gives an error on startup status=2/INVALIDARGUMENT -%> +<% if !@set_parameter -%> +setParameter: +<% end -%> + authenticationMechanisms: MONGODB-X509 +<% end -%> + <% if @config_data -%> <% @config_data.each do |k,v| -%> diff --git a/templates/mongoshrc.js.erb b/templates/mongoshrc.js.erb index bf627b962..4027c7115 100644 --- a/templates/mongoshrc.js.erb +++ b/templates/mongoshrc.js.erb @@ -30,10 +30,10 @@ function authRequired() { } if (authRequired()) { - <%- if @replset -%> - // rs.slaveOk has been deprecated, use secondaryOk if available + <%- if @admin_auth_mechanism != 'x509' -%> + <%- if @replset -%> db.getMongo().setReadPref('primaryPreferred') - <%- end -%> + <%- end -%> try { var prev_db = db.getName() db = db.getSiblingDB('admin') @@ -44,5 +44,6 @@ if (authRequired()) { // This isn't catching authentication errors as I'd expect... throw(err) } + <%- end -%> } <% end -%>