Skip to content

Use Puppet-Datatype Sensitive for Passwords #1279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .sync.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
---
".gitlab-ci.yml":
delete: true
.puppet-lint.rc:
extra_disabled_lint_checks:
- 140chars-check
appveyor.yml:
delete: true

Expand Down
24 changes: 16 additions & 8 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@ The following parameters are available in the `postgresql::server` class:

##### <a name="postgres_password"></a>`postgres_password`

Data type: `Any`
Data type: `Variant[String, Sensitive[String]]`

Sets the password for the postgres user to your specified value. By default, this setting uses the superuser account in the Postgres database, with a user called postgres and no password.

Expand Down Expand Up @@ -1646,7 +1646,7 @@ User to create and assign access to the database upon creation. Mandatory.

##### <a name="password"></a>`password`

Data type: `Any`
Data type: `Variant[String, Sensitive[String]]`

Required Sets the password for the created user.

Expand Down Expand Up @@ -2571,7 +2571,7 @@ Default value: ``true``

##### <a name="password_hash"></a>`password_hash`

Data type: `Any`
Data type: `Variant[String, Sensitive[String]]`

Sets the hash to use during password creation.

Expand Down Expand Up @@ -2713,7 +2713,7 @@ Create a new schema.

#### Examples

#####
#####

```puppet
postgresql::server::schema {'private':
Expand Down Expand Up @@ -2944,7 +2944,7 @@ Default value: ``undef``

##### <a name="database_password"></a>`database_password`

Data type: `Any`
Data type: `Variant[String, Sensitive[String]]`

Specifies the password to connect with.

Expand Down Expand Up @@ -3106,6 +3106,8 @@ The name of the database you are trying to validate a connection with.

##### <a name="db_password"></a>`db_password`

Data type: `Variant[String, Sensitive[String]]`

The password required to access the target PostgreSQL database.

##### <a name="db_username"></a>`db_username`
Expand Down Expand Up @@ -3319,7 +3321,7 @@ This function pull default values from the `params` class or `globals` class if

#### Examples

#####
#####

```puppet
postgresql::default('variable')
Expand All @@ -3333,7 +3335,7 @@ Returns: `Any`

##### Examples

######
######

```puppet
postgresql::default('variable')
Expand Down Expand Up @@ -3369,7 +3371,7 @@ Type: Ruby 4.x API

This function returns the postgresql password hash from the clear text username / password

#### `postgresql::postgresql_password(Variant[String[1],Integer] $username, Variant[String[1],Integer] $password)`
#### `postgresql::postgresql_password(Variant[String[1],Integer] $username, Variant[String[1], Sensitive[String[1]], Integer] $password)`

The postgresql::postgresql_password function.

Expand All @@ -3387,6 +3389,12 @@ Data type: `Variant[String[1],Integer]`

The clear text `password`

##### `sensitive`

Data type: `Boolean`

If the Postgresql-Passwordhash should be of Datatype Sensitive[String]

### <a name="postgresql_escape"></a>`postgresql_escape`

Type: Ruby 4.x API
Expand Down
20 changes: 15 additions & 5 deletions lib/puppet/functions/postgresql/postgresql_password.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@
# The clear text `username`
# @param password
# The clear text `password`
# @param sensitive
# If the Postgresql-Passwordhash should be of Datatype Sensitive[String]
#
# @return [String]
# @return
# The postgresql password hash from the clear text username / password.
dispatch :default_impl do
param 'Variant[String[1],Integer]', :username
param 'Variant[String[1],Integer]', :password
required_param 'Variant[String[1], Integer]', :username
required_param 'Variant[String[1], Sensitive[String[1]], Integer]', :password
optional_param 'Boolean', :sensitive
return_type 'Variant[String, Sensitive[String]]'
end

def default_impl(username, password)
'md5' + Digest::MD5.hexdigest(password.to_s + username.to_s)
def default_impl(username, password, sensitive = false)
password = password.unwrap if password.respond_to?(:unwrap)
result_string = 'md5' + Digest::MD5.hexdigest(password.to_s + username.to_s)
if sensitive
Puppet::Pops::Types::PSensitiveType::Sensitive.new(result_string)
else
result_string
end
end
end
2 changes: 1 addition & 1 deletion manifests/server.pp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
# @param extra_systemd_config Adds extra config to systemd config file, can for instance be used to add extra openfiles. This can be a multi line string
#
class postgresql::server (
$postgres_password = undef,
Optional[Variant[String[1], Sensitive[String[1]], Integer]] $postgres_password = undef,

$package_name = $postgresql::params::server_package_name,
$package_ensure = $postgresql::params::package_ensure,
Expand Down
4 changes: 2 additions & 2 deletions manifests/server/db.pp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# @summary Define for conveniently creating a role, database and assigning the correctpermissions.
#
#
# @param user User to create and assign access to the database upon creation. Mandatory.
# @param password Required Sets the password for the created user.
# @param comment Defines a comment to be stored about the database using the PostgreSQL COMMENT command.
Expand All @@ -13,7 +13,7 @@
# @param owner Sets a user as the owner of the database.
define postgresql::server::db (
$user,
$password,
Variant[String, Sensitive[String]] $password,
$comment = undef,
$dbname = $title,
$encoding = $postgresql::server::encoding,
Expand Down
2 changes: 1 addition & 1 deletion manifests/server/default_privileges.pp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
psql_group => $group,
psql_path => $psql_path,
unless => $unless_cmd,
environment => "PGOPTIONS=--client-min-messages=error"
environment => 'PGOPTIONS=--client-min-messages=error'
}

if($role != undef and defined(Postgresql::Server::Role[$role])) {
Expand Down
7 changes: 6 additions & 1 deletion manifests/server/passwd.pp
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# @api private
class postgresql::server::passwd {
$postgres_password = $postgresql::server::postgres_password
$postgres_password = if $postgresql::server::postgres_password =~ Sensitive {
$postgresql::server::postgres_password.unwrap
} else {
$postgresql::server::postgres_password
}

$user = $postgresql::server::user
$group = $postgresql::server::group
$psql_path = $postgresql::server::psql_path
Expand Down
33 changes: 19 additions & 14 deletions manifests/server/role.pp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# @param module_workdir Specifies working directory under which the psql command should be executed. May need to specify if '/tmp' is on volume mounted with noexec option.
define postgresql::server::role (
$update_password = true,
$password_hash = false,
Variant[Boolean, String, Sensitive[String]] $password_hash = false,
$createdb = false,
$createrole = false,
$db = $postgresql::server::default_database,
Expand All @@ -38,6 +38,11 @@
$module_workdir = $postgresql::server::module_workdir,
Enum['present', 'absent'] $ensure = 'present',
) {
$password_hash_unsensitive = if $password_hash =~ Sensitive[String] {
$password_hash.unwrap
} else {
$password_hash
}
#
# Port, order of precedence: $port parameter, $connect_settings[PGPORT], $postgresql::server::port
#
Expand Down Expand Up @@ -75,17 +80,17 @@
$createdb_sql = $createdb ? { true => 'CREATEDB', default => 'NOCREATEDB' }
$superuser_sql = $superuser ? { true => 'SUPERUSER', default => 'NOSUPERUSER' }
$replication_sql = $replication ? { true => 'REPLICATION', default => '' }
if ($password_hash != false) {
$password_sql = "ENCRYPTED PASSWORD '${password_hash}'"
if ($password_hash_unsensitive != false) {
$password_sql = "ENCRYPTED PASSWORD '${password_hash_unsensitive}'"
} else {
$password_sql = ''
}

postgresql_psql { "CREATE ROLE ${username} ENCRYPTED PASSWORD ****":
command => Sensitive("CREATE ROLE \"${username}\" ${password_sql} ${login_sql} ${createrole_sql} ${createdb_sql} ${superuser_sql} ${replication_sql} CONNECTION LIMIT ${connection_limit}"),
unless => "SELECT 1 FROM pg_roles WHERE rolname = '${username}'",
require => undef,
sensitive => true,
command => Sensitive("CREATE ROLE \"${username}\" ${password_sql} ${login_sql} ${createrole_sql} ${createdb_sql} ${superuser_sql} ${replication_sql} CONNECTION LIMIT ${connection_limit}"),
unless => "SELECT 1 FROM pg_roles WHERE rolname = '${username}'",
require => undef,
sensitive => true,
}

postgresql_psql { "ALTER ROLE \"${username}\" ${superuser_sql}":
Expand Down Expand Up @@ -124,17 +129,17 @@
unless => "SELECT 1 FROM pg_roles WHERE rolname = '${username}' AND rolconnlimit = ${connection_limit}",
}

if $password_hash and $update_password {
if($password_hash =~ /^md5.+/) {
$pwd_hash_sql = $password_hash
if $password_hash_unsensitive and $update_password {
if($password_hash_unsensitive =~ /^md5.+/) {
$pwd_hash_sql = $password_hash_unsensitive
} else {
$pwd_md5 = md5("${password_hash}${username}")
$pwd_md5 = md5("${password_hash_unsensitive}${username}")
$pwd_hash_sql = "md5${pwd_md5}"
}
postgresql_psql { "ALTER ROLE ${username} ENCRYPTED PASSWORD ****":
command => Sensitive("ALTER ROLE \"${username}\" ${password_sql}"),
unless => Sensitive("SELECT 1 FROM pg_shadow WHERE usename = '${username}' AND passwd = '${pwd_hash_sql}'"),
sensitive => true,
command => Sensitive("ALTER ROLE \"${username}\" ${password_sql}"),
unless => Sensitive("SELECT 1 FROM pg_shadow WHERE usename = '${username}' AND passwd = '${pwd_hash_sql}'"),
sensitive => true,
}
}
} else {
Expand Down
18 changes: 12 additions & 6 deletions manifests/validate_db_connection.pp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# @summary This type validates that a successful postgres connection.
#
#
# @note
# This validated if the postgres connection can be established
# between the node on which this resource is run and a specified postgres
Expand All @@ -20,7 +20,7 @@
define postgresql::validate_db_connection (
$database_host = undef,
$database_name = undef,
$database_password = undef,
Optional[Variant[String, Sensitive[String]]] $database_password = undef,
$database_username = undef,
$database_port = undef,
$connect_settings = undef,
Expand All @@ -34,6 +34,12 @@

warning('postgresql::validate_db_connection is deprecated, please use postgresql_conn_validator.')

$database_password_unsensitive = if $database_password =~ Sensitive[String] {
$database_password.unwrap
} else {
$database_password
}

$psql_path = $postgresql::params::psql_path
$module_workdir = $postgresql::params::module_workdir
$validcon_script_path = $postgresql::client::validcon_script_path
Expand All @@ -55,9 +61,9 @@
undef => "--dbname ${postgresql::params::default_database} ",
default => "--dbname ${database_name} ",
}
$pass_env = $database_password ? {
$pass_env = $database_password_unsensitive ? {
undef => undef,
default => "PGPASSWORD=${database_password}",
default => "PGPASSWORD=${database_password_unsensitive}",
}
$cmd = join([$cmd_init, $cmd_host, $cmd_user, $cmd_port, $cmd_dbname], ' ')
$validate_cmd = "${validcon_script_path} ${sleep} ${tries} '${cmd}'"
Expand All @@ -66,8 +72,8 @@
# time it takes to run each psql command.
$timeout = (($sleep + 2) * $tries)

# Combine $database_password and $connect_settings into an array of environment
# variables, ensure $database_password is last, allowing it to override a password
# Combine $database_password_unsensitive and $connect_settings into an array of environment
# variables, ensure $database_password_unsensitive is last, allowing it to override a password
# from the $connect_settings hash
if $connect_settings != undef {
if $pass_env != undef {
Expand Down
64 changes: 45 additions & 19 deletions spec/unit/defines/server/role_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,56 @@
'test'
end

let :params do
{
password_hash: 'new-pa$s',
}
end

let :pre_condition do
"class {'postgresql::server':}"
end

it { is_expected.to contain_postgresql__server__role('test') }
it 'has create role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('CREATE ROLE test ENCRYPTED PASSWORD ****')
.with('command' => 'Sensitive [value redacted]',
'sensitive' => 'true',
'unless' => "SELECT 1 FROM pg_roles WHERE rolname = 'test'",
'port' => '5432')
context 'with Password Datatype String' do
let :params do
{
password_hash: 'new-pa$s',
}
end

it { is_expected.to contain_postgresql__server__role('test') }
it 'has create role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('CREATE ROLE test ENCRYPTED PASSWORD ****')
.with('command' => 'Sensitive [value redacted]',
'sensitive' => 'true',
'unless' => "SELECT 1 FROM pg_roles WHERE rolname = 'test'",
'port' => '5432')
end
it 'has alter role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('ALTER ROLE test ENCRYPTED PASSWORD ****')
.with('command' => 'Sensitive [value redacted]',
'sensitive' => 'true',
'unless' => 'Sensitive [value redacted]',
'port' => '5432')
end
end
it 'has alter role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('ALTER ROLE test ENCRYPTED PASSWORD ****')
.with('command' => 'Sensitive [value redacted]',
'sensitive' => 'true',
'unless' => 'Sensitive [value redacted]',
'port' => '5432')

context 'with Password Datatype Sensitive[String]' do
let :params do
{
password_hash: sensitive('new-pa$s'),
}
end

it { is_expected.to contain_postgresql__server__role('test') }
it 'has create role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('CREATE ROLE test ENCRYPTED PASSWORD ****')
.with('command' => 'Sensitive [value redacted]',
'sensitive' => 'true',
'unless' => "SELECT 1 FROM pg_roles WHERE rolname = 'test'",
'port' => '5432')
end
it 'has alter role for "test" user with password as ****' do
is_expected.to contain_postgresql_psql('ALTER ROLE test ENCRYPTED PASSWORD ****')
.with('command' => 'Sensitive [value redacted]',
'sensitive' => 'true',
'unless' => 'Sensitive [value redacted]',
'port' => '5432')
end
end

context 'with specific db connection settings - default port' do
Expand Down