Skip to content

Commit ce40382

Browse files
(maint) Add read-only user.
1 parent 6bdd3d4 commit ce40382

15 files changed

+500
-243
lines changed

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -606,25 +606,26 @@ Which database backend to use for the read database. Only supports
606606
`postgres` (default). This option is supported in PuppetDB >= 1.6.
607607

608608
#### `read_database_host`
609-
*This parameter must be set to enable the PuppetDB read-database.*
609+
*This parameter must be set to use another PuppetDB instance for queries.*
610610

611-
The hostname or IP address of the read database server. Defaults to `undef`.
612-
The default is to use the regular database for reads and writes. This option is
611+
The hostname or IP address of the read database server. If set to `undef`,
612+
it will use the value of the `database_host` parameter. This option is
613613
supported in PuppetDB >= 1.6.
614614

615615
#### `read_database_port`
616616

617-
The port that the read database server listens on. Defaults to `5432`. This
617+
The port that the read database server listens on. If `read_database_host`
618+
is set to `undef`, it will use the value of the `database_port` parameter. This
618619
option is supported in PuppetDB >= 1.6.
619620

620621
#### `read_database_username`
621622

622-
The name of the read database user to connect as. Defaults to `puppetdb`. This
623+
The name of the read database user to connect as. Defaults to `puppetdb-read`. This
623624
option is supported in PuppetDB >= 1.6.
624625

625626
#### `read_database_password`
626627

627-
The password for the read database user. Defaults to `puppetdb`. This option is
628+
The password for the read database user. Defaults to `puppetdb-read`. This option is
628629
supported in PuppetDB >= 1.6.
629630

630631
#### `manage_read_db_password`
@@ -635,7 +636,8 @@ Defaults to `true`
635636

636637
#### `read_database_name`
637638

638-
The name of the read database instance to connect to. Defaults to `puppetdb`.
639+
The name of the read database instance to connect to. If `read_database_host`
640+
is set to `undef`, it will use the value of the `database_name` parameter.
639641
This option is supported in PuppetDB >= 1.6.
640642

641643
#### `read_log_slow_statements`
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Private class. Grant read permissions to $database_read_only_username by default, for new tables created by
2+
# $database_username.
3+
define puppetdb::database::default_read_grant(
4+
String $database_name,
5+
String $schema,
6+
String $database_username,
7+
String $database_read_only_username,
8+
) {
9+
postgresql_psql {"grant default select permission for ${database_read_only_username}":
10+
db => $database_name,
11+
command => "ALTER DEFAULT PRIVILEGES
12+
FOR USER \"${database_username}\"
13+
IN SCHEMA \"${schema}\"
14+
GRANT SELECT ON TABLES
15+
TO \"${database_read_only_username}\"",
16+
unless => "SELECT
17+
ns.nspname,
18+
acl.defaclobjtype,
19+
acl.defaclacl
20+
FROM pg_default_acl acl
21+
JOIN pg_namespace ns ON acl.defaclnamespace=ns.oid
22+
WHERE acl.defaclacl::text ~ '.*\\\\\"${database_read_only_username}\\\\\"=r/${database_username}\\\".*'
23+
AND nspname = '${schema}'",
24+
}
25+
26+
postgresql_psql {"grant default usage permission for ${database_read_only_username}":
27+
db => $database_name,
28+
command => "ALTER DEFAULT PRIVILEGES
29+
FOR USER \"${database_username}\"
30+
IN SCHEMA \"${schema}\"
31+
GRANT USAGE ON SEQUENCES
32+
TO \"${database_read_only_username}\"",
33+
unless => "SELECT
34+
ns.nspname,
35+
acl.defaclobjtype,
36+
acl.defaclacl
37+
FROM pg_default_acl acl
38+
JOIN pg_namespace ns ON acl.defaclnamespace=ns.oid
39+
WHERE acl.defaclacl::text ~ '.*\\\\\"${database_read_only_username}\\\\\"=U/${database_username}\\\".*'
40+
AND nspname = '${schema}'",
41+
}
42+
43+
postgresql_psql {"grant default execute permission for ${database_read_only_username}":
44+
db => $database_name,
45+
command => "ALTER DEFAULT PRIVILEGES
46+
FOR USER \"${database_username}\"
47+
IN SCHEMA \"${schema}\"
48+
GRANT EXECUTE ON FUNCTIONS
49+
TO \"${database_read_only_username}\"",
50+
unless => "SELECT
51+
ns.nspname,
52+
acl.defaclobjtype,
53+
acl.defaclacl
54+
FROM pg_default_acl acl
55+
JOIN pg_namespace ns ON acl.defaclnamespace=ns.oid
56+
WHERE acl.defaclacl::text ~ '.*\\\\\"${database_read_only_username}\\\\\"=X/${database_username}\\\".*'
57+
AND nspname = '${schema}'",
58+
}
59+
}

manifests/database/postgresql.pp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Class for creating the PuppetDB postgresql database. See README.md for more
22
# information.
3-
class puppetdb::database::postgresql(
3+
class puppetdb::database::postgresql (
44
$listen_addresses = $puppetdb::params::database_host,
55
$puppetdb_server = $puppetdb::params::puppetdb_server,
66
$database_name = $puppetdb::params::database_name,
@@ -14,7 +14,9 @@
1414
$postgresql_ssl_on = $puppetdb::params::postgresql_ssl_on,
1515
$postgresql_ssl_key_path = $puppetdb::params::postgresql_ssl_key_path,
1616
$postgresql_ssl_cert_path = $puppetdb::params::postgresql_ssl_cert_path,
17-
$postgresql_ssl_ca_cert_path = $puppetdb::params::postgresql_ssl_ca_cert_path
17+
$postgresql_ssl_ca_cert_path = $puppetdb::params::postgresql_ssl_ca_cert_path,
18+
$read_database_username = $puppetdb::params::read_database_username,
19+
$read_database_password = $puppetdb::params::read_database_password
1820
) inherits puppetdb::params {
1921

2022
if $manage_server {
@@ -35,6 +37,7 @@
3537
class { 'puppetdb::database::ssl_configuration':
3638
database_name => $database_name,
3739
database_username => $database_username,
40+
read_database_username => $read_database_username,
3841
puppetdb_server => $puppetdb_server,
3942
postgresql_ssl_key_path => $postgresql_ssl_key_path,
4043
postgresql_ssl_cert_path => $postgresql_ssl_cert_path,
@@ -62,5 +65,28 @@
6265
password => $database_password,
6366
grant => 'all',
6467
}
68+
69+
-> postgresql_psql { 'revoke all access on public schema':
70+
db => $database_name,
71+
command => 'REVOKE CREATE ON SCHEMA public FROM public',
72+
unless => "SELECT * FROM
73+
(SELECT has_schema_privilege('public', 'public', 'create') can_create) privs
74+
WHERE privs.can_create=false",
75+
}
76+
77+
-> postgresql_psql { "grant all permissions to ${database_username}":
78+
db => $database_name,
79+
command => "GRANT CREATE ON SCHEMA public TO \"${database_username}\"",
80+
unless => "SELECT * FROM
81+
(SELECT has_schema_privilege('${database_username}', 'public', 'create') can_create) privs
82+
WHERE privs.can_create=true",
83+
}
84+
85+
-> puppetdb::database::read_only_user { $read_database_username:
86+
read_database_username => $read_database_username,
87+
database_name => $database_name,
88+
password_hash => postgresql::postgresql_password($read_database_username, $read_database_password),
89+
database_owner => $database_username
90+
}
6591
}
66-
}
92+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Private class for configuring the pg_ident.conf and pg_hba.conf files
2+
define puppetdb::database::postgresql_ssl_rules (
3+
String $database_name,
4+
String $database_username,
5+
String $puppetdb_server,
6+
) {
7+
$identity_map_key = "${database_name}-${database_username}-map"
8+
9+
postgresql::server::pg_hba_rule { "Allow certificate mapped connections to ${database_name} as ${database_username} (ipv4)":
10+
type => 'hostssl',
11+
database => $database_name,
12+
user => $database_username,
13+
address => '0.0.0.0/0',
14+
auth_method => 'cert',
15+
order => 0,
16+
auth_option => "map=${identity_map_key} clientcert=1"
17+
}
18+
19+
postgresql::server::pg_hba_rule { "Allow certificate mapped connections to ${database_name} as ${database_username} (ipv6)":
20+
type => 'hostssl',
21+
database => $database_name,
22+
user => $database_username,
23+
address => '::0/0',
24+
auth_method => 'cert',
25+
order => 0,
26+
auth_option => "map=${identity_map_key} clientcert=1"
27+
}
28+
29+
postgresql::server::pg_ident_rule { "Map the SSL certificate of the server as a ${database_username} user":
30+
map_name => $identity_map_key,
31+
system_username => $puppetdb_server,
32+
database_username => $database_username,
33+
}
34+
}

manifests/database/read_grant.pp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Private class. Grant read-only permissions to $database_read_only_username for all objects in $schema of
2+
# $database_name
3+
define puppetdb::database::read_grant (
4+
String $database_name,
5+
String $schema,
6+
String $database_read_only_username,
7+
) {
8+
postgresql_psql { "grant select permission for ${database_read_only_username}":
9+
db => $database_name,
10+
command => "GRANT SELECT
11+
ON ALL TABLES IN SCHEMA \"${schema}\"
12+
TO \"${database_read_only_username}\"",
13+
unless => "SELECT * FROM (
14+
SELECT COUNT(*)
15+
FROM pg_tables
16+
WHERE schemaname='public'
17+
AND has_table_privilege('${database_read_only_username}', schemaname || '.' || tablename, 'SELECT')=false
18+
) x
19+
WHERE x.count=0",
20+
}
21+
22+
postgresql_psql { "grant usage permission for ${database_read_only_username}":
23+
db => $database_name,
24+
command => "GRANT USAGE
25+
ON ALL SEQUENCES IN SCHEMA \"${schema}\"
26+
TO \"${database_read_only_username}\"",
27+
unless => "SELECT * FROM (
28+
SELECT COUNT(*)
29+
FROM information_schema.sequences
30+
WHERE sequence_schema='public'
31+
AND has_sequence_privilege('${database_read_only_username}', sequence_schema || '.' || sequence_name, 'USAGE')=false
32+
) x
33+
WHERE x.count=0",
34+
}
35+
36+
postgresql_psql { "grant execution permission for ${database_read_only_username}":
37+
db => $database_name,
38+
command => "GRANT EXECUTE
39+
ON ALL FUNCTIONS IN SCHEMA \"${schema}\"
40+
TO \"${database_read_only_username}\"",
41+
unless => "SELECT * FROM (
42+
SELECT COUNT(*)
43+
FROM pg_catalog.pg_proc p
44+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
45+
WHERE n.nspname='public'
46+
AND has_function_privilege('${database_read_only_username}', p.oid, 'EXECUTE')=false
47+
) x
48+
WHERE x.count=0",
49+
}
50+
}

manifests/database/read_only_user.pp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Private class
2+
# A define type to manage the creation of a read-only postgres users.
3+
# In particular, it manages the necessary grants to enable such a user
4+
# to have read-only access to any existing objects as well as changes
5+
# the default access privileges so read-only access is maintained when
6+
# new objects are created by the $database_owner
7+
#
8+
# @param database_read_only_username [String] The name of the postgres read only user.
9+
# @param database [String] The name of the database to grant access to.
10+
# @param database_owner [String] The user which owns the database (i.e. the migration user
11+
# for the database).
12+
# @param password_hash [String] The value of $_database_password in app_database.
13+
14+
define puppetdb::database::read_only_user (
15+
String $read_database_username,
16+
String $database_name,
17+
String $database_owner,
18+
Variant[String, Boolean] $password_hash = false,
19+
) {
20+
postgresql::server::role { $read_database_username:
21+
password_hash => $password_hash,
22+
}
23+
24+
-> postgresql::server::database_grant { "${database_name} grant connection permission to ${read_database_username}":
25+
privilege => 'CONNECT',
26+
db => $database_name,
27+
role => $read_database_username,
28+
}
29+
30+
-> puppetdb::database::default_read_grant {
31+
"${database_name} grant read permission on new objects from ${database_owner} to ${read_database_username}":
32+
database_username => $database_owner,
33+
database_read_only_username => $read_database_username,
34+
database_name => $database_name,
35+
schema => 'public',
36+
}
37+
38+
-> puppetdb::database::read_grant {
39+
"${database_name} grant read-only permission on existing objects to ${read_database_username}":
40+
database_read_only_username => $read_database_username,
41+
database_name => $database_name,
42+
schema => 'public',
43+
}
44+
}
Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,64 @@
11
# Class for configuring SSL connection for the PuppetDB postgresql database. See README.md for more
22
# information.
3-
class puppetdb::database::ssl_configuration(
3+
class puppetdb::database::ssl_configuration (
44
$database_name = $puppetdb::params::database_name,
55
$database_username = $puppetdb::params::database_username,
6+
$read_database_username = $puppetdb::params::read_database_username,
67
$puppetdb_server = $puppetdb::params::puppetdb_server,
78
$postgresql_ssl_key_path = $puppetdb::params::postgresql_ssl_key_path,
89
$postgresql_ssl_cert_path = $puppetdb::params::postgresql_ssl_cert_path,
910
$postgresql_ssl_ca_cert_path = $puppetdb::params::postgresql_ssl_ca_cert_path
1011
) inherits puppetdb::params {
11-
12-
file {'postgres private key':
12+
File {
1313
ensure => present,
14-
path => "${postgresql::server::datadir}/server.key",
15-
source => $postgresql_ssl_key_path,
1614
owner => 'postgres',
1715
mode => '0600',
1816
require => Package['postgresql-server'],
1917
}
2018

21-
file {'postgres public key':
22-
ensure => present,
23-
path => "${postgresql::server::datadir}/server.crt",
24-
source => $postgresql_ssl_cert_path,
25-
owner => 'postgres',
26-
mode => '0600',
27-
require => Package['postgresql-server'],
19+
file { 'postgres private key':
20+
path => "${postgresql::server::datadir}/server.key",
21+
source => $postgresql_ssl_key_path,
22+
}
23+
24+
file { 'postgres public key':
25+
path => "${postgresql::server::datadir}/server.crt",
26+
source => $postgresql_ssl_cert_path,
2827
}
2928

30-
postgresql::server::config_entry {'ssl':
29+
postgresql::server::config_entry { 'ssl':
3130
ensure => present,
3231
value => 'on',
3332
require => [File['postgres private key'], File['postgres public key']]
3433
}
3534

36-
postgresql::server::config_entry {'ssl_cert_file':
35+
postgresql::server::config_entry { 'ssl_cert_file':
3736
ensure => present,
3837
value => "${postgresql::server::datadir}/server.crt",
3938
require => [File['postgres private key'], File['postgres public key']]
4039
}
4140

42-
postgresql::server::config_entry {'ssl_key_file':
41+
postgresql::server::config_entry { 'ssl_key_file':
4342
ensure => present,
4443
value => "${postgresql::server::datadir}/server.key",
4544
require => [File['postgres private key'], File['postgres public key']]
4645
}
4746

48-
postgresql::server::config_entry {'ssl_ca_file':
47+
postgresql::server::config_entry { 'ssl_ca_file':
4948
ensure => present,
5049
value => $postgresql_ssl_ca_cert_path,
5150
require => [File['postgres private key'], File['postgres public key']]
5251
}
5352

54-
$identity_map_key = "${database_name}-${database_username}-map"
55-
56-
postgresql::server::pg_hba_rule { "Allow certificate mapped connections to ${database_name} as ${database_username} (ipv4)":
57-
type => 'hostssl',
58-
database => $database_name,
59-
user => $database_username,
60-
address => '0.0.0.0/0',
61-
auth_method => 'cert',
62-
order => 0,
63-
auth_option => "map=${identity_map_key} clientcert=1"
64-
}
65-
66-
postgresql::server::pg_hba_rule { "Allow certificate mapped connections to ${database_name} as ${database_username} (ipv6)":
67-
type => 'hostssl',
68-
database => $database_name,
69-
user => $database_username,
70-
address => '::0/0',
71-
auth_method => 'cert',
72-
order => 0,
73-
auth_option => "map=${identity_map_key} clientcert=1"
53+
puppetdb::database::postgresql_ssl_rules { "Configure postgresql ssl rules for ${database_username}":
54+
database_name => $database_name,
55+
database_username => $database_username,
56+
puppetdb_server => $puppetdb_server,
7457
}
7558

76-
postgresql::server::pg_ident_rule {"Map the SSL certificate of the server as a ${database_username} user":
77-
map_name => $identity_map_key,
78-
system_username => $puppetdb_server,
79-
database_username => $database_username,
59+
puppetdb::database::postgresql_ssl_rules { "Configure postgresql ssl rules for ${read_database_username}":
60+
database_name => $database_name,
61+
database_username => $read_database_username,
62+
puppetdb_server => $puppetdb_server,
8063
}
8164
}

0 commit comments

Comments
 (0)