Skip to content

Commit cfdc6e2

Browse files
authored
Merge pull request #330 from Filipovici-Andrei/(maint)_add_user
(maint) Add read-only user.
2 parents 6bdd3d4 + c27ef55 commit cfdc6e2

16 files changed

+454
-149
lines changed

README.md

Lines changed: 12 additions & 10 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
613-
supported in PuppetDB >= 1.6.
611+
The hostname or IP address of the read database server. If set to `undef`, and
612+
`manage_database` is set to `true`, it will use the value of the `database_host`
613+
parameter. This option is 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
618-
option is supported in PuppetDB >= 1.6.
617+
The port that the read database server listens on. If `read_database_host`
618+
is set to `undef`, and `manage_database` is set to `true`, it will use the value of
619+
the `database_port` parameter. This 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,8 +636,9 @@ Defaults to `true`
635636

636637
#### `read_database_name`
637638

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

641643
#### `read_log_slow_statements`
642644

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: 41 additions & 4 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,10 @@
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,
20+
$read_database_host = $puppetdb::params::read_database_host
1821
) inherits puppetdb::params {
1922

2023
if $manage_server {
@@ -29,16 +32,27 @@
2932
port => scanf($database_port, '%i')[0],
3033
}
3134

35+
# We need to create the ssl connection for the read user, when
36+
# manage_database is set to true, or when read_database_host is defined.
37+
# Otherwise we don't create it.
38+
if $manage_database or $read_database_host != undef{
39+
$create_read_user_rule = true
40+
} else {
41+
$create_read_user_rule = false
42+
}
43+
3244
# configure PostgreSQL communication with Puppet Agent SSL certificates if
3345
# postgresql_ssl_on is set to true
3446
if $postgresql_ssl_on {
3547
class { 'puppetdb::database::ssl_configuration':
3648
database_name => $database_name,
3749
database_username => $database_username,
50+
read_database_username => $read_database_username,
3851
puppetdb_server => $puppetdb_server,
3952
postgresql_ssl_key_path => $postgresql_ssl_key_path,
4053
postgresql_ssl_cert_path => $postgresql_ssl_cert_path,
41-
postgresql_ssl_ca_cert_path => $postgresql_ssl_ca_cert_path
54+
postgresql_ssl_ca_cert_path => $postgresql_ssl_ca_cert_path,
55+
create_read_user_rule => $create_read_user_rule
4256
}
4357
}
4458

@@ -62,5 +76,28 @@
6276
password => $database_password,
6377
grant => 'all',
6478
}
79+
80+
-> postgresql_psql { 'revoke all access on public schema':
81+
db => $database_name,
82+
command => 'REVOKE CREATE ON SCHEMA public FROM public',
83+
unless => "SELECT * FROM
84+
(SELECT has_schema_privilege('public', 'public', 'create') can_create) privs
85+
WHERE privs.can_create=false",
86+
}
87+
88+
-> postgresql_psql { "grant all permissions to ${database_username}":
89+
db => $database_name,
90+
command => "GRANT CREATE ON SCHEMA public TO \"${database_username}\"",
91+
unless => "SELECT * FROM
92+
(SELECT has_schema_privilege('${database_username}', 'public', 'create') can_create) privs
93+
WHERE privs.can_create=true",
94+
}
95+
96+
-> puppetdb::database::read_only_user { $read_database_username:
97+
read_database_username => $read_database_username,
98+
database_name => $database_name,
99+
password_hash => postgresql::postgresql_password($read_database_username, $read_database_password),
100+
database_owner => $database_username
101+
}
65102
}
66-
}
103+
}
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+
}

0 commit comments

Comments
 (0)