Skip to content

Commit

Permalink
Add x509 authentication support for admin user
Browse files Browse the repository at this point in the history
  • Loading branch information
Johan De Wit committed Jul 18, 2023
1 parent 97f1c14 commit 2636d3f
Show file tree
Hide file tree
Showing 17 changed files with 308 additions and 280 deletions.
15 changes: 12 additions & 3 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -2026,12 +2027,20 @@ Default value: `undef`

##### <a name="-mongodb--server--admin_auth_mechanism"></a>`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`

##### <a name="-mongodb--server--admin_tls_key"></a>`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`

##### <a name="-mongodb--server--admin_update_password"></a>`admin_update_password`

Data type: `Boolean`
Expand Down Expand Up @@ -2137,7 +2146,7 @@ Database username.

##### <a name="-mongodb--db--auth_mechanism"></a>`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'.

Expand Down Expand Up @@ -2455,7 +2464,7 @@ The following parameters are available in the `mongodb_user` type.

##### <a name="-mongodb_user--auth_mechanism"></a>`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.

Expand Down
61 changes: 24 additions & 37 deletions lib/facter/is_master.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand All @@ -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
Expand Down
17 changes: 14 additions & 3 deletions lib/puppet/provider/mongodb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand All @@ -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'],
Expand Down Expand Up @@ -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

Expand Down
16 changes: 10 additions & 6 deletions lib/puppet/provider/mongodb_user/mongodb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 # rubocop:disable Naming/VariableNumber
command[:mechanisms] = ['SCRAM-SHA-256']
command[:pwd] = @resource[:password]
command[:digestPassword] = true
else
when :scram_sha_1 # rubocop:disable Naming/VariableNumber
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])
Expand Down Expand Up @@ -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

Expand Down
18 changes: 10 additions & 8 deletions lib/puppet/type/mongodb_user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ 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)
raise Puppet::Error, "Property 'password_hash' must be set. Use mongodb_password() for creating hash." if @resource[:auth_mechanism] != :x509 && @resource[:password].nil? && (@resource[:password].nil? && (provider.database == :absent))
end
newvalue(%r{^\w+$})

Expand Down Expand Up @@ -97,7 +97,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
Expand All @@ -122,12 +122,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
Expand Down
38 changes: 21 additions & 17 deletions manifests/db.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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}":
Expand Down
9 changes: 2 additions & 7 deletions manifests/mongos/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion manifests/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 2636d3f

Please sign in to comment.