6a7f7dc4bc735a8421c16c5e49384b71ef3527da
[mirror/dsa-puppet.git] / modules / postgres / manifests / cluster / hba_entry.pp
1 # An entry in pg_hba and the corresponding firewall rule if necessary
2 #
3 # This currently only supports a limited number of entry types.  Only
4 # what we need at the moment.
5 #
6 # See the upstream documentation at https://www.postgresql.org/docs/11/auth-pg-hba-conf.html
7 # for details.
8 #
9 # Default order is 50, postgres::cluster puts the md5 localhost rules at 30,
10 # so guest/trust access should probably go at 25.
11 #
12 # @param pg_port          port of the postgres cluster
13 # @param pg_cluster       cluster name
14 # @param pg_version       pg version of the cluster
15 # @param connection_type  connection type
16 # @param database         database (or all, sameuser, replication, etc.)
17 # @param user             user (or all, etc.)
18 # @param address          hosts that match
19 # @param method           auth method
20 # @param order            ordering of this entry in pg_hba.conf
21 # @param firewall         also add a firewall rule
22 define postgres::cluster::hba_entry (
23   Optional[Integer] $pg_port = undef,
24   Optional[String] $pg_cluster = undef,
25   Optional[String] $pg_version = undef,
26   Enum['local', 'host', 'hostssl'] $connection_type = 'hostssl',
27   Variant[String,Array[String]] $database = 'sameuser',
28   Variant[String,Array[String]] $user = 'all',
29   Optional[Variant[Stdlib::IP::Address, Array[Stdlib::IP::Address]]] $address = undef,
30   Enum['md5', 'trust'] $method = 'md5',
31   String $order = '50',
32   Boolean $firewall = true,
33 ) {
34   $address_methods = ['md5', 'trust']
35   if $method in $address_methods {
36     if !$address {
37       fail("Authentication method ${method} needs an address")
38     }
39   } else {
40     if !($method in $address_methods) {
41       fail("Authentication method ${method} needs no address")
42     }
43   }
44
45   # get remaining cluster info and verify consistency
46   ###
47   $clusters = $facts['postgresql_clusters']
48   if $pg_port {
49     $filtered = $clusters.filter |$cluster| { $cluster['port'] == $pg_port }
50     if $filtered.length != 1 {
51       fail("Did not find exactly one cluster with port ${pg_port}")
52     }
53     $cluster = $filtered[0]
54   } elsif $pg_cluster and $pg_version {
55     $filtered = $clusters.filter |$cluster| { $cluster['version'] == $pg_version and $cluster['cluster'] == $pg_cluster}
56     if $filtered.length != 1 {
57       fail("Did not find exactly one cluster ${pg_version}/${pg_cluster}")
58     }
59     $cluster = $filtered[0]
60   } else {
61     fail('postgres::cluster::hba_entry needs either the port of both a pg version and cluster name')
62   }
63   $real_port    = $cluster['port']
64   $real_version = $cluster['version']
65   $real_cluster = $cluster['cluster']
66   if $pg_version and $pg_version != $real_version {
67     fail("Inconsisten cluster version information: ${pg_version} != ${real_version}")
68   }
69   if $pg_cluster and $pg_cluster != $real_cluster {
70     fail("Inconsisten cluster name information: ${pg_cluster} != ${real_cluster}")
71   }
72   ###
73
74   if ($address and $firewall) {
75     ferm::rule::simple { "postgres::cluster::hba_entry::${name}":
76       description => "allow access to pg${real_version}/${real_cluster}: ${name}",
77       saddr       => $address,
78       chain       => "pg-${real_port}",
79     }
80   }
81
82   $real_database = Array($database, true).sort().join(',')
83   $real_user     = Array($user, true).sort().join(',')
84   $real_address  = $address ? {
85     undef   => [''],
86     default => Array($address, true).map |$a| {
87       if    $a =~ Stdlib::IP::Address::V4::CIDR     { $a }
88       elsif $a =~ Stdlib::IP::Address::V4::Nosubnet { "${a}/32" }
89       elsif $a =~ Stdlib::IP::Address::V6::CIDR     { $a }
90       elsif $a =~ Stdlib::IP::Address::V6::Nosubnet { "${a}/128" }
91       else { fail("Do not know address type for ${a}") }
92     }
93   }
94
95   @concat::fragment { "postgres::cluster::pg_hba::${name}":
96     tag     => "postgres::cluster::${real_version}::${real_cluster}::hba",
97     target  => "postgres::cluster::${real_version}::${real_cluster}::hba",
98     order   => $order,
99     content => inline_template( @(EOF) ),
100                   #
101                   # rule <%= @name %>
102                   <% @real_address.each do |addr| -%>
103                   <%= [@connection_type, @real_database, @real_user, addr, @method].join(' ') %>
104                   <% end -%>
105                   #
106                   | EOF
107   }
108 }