Skip to content

Commit 9da226b

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

11 files changed

+423
-219
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# 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: 28 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 {
@@ -61,6 +63,29 @@
6163
user => $database_username,
6264
password => $database_password,
6365
grant => 'all',
66+
} ->
67+
68+
postgresql_psql { "revoke all access on public schema":
69+
db => $database_name,
70+
command => "REVOKE CREATE ON SCHEMA public FROM public",
71+
unless => "SELECT * FROM
72+
(SELECT has_schema_privilege('public', 'public', 'create') can_create) privs
73+
WHERE privs.can_create=false",
74+
} ->
75+
76+
postgresql_psql { "grant all permissions to ${database_username}":
77+
db => $database_name,
78+
command => "GRANT CREATE ON SCHEMA public TO \"${database_username}\"",
79+
unless => "SELECT * FROM
80+
(SELECT has_schema_privilege('${database_username}', 'public', 'create') can_create) privs
81+
WHERE privs.can_create=true",
82+
} ->
83+
84+
puppetdb::database::read_only_user { $read_database_username:
85+
read_database_username => $read_database_username,
86+
database_name => $database_name,
87+
password_hash => postgresql::postgresql_password($read_database_username, $read_database_password),
88+
database_owner => $database_username
6489
}
6590
}
66-
}
91+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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}
10+
(ipv4)":
11+
type => 'hostssl',
12+
database => $database_name,
13+
user => $database_username,
14+
address => '0.0.0.0/0',
15+
auth_method => 'cert',
16+
order => 0,
17+
auth_option => "map=${identity_map_key} clientcert=1"
18+
}
19+
20+
postgresql::server::pg_hba_rule { "Allow certificate mapped connections to ${database_name} as ${database_username}
21+
(ipv6)":
22+
type => 'hostssl',
23+
database => $database_name,
24+
user => $database_username,
25+
address => '::0/0',
26+
auth_method => 'cert',
27+
order => 0,
28+
auth_option => "map=${identity_map_key} clientcert=1"
29+
}
30+
31+
postgresql::server::pg_ident_rule { "Map the SSL certificate of the server as a ${database_username} user":
32+
map_name => $identity_map_key,
33+
system_username => $puppetdb_server,
34+
database_username => $database_username,
35+
}
36+
}

manifests/database/read_grant.pp

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

manifests/database/read_only_user.pp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# A define type to manage the creation of a read-only postgres users.
2+
# In particular, it manages the necessary grants to enable such a user
3+
# to have read-only access to any existing objects as well as changes
4+
# the default access privileges so read-only access is maintained when
5+
# new objects are created by the $database_owner
6+
#
7+
# @param database_read_only_username [String] The name of the postgres read only user.
8+
# @param database [String] The name of the database to grant access to.
9+
# @param database_owner [String] The user which owns the database (i.e. the migration user
10+
# for the database).
11+
# @param password_hash [String] The value of $_database_password in app_database.
12+
13+
define puppetdb::database::read_only_user (
14+
String $read_database_username,
15+
String $database_name,
16+
String $database_owner,
17+
Variant[String, Boolean] $password_hash = false,
18+
) {
19+
postgresql::server::role { $read_database_username:
20+
password_hash => $password_hash,
21+
} ->
22+
23+
postgresql::server::database_grant { "${database_name} grant connection permission to ${read_database_username}":
24+
privilege => 'CONNECT',
25+
db => $database_name,
26+
role => $read_database_username,
27+
} ->
28+
29+
puppetdb::database::default_read_grant {
30+
"${database_name} grant read permission on new objects from ${database_owner} to ${read_database_username}":
31+
database_username => $database_owner,
32+
database_read_only_username => $read_database_username,
33+
database_name => $database_name,
34+
schema => 'public',
35+
} ->
36+
37+
puppetdb::database::read_grant {
38+
"${database_name} grant read-only permission on existing objects to ${read_database_username}":
39+
database_read_only_username => $read_database_username,
40+
database_name => $database_name,
41+
schema => 'public',
42+
}
43+
}
Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
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 {
1112

12-
file {'postgres private key':
13+
file { 'postgres private key':
1314
ensure => present,
1415
path => "${postgresql::server::datadir}/server.key",
1516
source => $postgresql_ssl_key_path,
@@ -18,7 +19,7 @@
1819
require => Package['postgresql-server'],
1920
}
2021

21-
file {'postgres public key':
22+
file { 'postgres public key':
2223
ensure => present,
2324
path => "${postgresql::server::datadir}/server.crt",
2425
source => $postgresql_ssl_cert_path,
@@ -27,55 +28,39 @@
2728
require => Package['postgresql-server'],
2829
}
2930

30-
postgresql::server::config_entry {'ssl':
31+
postgresql::server::config_entry { 'ssl':
3132
ensure => present,
3233
value => 'on',
3334
require => [File['postgres private key'], File['postgres public key']]
3435
}
3536

36-
postgresql::server::config_entry {'ssl_cert_file':
37+
postgresql::server::config_entry { 'ssl_cert_file':
3738
ensure => present,
3839
value => "${postgresql::server::datadir}/server.crt",
3940
require => [File['postgres private key'], File['postgres public key']]
4041
}
4142

42-
postgresql::server::config_entry {'ssl_key_file':
43+
postgresql::server::config_entry { 'ssl_key_file':
4344
ensure => present,
4445
value => "${postgresql::server::datadir}/server.key",
4546
require => [File['postgres private key'], File['postgres public key']]
4647
}
4748

48-
postgresql::server::config_entry {'ssl_ca_file':
49+
postgresql::server::config_entry { 'ssl_ca_file':
4950
ensure => present,
5051
value => $postgresql_ssl_ca_cert_path,
5152
require => [File['postgres private key'], File['postgres public key']]
5253
}
5354

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"
55+
puppetdb::database::postgresql_ssl_rules { "Configure postgresql ssl rules for $database_username":
56+
database_name => $database_name,
57+
database_username => $database_username,
58+
puppetdb_server => $puppetdb_server,
7459
}
7560

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,
61+
puppetdb::database::postgresql_ssl_rules { "Configure postgresql ssl rules for $read_database_username":
62+
database_name => $database_name,
63+
database_username => $read_database_username,
64+
puppetdb_server => $puppetdb_server,
8065
}
8166
}

manifests/init.pp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -183,18 +183,23 @@
183183
}
184184

185185
class { '::puppetdb::database::postgresql':
186-
listen_addresses => $database_listen_address,
187-
database_name => $database_name,
188-
puppetdb_server => $puppetdb_server,
189-
database_username => $database_username,
190-
database_password => $database_password,
191-
database_port => $database_port,
192-
manage_server => $manage_dbserver,
193-
manage_database => $manage_database,
194-
manage_package_repo => $manage_package_repo,
195-
postgres_version => $postgres_version,
196-
postgresql_ssl_on => $postgresql_ssl_on,
197-
before => $database_before
186+
listen_addresses => $database_listen_address,
187+
database_name => $database_name,
188+
puppetdb_server => $puppetdb_server,
189+
database_username => $database_username,
190+
database_password => $database_password,
191+
database_port => $database_port,
192+
manage_server => $manage_dbserver,
193+
manage_database => $manage_database,
194+
manage_package_repo => $manage_package_repo,
195+
postgres_version => $postgres_version,
196+
postgresql_ssl_on => $postgresql_ssl_on,
197+
postgresql_ssl_key_path => $postgresql_ssl_key_path,
198+
postgresql_ssl_cert_path => $postgresql_ssl_cert_path,
199+
postgresql_ssl_ca_cert_path => $postgresql_ssl_ca_cert_path,
200+
read_database_username => $read_database_username,
201+
read_database_password => $read_database_password,
202+
before => $database_before
198203
}
199204
}
200205
}

manifests/params.pp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
$read_database_host = undef
6262
$read_database_port = '5432'
6363
$read_database_name = 'puppetdb'
64-
$read_database_username = 'puppetdb'
65-
$read_database_password = 'puppetdb'
64+
$read_database_username = 'puppetdb-read'
65+
$read_database_password = 'puppetdb-read'
6666
$manage_read_db_password = true
6767
$read_database_jdbc_ssl_properties = ''
6868
$read_database_validate = true

0 commit comments

Comments
 (0)