ldap2pg
ldap2pg — automate the creation, update, and removal of Postgres Pro roles and users from an enterprise directory
Synopsis
ldap2pg
[option
...] [dbname
]
Description #
Out of the box, Postgres Pro is able to check password of an existing role using the LDAP protocol. ldap2pg automates the creation, update and removal of Postgres Pro roles, and users from an enterprise directory.
Managing roles is close to managing privileges as you expect roles to have proper default privileges. Postgres Pro can grant and revoke privileges too.
ldap2pg is provided with Postgres Pro Standard as a separate pre-built package ldap2pg
(for the detailed installation instructions, see Chapter 16).
ldap2pg is reported to work with Samba DC, OpenLDAP, FreeIPA, Oracle Internet Directory and Microsoft Active Directory.
ldap2pg requires a configuration file called ldap2pg.yaml
.
Features #
Reads settings from an expressive YAML config file.
Creates, alters and drops Postgres Pro roles from LDAP searches.
Creates static roles from YAML to complete LDAP entries.
Manages role parents (alias groups).
Grants or revokes privileges statically or from LDAP entries.
Dry run, check mode.
Logs LDAP searches as
ldapsearch(1)
commands.Logs every SQL statements.
Requirements #
ldap2pg is released as a single binary with no dependencies.
At run time, ldap2pg requires an unprivileged role with CREATEDB
and CREATEROLE
options or a superuser access.
ldap2pg does not require to run on the same host as the synchronized Postgres Pro Standard cluster.
With 2MiB of RAM and one vCPU, ldap2pg can synchronize several thousands of roles in seconds, depending on Postgres Pro Standard instance and LDAP directory response time.
Command-Line Reference #
ldap2pg tries to be friendly regarding configuration and consistent with psql, OpenLDAP utils and The Twelve-Factor App. ldap2pg reads its configuration from several sources, in the following order, first prevail:
Command-line arguments.
Environment variables.
Configuration file.
ldaprc
,ldap.conf
, etc.
The command-line usage is as follows:
ldap2pg [OPTIONS] [dbname]
Where options are as follows:
--check
Check mode: exits with 1 if Postgres Pro instance is unsynchronized.
--color
Force color output.
-c
string
--config
string
Path to YAML configuration file. Use - for stdin.
--directory
string
Path to directory containing configuration files.
--help
Show help message and exit (default true).
--ldappassword-file
string
Path to LDAP password file.
--quiet
count
Decrease log verbosity.
--real
Real mode. Apply changes to Postgres Pro instance.
-skip-privileges
Turn off privilege synchronization.
--verbose
count
Increase log verbosity.
--version
Show version and exit (default true).
Optional argument dbname
is alternatively the database name or a conninfo string or a URI. See psql(1) man page for more information.
By default, ldap2pg runs in dry mode. ldap2pg requires a configuration file to describe LDAP searches and mappings.
Arguments can be defined multiple times. On conflict, the last argument is used.
Environment Variables #
ldap2pg has no command-line interface switch to configure Postgres Pro connection. However, ldap2pg supports libpq PG*
environment variables.
See psql(1) for details on libpq environment variables.
The same goes for LDAP, ldap2pg supports standard LDAP*
environment variables and .ldaprc
files. See ldap.conf(5)
man page for further details on how to configure. ldap2pg accepts two extra variables: LDAPPASSWORD
and LDAPPASSWORD_FILE
.
ldap2pg loads .env
file in the lda2pg.yml
's parent directory if exists.
Use true
or false
for boolean values in environment, e.g. LDAP2PG_SKIPPRIVILEGES=true
.
Tip
Test Postgres Pro connection using psql(1) and LDAP using ldapwhoami(1) man pages, ldap2pg will be okay and it will be easier to debug the setup and the configuration later.
Logging Setup #
ldap2pg has several levels of logging:
ERROR
: error details. When this happend, ldap2pg will crash.WARNING
: ldap2pg warns about choices you should be aware of.CHANGE
: only changes applied to Postgres Pro> cluster.INFO
(default): tells what ldap2pg is doing, especially before long task.DEBUG
: everything, including raw SQL queries and LDAP searches and introspection details.
The --quiet
and --verbose
command-line switches respectively decrease and increase verbosity.
You can select the highest level of verbosity with LDAP2PG_VERBOSITY
environment variable. For example:
$ LDAP2PG_VERBOSITY=DEBUG ldap2pg 12:23:45 INFO Starting ldap2pg version=v6.0-alpha5 runtime=go1.21.0 commit=<none> 12:23:45 WARN Running a prerelease! Use at your own risks! 12:23:45 DEBUG Searching configuration file in standard locations. 12:23:45 DEBUG Found configuration file. path=./ldap2pg.yml $
ldap2pg output varies whether it is running with a TTY or not. If standard error is a TTY, logging is colored and tweaked for human reading. Otherwise, logging format is pure logfmt, for machine processing. You can force human-readable output by using --color
command-line interface switch.
ldap2pg.yml
File Reference #
ldap2pg requires a YAML configuration file usually named ldap2pg.yml
and put in working directory. Everything can be configured from the YAML file: Postgres Pro inspect queries, LDAP searches, privileges and synchronization map.
Warning
ldap2pg requires a configuration file where the synchronization map is described.
Here are the contents of a tested ldap2pg.yml
, which can be used as a starting point:
# # # L D A P 2 P G S A M P L E C O N F I G U R A T I O N # # # This is a starting point configuration file for ldap2pg.yml. Including static # roles, groups, privilege and LDAP search. # # This configuration assumes the following principles: # # - All LDAP users are grouped in `ldap_roles` group. # - Read privileges are granted to `readers` group. # - Write privileges are granted to `writers` group. # - DDL privileges are granted to `owners` group. # - We have one or more databases with public and maybe a schema. # - Grants are not specific to a schema. Once you're writer in a database, you # are writer to all schemas in it. # # The LDAP directory content is described in test/fixtures/openldap-data.ldif # # Adapt to your needs! # # # File format version. Allows ldap2pg to check whether the file is supported. # version: 6 # # 1. P O S T G R E S I N S P E C T I O N # # See the section called “Inspecting Postgres Pro Cluster”. # postgres: roles_blacklist_query: [nominal, postgres, pg_*] databases_query: [nominal] # # 2. P R I V I L E G E S D E F I N I T I O N # # See the section called “Managing Privileges”. Privileges wrapped # in double underscores are built-in privilege profiles. See # the section called “Built-in Privileges” for a documentation of # each of them. # privileges: # Define `ro` privilege group with read-only grants ro: - __connect__ - __select_on_tables__ - __select_on_sequences__ - __usage_on_schemas__ - __usage_on_types__ # `rw` privilege group lists write-only grants rw: - __temporary__ - __all_on_tables__ - __all_on_sequences__ # `ddl` privilege group lists DDL only grants. ddl: - __create_on_schemas__ # # 3. S Y N C H R O N I S A T I O N M A P # # This list contains rules to declare roles and grants. Each role or grant rule # can be templated with attributes from LDAP entries returned by a search # query. # # Any role found in cluster and not generated by rules will be dropped. Any # grant found in cluster and not generated by rules will be revoked. # rules: - description: "Setup static roles and grants." roles: - names: - readers options: NOLOGIN - name: writers # Grant reading to writers parent: readers options: NOLOGIN - name: owners # Grant read/write to owners parent: writers options: NOLOGIN grant: - privilege: ro role: readers # Scope to a single schema schemas: nominal - privilege: rw role: writers - privilege: ddl role: owners - description: "Search LDAP to create readers, writers and owners." ldapsearch: base: cn=users,dc=bridoulou,dc=fr filter: " (| (cn=owners) (cn=readers) (cn=writers) ) " role: name: '{member.cn}' options: LOGIN parent: "{cn}"
File Location #
ldap2pg searches for configuration file in the following order:
ldap2pg.yml
in current working directory.~/.config/ldap2pg.yml
./etc/ldap2pg.yml
./etc/ldap2pg/ldap2pg.yml
.
If LDAP2PG_CONFIG
or --config
is set, ldap2pg skips searching the standard file locations. You can specify -
to read configuration from standard input. This is helpful to feed ldap2pg with dynamic configuration.
File Structure #
ldap2pg.yml
is split in several sections:
postgres
: setup Postgres Pro> connection and inspection queries.ldap
: configuration for LDAP client.privileges
: the definition of privilege profiles.rules
: the list of LDAP searches and associated mapping to roles and grants.
If you don't know how to begin, a simple tested and well commented ldap2pg.yml is a good starting point.
About YAML #
YAML is a super-set of JSON. A JSON document is a valid YAML document. YAML is a very permissive format where indentation is meaningful. See this YAML cheatsheet for some example.
In ldap2pg.yaml
file, you will likely use wildcard for global pattern and curly brace for LDAP attribute injection. Take care of protecting these characters with quotes.
rules: - role: {cn} # It's an invalid YAML dict. - role: "{cn}" # It's a string with LDAP attribute injection
postgres
Section #
The postgres
section defines custom SQL queries for Postgres Pro inspection.
The postgres
section contains several *_query
parameters. These parameters can be either a string containing an SQL query or a YAML list to return a static list of values, skipping execution of a query on Postgres Pro cluster.
databases_query
#The SQL query to list databases names in the cluster. By default, ldap2pg searches databases it can connect to and it can reassign objects to its owner. ldap2pg loops databases to reassign objects before dropping a role. ldap2pg manages privilege on each database.
postgres: databases_query: "SELECT datname FROM pg_catalog.pg_databases;" # OR databases_query: [mydb]
Note
Configuring a
_query
parameter with a YAML list skips querying the cluster for inspection and forces ldap2pg to use a static value.fallback_owner
#Name of the role accepting ownership of database of dropped role.
Before dropping a role, ldap2pg reassigns objects and purges the access control list (ACL). ldap2pg starts by reassigning database owner by the targetted user. The new owner of the database is the fallback owner. Other objects are reassigned to each database owner.
managed_roles_query
#The SQL query to list the name of managed roles.
ldap2pg restricts role deletion and privilege edition to managed roles. Usually, this query returns children of a dedicated group like
ldap_roles
. By default, ldap2pg manages all roles it has access to.public
is a special built-in role in Postgres Pro. Ifmanaged_roles_query
returnspublic
role in the list, ldap2pg will manage privileges onpublic
. By default, ldap2pg managespublic
privileges.The following example tells ldap2pg to manage
public
role,ldap_roles
and any members ofldap_roles
:postgres: managed_roles_query: | VALUES ('public'), ('ldap_roles') UNION SELECT DISTINCT role.rolname FROM pg_roles AS role JOIN pg_auth_members AS ms ON ms.member = role.oid JOIN pg_roles AS parent ON parent.rolname = 'ldap_roles' AND parent.oid = ms.roleid ORDER BY 1;
roles_blacklist_query
#The SQL query returning name and global pattern to blacklist role from management. ldap2pg won't touch anything on these roles. Default value is
[postgres, pg_*]
. ldap2pg blacklists self user.postgres: roles_blacklist_query: - postgres - "pg_*" - "rds_*"
Warning
Beware that '*foo' is a YAML reference. You must quote pattern beginning with '*'.
schemas_query
#The SQL query returning the name of managed schemas in a database. ldap2pg executes this query on each databases returned by
databases_query
only if ldap2pg manages privileges. ldap2pg loops on objects in theses schemas when inspecting GRANTs in the cluster.postgres: schemas_query: | SELECT nspname FROM pg_catalog.pg_namespace
ldap
Section #
The ldap
section customizes LDAP client behaviour. Configure connection using ldap.conf
and LDAP*
environment variables.
known_rdns
#List of attributes known to be part of the distinguished name (DN).
ldap2pg skips sub-search for attributes in this list. e.g.,
{member.cn}
won't trigger a sub-search on all members ifcn
is inknown_rdns
. Default value is[n, l, st, o, ou, c, street, dc, uid]
. Add a value to fasten synchronization. Remove a value if an attribute is not part of the DN.ldap: known_rdns: [cn, uid]
privileges
Section #
The privileges
top-level section defines Postgres Pro privileges. It is a mapping defining privilege profiles, referenced later in synchronisation map's grant rule. A privilege profile is a list of either references to a privilege type in a Postgres Pro ACL or other profiles. A privilege profile may include another profile, recursively. See Managing Privileges for details.
privileges: reading: - on: GLOBAL DEFAULT type: SELECT object: TABLES writing: - reading - on: GLOBAL type: SELECT object: TABLES
A privilege profile whose name starts with _
is inactive unless included in an active profile.
object
#Defines the target object for object-grained ACL.
Actually useful only for
GLOBAL DEFAULT
andSCHEMA DEFAULT
ACL where the object is the target object class likeTABLES
,SEQUENCES
, etc. grant rule defines target schema forSCHEMA DEFAULT
.privileges: reading: - type: SELECT on: GLOBAL DEFAULT object: TABLES
type
#Type of privilege as described in Section 5.7, e.g.,
SELECT
,REFERENCES
,USAGE
, etc.The value can be either a single string or a list of strings. Plural form
types
is valid. When multiple types are defined, a new privilege is defined for each type, each with the same attributes such ason
.privileges: reading: - type: USAGE on: SCHEMAS
on
#Target ACL of privilege type. e.g., TABLES, SEQUENCES, SCHEMAS, etc. Note the special cases
ALL TABLES
,ALL SEQUENCES
, etc. See Managing Privileges documentation for details.privileges: reading: - type: SELECT on: ALL TABLES
rules
Section #
The top-level rules
section defines synchronisation rules. The section contains is a YAML list. This is the only mandatory parameter in ldap2pg.yaml
. Each item of rules
is called a mapping. A mapping is a YAML dictionary with any of role
or grant
subsection. A mapping can optionnaly have a description
field and a ldapsearch
section.
rules: - description: "Define DBA roles" ldapsearch: base: ... roles: - name: "{cn}" options: LOGIN SUPERUSER
The ldapsearch
subsection is optional. You can define roles and grants without querying a directory.
description
#A free string used for logging. This parameter does not accepts mustache parameter injection.
ldapsearch
#This directive defines LDAP search parameters. It is named after the ldapsearch command-line interface utility shipped by OpenLDAP project. Its behaviour should be mostly the same.
Note
This documentation refers to LDAP query as
search
while the word query is reserved for SQL query.ldapsearch
directives allow and require LDAP attributes injection inrole
andgrant
rules using curly braces. See Querying Directory with LDAP for details.base
,scope
andfilter
These parameters have the same meaning, definition and default as base, scope and filter arguments of ldapsearch command-line interface utility.
rules: - ldapsearch: base: ou=people,dc=acme,dc=tld scope: sub filter: > (&f (member=*) (cn=group_*) )
joins
Customizes LDAP sub-search. The
joins
section is a dictionary with attribute name as key and LDAP search parameters as value. LDAP search parameters are the same as for top LDAP search. Actually, a single sub-search is supported.rules: - ldapsearch: joins: member: filter: ... scope: ... role: - name: "{member.sAMAccountName}"
The search base of sub-search is the value of the referencing attribute, e.g., each value of
member
. You can't customize thebase
attribute of sub-search. Likewise, ldap2pg infers attributes of sub-searches fromrole
andgrant
rules. You can have only a single sub-search per top-level search. You can't do second-level sub-search.See Querying Directory with LDAP for details.
Note
Executing a sub-search for each entry of a result set can be very heavy. You may optimize the query by using special LDAP search filter like
memberOf
. Refer to your LDAP directory administrator and documentation for details.
role
#Defines a rule to describe one or more roles wanted in the target Postgres Pro cluster. This includes name, options, config, comment and membership. Plural form
roles
is valid. The value can be either a single role rule or a list of role rules.rules: - role: name: dba options: SUPERUSER LOGIN - roles: - name: group0 options: NOLOGIN - name: group1 options: NOLOGIN
comment
#Defines the SQL comment of a role. Default value is
Managed by ldap2pg
. Accepts LDAP attribute injection.In case of LDAP attributes injection, you must take care of how many combinations will be generated. If the template generates a single comment, ldap2pg will copy the comment for each role generated by the role rule. If the template generates multiple comments, ldap2pg associates name and comment. If there is more or less comments generated than name generated, ldap2pg fails.
The following example defines a static comment shared by all generated roles:
rules: - roles: names: - alice - bob comment: "Static roles from YAML."
The following example generates a single comment from LDAP entry distinguished name, copied for all generated roles:
rules: - ldapsearch: ... role: name: "{cn}" comment: "Generated from LDAP entry {dn}."
The following example generate a unique comment for each roles generated:
rules: - ldapsearch: ... role: name: "{member.cn}" comment: "Generated from LDAP entry {member}."
Tip
If a role is defined multiple times, parents are merged. Other fields are kept as declared by the first definition of the role.
name
#Name of the role wanted in the cluster. The value can be either a single string or a list of strings. Plural form
names
is valid. You can inject LDAP attributes in name using curly braces. When multiple names are defined, a new role is defined for each name, each with the same attributes such asoptions
andparents
.comment
parameter has a special handling, seecomment
.rules: - roles: name: "my-role-name"
When injecting LDAP attribute in name, each value of the LDAP attribute of each LDAP entry will define a new role. When multiple LDAP attributes are defined in the format, all combination of attributes are generated.
ldap2pg protects role name with double quotes in the target Postgres Pro cluster. Capitalization is preserved, spaces are allowed (even if it's a really bad idea).
ldap2pg applies
roles_blacklist_query
on this parameter.options
#Defines Postgres Pro role options. Can be an SQL-like string or a YAML dictionary. Valid options are
BYPASSRLS
,CONNECTION LIMIT
,LOGIN
,CREATEDB
,CREATEROLE
,INHERIT
,REPLICATION
andSUPERUSER
. Available options vary following the version of the target Postgres Pro cluster and the privilege of ldap2pg user.- roles: - name: my-dba options: LOGIN SUPERUSER - name: my-group options: LOGIN: no INHERIT: yes
config
#Defines Postgres Pro configuration parameters that will be set for the role. Must be a YAML dictionary. Available configuration parameters vary following the version of the target Postgres Pro cluster. Some parameters require superuser privileges to be set. ldap2pg will fail if it does not have privilege to set a config parameter.
- roles: - name: my-db-writer config: log_statement: mod log_min_duration_sample: 100
Setting
config
tonull
(the default) will disable the feature for the role. Ifconfig
is a dict, ldap2pg will drop parameter set in cluster but not defined in ldap2pg YAML. To reset all parameters, setconfig
to an empty dict like below.- roles: - name: reset-my-configuration config: {}
Note that LDAP attributes are not expanded in config values.
parent
#Name of a parent role. A list of names is accepted. The plural form
parents
is valid too. Parent role is granted withGRANT ROLE parent TO role;
.parent
parameter accepts LDAP attributes injection using curly braces. ldap2pg appliesroles_blacklist_query
on this parameter. Parent reference can be local roles not managed by ldap2pg.rules: - role: name: myrole parent: myparent
before_create
#SQL snippet to execute before role creation.
before_create
accepts LDAP attributes injection using curly braces. You are responsible for escaping attribute with either.identifier()
or.string()
.rules: - ldapsearch: ... role: name: "{cn}" before_create: "INSERT INTO log VALUES ({cn.string()})"
after_create
#SQL snippet to execute after role creation.
after_create
accepts LDAP attributes injection using curly braces. You are responsible for escaping attribute with either.identifier()
or.string()
.rules: - ldapsearch: ... role: name: "{sAMAccountName}" after_create: "CREATE SCHEMA {sAMAccountName.identifier()} AUTHORIZATION {sAMAccountName.identifier()}"
grant
#Defines a grant of a privilege to a role with corresponding parameters. Can be a mapping or a list of mapping. Plural form
grants
is valid too.rules: - grant: privilege: reader databases: __all__ schema: public role: myrole
database
Scope the grant to one or more databases. May be a list of names. Plural form
databases
is valid. Special value__all__
expands to all managed databases as returned bydatabases_query
. Defaults to__all__
. Grants found in other databases will be revoked. Accepts LDAP attributes injection using curly braces.This parameter is ignored for instance-wide privileges (e.g., on
LANGUAGE
).privilege
Name of a privilege, within the privileges defined in
privileges
YAML section. May be a list of names. Plural formprivileges
is valid. Required, there is not default value. Accepts LDAP attribute injection using curly braces.role
Name of the target role of the grant (granted role or grantee). Must be listed by
managed_roles_query
. May be a list of names. Plural formroles
is valid. Accepts LDAP attribute injection using curly braces. ldap2pg appliesroles_blacklist_query
on this parameter.schema
Name of a schema whithin the schemas returned by
schemas_query
. Special value__all__
means all managed schemas in the databases. May be a list of names. Plural formschemas
is valid. Accepts LDAP attribute injection using curly braces.This parameter is ignored for privileges on
DATABASE
and other instance-wide or database-wide privileges.owner
Name of role to configure default privileges for. Special value
__auto__
fallbacks to managed roles havingCREATE
privilege on the target schema. May be a list of names. Plural formowners
is valid. Accepts LDAP attribute injection using curly braces.
acls
Section #
Defines Postgres Pro ACLs. An ACL is set of queries to list GRANTs in the cluster and to manage them by granting or revoking item in the list. ACL is scoped to instance or database. References ACL in privileges profiles. Writing a custom ACL is tricky, ensure you understand both PostgreSQL and ldap2pg before.
acls
The
acls
top level section is a mapping defining ACLs. All fields are mandatory.acls: PROCEDURE: scope: database inspect: | WITH ... grant: GRANT ... revoke: REVOKE ...
scope
#Scope of the ACL. Can be
instance
ordatabase
.inspect
#SQL query to list GRANTs in the cluster. The signature of the query depends on the scope. See custom ACLs for details.
grant
#SQL query to grant a privilege. The query accepts templating using angle brackets. Some parameters are injected as keyword, i.e., as raw SQL. Other parameters are quoted as identifiers.
Available parameters:
<acl>
name of the ACL. Raw SQL.<database>
name of database to grant on. Quoted identifier.<grantee>
name of role to grant on. Quoted identifier.<object>
name of object to grant on. Quoted identifier.<owner>
name of role to grant to. Quoted identifier.<privilege>
type of privilege. Raw SQL.<schema>
name of schema to grant on. Quoted identifier.
revoke
#SQL query to revoke a privilege. Like
grant
, the query accepts templating using angle brackets. Accepts same parameters as grant. Having different paramenter between GRANT and REVOKE leads to unexpected behaviour.
Inspecting Postgres Pro Cluster #
ldap2pg follows the explicit create/implicit drop and explicit grant/implicit revoke pattern. Thus properly inspecting cluster for what you want to drop/revoke is very crucial to succeed in synchronization.
ldap2pg inspects databases, schemas, roles, owners and grants with SQL queries. You can customize all these queries in the postgres
YAML section with parameters ending with _query
. See the section called “postgres
Section” for details.
What Databases to Synchronize? #
databases_query
returns the flat list of databases to manage. The databases_query
must return the default database as defined in PGDATABASE
. When dropping roles, ldap2pg loops the databases list to reassign objects and clean GRANTs of the role to be dropped. This databases list also narrows the scope of GRANTs inspection. ldap2pg will revoke GRANTs only on these databases. See the section called “postgres
Section” for details.
postgres: databases_query: | SELECT datname FROM pg_catalog.pg_database WHERE datallowconn IS TRUE;
Synchronize a Subset of Roles #
By default, ldap2pg manages all roles from Postgres Pro, it has powers on, minus the default blacklist. If you want ldap2pg to synchronsize only a subset of roles, you need to customize inspection query in postgres:managed_roles_query
. The following query excludes superusers from synchronization.
postgres: managed_roles_query: | SELECT 'public' UNION SELECT rolname FROM pg_catalog.pg_roles WHERE rolsuper IS FALSE ORDER BY 1;
ldap2pg will only drop, revoke, grant on roles returned by this query.
A common case for this query is to return only members of a group like ldap_roles
. This way, ldap2pg is scoped to a subset of roles in the cluster.
The public
role does not exist in the system catalog. Thus if you want ldap2pg to manage public
privileges, you must include explicitly public
in the set of managed roles. This is the default. Of course, even if public
is managed, ldap2pg won't drop or alter it if it's not in the directory.
A safety net to completely ignore some roles is roles_blacklist_query
.
postgres: roles_blacklist_query: [postgres, pg_*] # This is the default.
Note
A pattern starting with a '*' must be quoted. Else you'll end up with a YAML error like “found undefined alias”.
Inspecting Schemas #
For schema-wide privileges, ldap2pg needs to know managed schemas for each database. This is the purpose of schemas_query
.
Configuring Owners Default Privileges #
To configure default privileges, use the default
keyword when referencing a privilege:
privileges: reading: - default: global type: SELECT on: TABLES
Then grant it using grant
rule:
rules: - grant: - privilege: reading role: readers schema: public owner: ownerrole
You can use __auto__
as owner. For each schema, ldap2pg will configure every managed role having CREATE
privilege on schema.
rules: - grant: - privilege: reading role: readers schema: public owner __auto__
ldap2pg configures default privileges last, after all effective privileges. Thus CREATE
on schema is granted before ldap2pg inspects creators on schemas.
Static Queries #
You can replace all queries with a static list in YAML. This list will be used as if returned by Postgres Pro. That's very handy to freeze a value like databases or schemas.
postgres: databases_query: [postgres] schemas_query: [public]
Managing Roles #
ldap2pg synchronizes Postgres Pro roles in three steps:
Loop
rules
and generate wanted roles list fromrole
rules.Inspect Postgres Pro for existing roles, their options and their membership.
Compare the two roles sets and apply to the Postgres Pro cluster using
CREATE
,DROP
andALTER
.
Each role
entry in rules
is a rule to generate zero or more roles with the corresponding parameters. A role
rule is like a template. role
rules allow you to deduplicate membership and options by setting a list of names.
You can mix static rules and dynamic rules in the same file.
Running Unprivileged #
ldap2pg is designed to run unprivileged. Synchronization user needs CREATEROLE
option to manage other unprivileged roles. CREATEDB
options allow synchronization user to manage database owners.
ldap2pg user must have createrole_self_grant
set to inherit,set
to properly handle groups.
CREATE ROLE ldap2pg LOGIN CREATEDB CREATEROLE; ALTER ROLE ldap2pg SET createrole_self_grant TO 'inherit,set;
Running unprivileged before PostgreSQL 16 is actually flawed. You'd better just run ldap2pg with superuser privileges, you won't feel falsly secured.
Ignoring Roles #
ldap2pg totally ignores roles matching one of the global pattern defined in roles_blacklist_query
:
postgres: # This is the default value. roles_blacklist_query: [postgres, pg_*]
The role blacklist is also applied to grants. ldap2pg will never apply grant
or revoke
on a role matching one of the blacklist patterns.
ldap2pg blacklists its running user.
Membership #
ldap2pg manages parents of roles. ldap2pg applies roles_blacklist_query
to parents. However, ldap2pg grants unmanaged parents. This way, you can create a group manually and manage its members using ldap2pg.
Querying Directory with LDAP #
ldap2pg reads LDAP searches in rules
steps in the ldapsearch
entry.
A LDAP search is not mandatory. ldap2pg can create roles defined statically from YAML. Each LDAP search is executed once and only once. There is neither loop nor deduplication of LDAP searches.
Tip
ldap2pg logs LDAP searches as ldapsearch commands. Enable verbose messages to see them.
You can debug a failing search by copy-pasting the command in your shell and update parameters. Once you are okay, translate back the right parameters in the YAML.
Configuring Directory Access #
ldap2pg reads directory configuration from ldaprc
file and LDAP*
environment variables. Known LDAP options are:
BASE
BINDDN
PASSWORD
REFERRALS
SASL_AUTHCID
SASL_AUTHZID
SASL_MECH
TIMEOUT
TLS_REQCERT
NETWORK_TIMEOUT
URI
See ldap.conf(5) man page for the meaning and format of each options.
Injecting LDAP Attributes #
Several parameters accept LDAP attribute injection using curly braces. To do this, wrap attribute name with curly braces like {cn}
or {sAMAccountName}
. ldap2pg expands to each value of the attribute for each entries of the search.
If the parameter has multiple LDAP attributes, ldap2pg expands to all combinations of attributes for each entries.
Given the following LDAP entries:
dn: uid=dimitri,cn=Users,dc=bridoulou,dc=fr objectClass: inetOrgPerson uid: dimitri sn: Dimitri cn: dimitri mail: dimitri@bridoulou.fr company: external dn: cn=domitille,cn=Users,dc=bridoulou,dc=fr objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: person objectClass: top cn: domitille sn: Domitille company: acme company: external
The format {company}_{cn}
with the above LDAP entries generates the following strings:
acme_domitille
external_domitille
external_dimitri
The pseudo attribute dn
is always available and references the distinguished name (DN) of the original LDAP entry.
Accessing RDN and Sub-Search #
If an attribute type is DN, you can refer to a relative distinguished name (RDN) with a dot, like this: <attribute>.<rdn>
. If an RDN has multiple values, only the first value is returned. There is no way to access other values.
For example, if a LDAP entry has member
attribute with value cn=toto,cn=Users,dc=bridoulou,dc=fr
, the {member.cn}
format will generate toto
. The {member.dc}
format will generate ldap
. There is no way to access acme
and fr
.
Known RDN are cn
, l
, st
, o
, ou
, c
, street
, dc
, and uid
. Other attributes trigger a sub-search. The format {member.sAMAccountName}
will issue a sub-search for all member
value as LDAP search base narrowed to sAMAccountName
attribute.
LDAP Attribute Case #
When injecting a LDAP attribute with curly braces, you can control the case of the value using .lower()
or .upper()
methods.
- ldapsearch: ... role: "{cn.lower()}"
Managing Privileges #
Managing privileges is tricky. ldap2pg tries to make this simpler and safer.
Basics #
The base design of ldap2pg is as follows. Instead of revoke-everything-regrant design, ldap2pg uses inspect-modify design. The process is the same as for roles synchronization, including the three following steps:
Loop
rules
and generate wanted grants set.Inspect Postgres Pro cluster for granted privileges.
Compare the two sets of grants and update the Postgres Pro cluster using
grant
,revoke
.
ldap2pg represents privileges with these core objects:
Privilege
: An action on an object. e.g.,CONNECT ON DATABASE
.grant
: a privilege granted to a role on an object.ACL
: a list of grants.profile
: a list of privileges.rule
: a template to generate wanted grants.
ldap2pg.yml
holds profiles and grant rule. ldap2pg synchronizes ACL one at a time, database by database. ldap2pg synchronizes default privileges last.
By default, ldap2pg does not manage any privileges. To enable privilege management, you must define at least one active profile in privileges
section. The simplest way is to reuse built-in privilege profiles shipped with ldap2pg in an active custom profile.
Defining a Privilege Profile #
A privilege profile is a list of references to either a privilege type on an ACL or another profile. ldap2pg ships several predefined ACL like DATABASE
, LANGUAGE
, etc. A privilege type is USAGE
, CONNECT
and so on, as described in Section 5.7. See privileges
YAML section documentation for details on privilege profile format.
ldap2pg loads referenced ACL by inspecting Postgres Pro cluster with carefully crafted queries. ldap2pg inspects only ACL referenced in at least one profile. Inspected grants are supposed to revocation unless explicitly wanted by a grant
rule.
Warning
If it's not granted, revoke it!
Once an ACL is inspected, ldap2pg revokes all grants found in Postgres Pro instance and not required by a grant
rule in rules
.
Extended Intance Inspection #
When managing privileges, ldap2pg has deeper inspection of Postgres Pro instance. ldap2pg inspects schemas after roles synchronization and before synchronizing privileges. ldap2pg inspects objects owner after privileges synchronization and before synchronizing default privileges. An object owner is a role having CREATE
privilege on the schema.
Granting Privilege Profile #
Inspecting millions of privileges may consume a lot of resources on Postgres Pro instance. Revoking privileges is known to be slow in Postgres Pro. The best practice is to grant privileges to a group role and let user inherit privileges. With ldap2pg, you can define static groups in YAML and inherit them when creating roles from directory.
Use grant rule
to grant a privilege profile to one or more roles. When granting privileges, you must define the grantee. You may scope the grant to one or more databases, one or more schemas. If the privilege profile includes default privileges, you may define the owners on which to configure default privileges.
By default, a grant applies to all managed databases as returned by databases_query
, to all schemas of each database as returned by schemas_query
.
Example #
The following example defines three privileges profile. The rules
defines three groups and grant the corresponding privilege profile:
privileges: reading: - __connect__ - __usage_on_schemas__ - __select_on_tables__ writing: - reading # include reading privileges - __insert_on_tables__ - __update_on_tables__ owning: - writing - __create_on_schemas__ - __truncate_on_tables__ rules: - role: - names: - readers - writers - owners options: NOLOGIN - grant: - privilege: reading role: readers - privilege: writing role: writers - privilege: owning role: owners
Another way of including reading profile in writing is to writers group to inherit readers group.
Managing Public Privileges #
Postgres Pro has a pseudo-role called public
. It's a wildcard role meaning every users. All roles in Postgres Pro implicitly inherits from this public
role. Granting a privilege to public
role grants to every roles now and in the future.
Postgres Pro also has the public
schema. The public
schema is a real schema available in all databases.
Postgres Pro has some built-in privileges for public
role. Especially for the public
schema. For example, public
has CONNECT
on all databases by default. This means that you only rely on pg_hba.conf
to configure access to databases, which requires administrative access to the cluster and a pg_reload_conf()
call.
By default, ldap2pg includes public
role in managed roles. Predefined ACL knows how to inspect built-in privileges granted to public
. If you want to preserve public
role, rewrite managed_roles_query
to not include public
.
Managing Default Privileges #
If you grant SELECT
privileges on all tables in a schema to a role, this won't apply to new tables created afterward. Instead of reexecuting ldap2pg after the creation of every objects, Postgres Pro provides a way to define default privileges for future objects.
Postgres Pro attaches default privileges to the creator role. When the role creates an object, Postgres Pro applies the corresponding default privileges to the new object. e.g., ALTER DEFAULT PRIVILEGES FOR ROLE bob GRANT SELECT ON TABLES TO alice;
ensures every new table bob creates will be selectable by alice:
If ldap2pg creates and drops creator roles, you want ldap2pg to properly configure default privileges on these roles. If you wonder whether to manage privileges with ldap2pg, you should at least manage default privileges along creator.
ldap2pg inspects the creators from Postgres Pro, per schemas, not LDAP directory. A creator is a role with LOGIN
option and CREATE
privilege on a schema. You can manually set the target owner of a grant to any managed roles.
ldap2pg does not configure privileges on __all__
schemas. You are supposed to use GLOBAL DEFAULT
ACL instead. If you want to grant/revoke default privilege per schema, you must reference SCHEMA DEFAULT
ACL.
The following example configures default privileges for alice to allow bob to SELECT on future tables created by alice.
privileges: reading: - type: SELECT on: SCHEMA DEFAULT object: TABLES owning: - type: CREATE on: SCHEMAS rules: - roles: names: - alice - bob options: LOGIN - grant: privilege: owning role: alice - grant: privilege: reading role: bob
Postgres Pro has hard-wired global default privileges. If a role does not have global default privileges configured, Postgres Pro assumes some defaults. By default, Postgres Pro just grants privileges on owner. You can see them once you modify the default privileges. Postgres Pro will copy the hard-wired values along with your granted privileges.
If you don't explicitly re-grant these privileges in ldap2pg.yml
, ldap2pg will revoke these hard-wired privileges. Actually, an owner of table don't need to be granted SELECT on its own tables. Thus, the hard-wired defaults are useless. You can let ldap2pg purge these useless defaults.
Built-in Privileges #
ldap2pg provides some built-in ACLs and predefined privilege profiles for recurrent usage. There is no warranty on these privileges. You have to check privileges configuration on your databases just like you should do with your own code.
Using Predefined Privilege Profiles #
A privilege profile is a list of references to a privilege type in an ACL. In ldap2pg, an ACL is a set of queries to inspect, grant revoke privilege on a class of objects. The inspect query expands aclitem
Postgres Pro type to list all grants from system catalog. Privilege profile can include another profile.
Built-in privilege profile starts and ends with __
. ldap2pg disables privilege profile starting with _
. Thus you have to include built-in privileges profile in another profile to enable them. If two profiles reference the same privilege, ldap2pg will inspect it once.
privileges: ro: - __connect__ - __usage_on_schemas__ - __select_on_tables__ rw: - ro - __insert__ ddl: - rw - __all_on_schemas__ rules: - grant: privilege: ddl database: mydb role: admins
Built-in profile's name follows the loose convention below:
..._on_all_tables__
referencesALL TABLES IN SCHEMA
ACL. Likewise for sequences and functions.__default_...__
references both global and schema-wide default privileges.__..._on_tables__
groups__..._on_all_tables__
and__default_..._on_tables__
.Group starting with
__all_on_...__
is equivalent toALL PRIVILEGES
in SQL. However, each privilege will be granted individually.A privilege specific to one object type does not have
_on_<type>
suffix. E.g.,__delete_on_tables__
is an alias of__delete__
.
This page does not document the SQL standard and the meaning of each SQL privileges. You will find information on SQL privileges in GRANT
documentation and ALTER DEFAULT PRIVILEGES
documentation.
ACL Reference #
Here is the list of built-in ACLs.
For effective privileges:
DATABASE
Privilege on database like
CONNECT
,CREATE
, etc.SCHEMA
Manage
USAGE
andCREATE
on schema.LANGUAGE
Manage
USAGE
on procedural languages.ALL FUNCTIONS IN SCHEMA
Manage
EXECUTE
on all functions per schema.ALL SEQUENCES IN SCHEMA
Like above but for sequences.
ALL TABLES IN SCHEMA
Like above but for tables and views.
GLOBAL DEFAULT
Manage default privileges on database.
SCHEMA DEFAULT
Manage default privileges per schema.
ALL ... IN SCHEMA
ACL inspects whether a privilege is granted to only a subset of objects. This is a partial grant. A partial grant is either revoked if unwanted or re-granted if expected.
You can reference these ACLs using privileges: on parameter in YAML. Like this:
privileges: myprofile: - type: SELECT on: ALL TABLES IN SCHEMA
Default privileges reference a privilege type and a class of objects. ldap2pg inspects default privileges for the following object classes:
SEQUENCES
FUNCTIONS
TABLES
You must reference object class in privilege profile using object parameter in YAML.
You cannot configure custom ACL (yet).
Profiles Reference #
- Profile
__all_on_functions__
# - Profile
__all_on_schemas__
# - Profile
__all_on_sequences__
# - Profile
__all_on_tables__
# - Profile
__delete_on_tables__
# - Profile
__execute_on_functions__
# - Profile
__insert_on_tables__
# - Profile
__references_on_tables__
# - Profile
__select_on_sequences__
# - Profile
__select_on_tables__
# - Profile
__trigger_on_tables__
# - Profile
__truncate_on_tables__
# - Profile
__update_on_sequences__
# - Profile
__update_on_tables__
# - Profile
__usage_on_sequences__
#
Privileges Reference #
Here is the list of predefined privileges:
Name | Manages |
---|---|
__connect__ | CONNECT ON DATABASE |
__create_on_schemas__ | CREATE ON SCHEMA |
__delete_on_all_tables__ | DELETE ON ALL TABLES IN SCHEMA |
__execute_on_all_functions__ | EXECUTE ON ALL FUNCTIONS IN SCHEMA |
__insert_on_all_tables__ | INSERT ON ALL TABLES IN SCHEMA |
__references_on_all_tables__ | REFERENCES ON ALL TABLES IN SCHEMA |
__select_on_all_sequences__ | SELECT ON ALL SEQUENCES IN SCHEMA |
__select_on_all_tables__ | SELECT ON ALL TABLES IN SCHEMA |
TEMPORARY ON DATABASE | |
__trigger_on_all_tables__ | TRIGGER ON ALL TABLES IN SCHEMA |
__truncate_on_all_tables__ | TRUNCATE ON ALL TABLES IN SCHEMA |
__update_on_all_sequences__ | UPDATE ON ALL SEQUENCES IN SCHEMA |
__update_on_all_tables__ | UPDATE ON ALL TABLES IN SCHEMA |
__usage_on_all_sequences__ | USAGE ON ALL SEQUENCES IN SCHEMA |
__usage_on_schemas__ | USAGE ON SCHEMA |
Default Privileges Reference #
Here is the list of predefined default privileges. Default privilege profile references both global and schema defaults.
Name | Manages |
---|---|
__default_delete_on_tables__ | DELETE ON TABLES |
__default_execute_on_functions__ | EXECUTE ON FUNCTIONS |
__default_insert_on_tables__ | INSERT ON TABLES |
__default_references_on_tables__ | REFERENCES ON TABLES |
__default_select_on_sequences__ | SELECT ON SEQUENCES |
__default_select_on_tables__ | SELECT ON TABLES |
__default_trigger_on_tables__ | TRIGGER ON TABLES |
__default_truncate_on_tables__ | TRUNCATE ON TABLES |
__default_update_on_sequences__ | UPDATE ON SEQUENCES |
__default_update_on_tables__ | UPDATE ON TABLES |
__default_usage_on_sequences__ | USAGE ON SEQUENCES |
Usage Recipes #
In this section, you'll find some recipes for various use cases of ldap2pg.
Configure pg_hba.conf
with LDAP #
ldap2pg does NOT configure Postgres Pro for you. You should carefully read Section 19.10 for this point. Having Postgres Pro properly configured before writing ldap2pg.yaml
is a good start. Here is the steps to setup Postgres Pro with LDAP in the best order:
Write the LDAP search and test it with
ldapsearch(1)
. This way, you can also check how you connect to your LDAP directory.In Postgres Pro cluster, manually create a single role having its password in LDAP directory.
Edit
pg_hba.conf
following Section 19.10 until you can effectively login with the single role and the password from LDAP.
Once you have LDAP authentication configured in Postgres Pro cluster, you can move to automate role creation from the LDAP directory using ldap2pg:
Write a simple
ldap2pg.yaml
with only one LDAP search just to setup ldap2pg connection parameters for Postgres Pro and LDAP connection. ldap2pg always runs in dry mode by default, so you can safely loop ldap2pg execution until you get it right.Then, complete
ldap2pg.yaml
to fit your needs following the section called “Command-Line Reference”. Run ldap2pg for real and check that ldap2pg maintain your single test role, and that you can still connect to the cluster with it.Finally, you must decide when and how you want to trigger synchronization: a regular cron tab/an Ansible task/manually/ other. Ensure ldap2pg execution is frequent, on purpose and notified.
Search LDAP Directory #
The first step is to search your LDAP server with ldapsearch(1)
, the command-line interface tool from OpenLDAP. Like this:
$ ldapsearch -H ldaps://ldap.ldap2pg.docker -U testsasl -W Enter LDAP Password: SASL/DIGEST-MD5 authentication started SASL username: testsasl SASL SSF: 128 SASL data security layer installed. # extended LDIF # # LDAPv3 ... # search result search: 4 result: 0 Success # numResponses: 16 # numEntries: 15 $
Now save the settings in ldaprc
:
LDAPURI ldaps://ldap.ldap2pg.docker LDAPSASL_AUTHCID testsasl
And in environment: LDAPPASSWORD=secret
Next, update your ldapsearch
to properly match role entries in LDAP server:
$ ldapsearch -H ldaps://ldap.ldap2pg.docker -U testsasl -W -b cn=dba,ou=groups,dc=ldap,dc=ldap2pg,dc=docker '' member ... # dba, groups, ldap.ldap2pg.docker dn: cn=dba,ou=groups,dc=ldap,dc=ldap2pg,dc=docker member: cn=Alan,ou=people,dc=ldap,dc=ldap2pg,dc=docker member: cn=albert,ou=people,dc=ldap,dc=ldap2pg,dc=docker member: cn=ALICE,ou=people,dc=ldap,dc=ldap2pg,dc=docker # search result search: 4 result: 0 Success ... $
Now translate the query in ldap2pg.yaml
and associate a role mapping to produce roles from each values of each entries returned by the LDAP search:
- ldapsearch: base: cn=dba,ou=groups,dc=ldap,dc=ldap2pg,dc=docker role: name: '{member.cn}' options: LOGIN SUPERUSER
Test it:
$ ldap2pg ... Querying LDAP cn=dba,ou=groups,dc=ldap,dc=ldap2pg,dc=docker... Would create alan. Would create albert. Would update options of alice. ... Comparison complete. $
Read further on how to control role creation from LDAP entry in Configuration. Once you're satisfied with the comparison output, go real with --real
.
Using LDAP High-Availability #
ldap2pg supports LDAP high availability out of the box just like any OpenLDAP client. Use a space-separated list of URIs to tell all servers.
$ LDAPURI="ldaps://ldap1 ldaps://ldap2" ldap2pg
See ldap.conf(5) for further details.
Running as Non-Superuser #
Since Postgres Pro> provides a CREATEROLE
role option, you can manage roles without superuser privileges. Security-wise, it's a good idea to manage roles without superuser privileges.
Warning
Up to Postgres Pro 15, having CREATEROLE
is roughly equivalent to being superuser. This because CREATEROLE
user can grant themselves almost every privileges. Thus ldap2pg supports running unprivileged against Postgres Pro 16 and later only.
ldap2pg supports this case. However, you must be careful about the limitations. Let's call the non-superuser role creating other roles creator
.
You can't manage some roles options like
SUPERUSER
,BYPASSRLS
andREPLICATION
. Thus you won't be able to detect spurious superusers.Ensure
creator
can revoke all grants of managed users.
Removing All Roles #
If ever you want to clean all roles in a Postgres Pro cluster, ldap2pg could be helpful. You must explicitly define empty rules
.
$ echo '{version: 6, rules: []}' | ldap2pg --config - ... Empty synchronization map. All roles will be dropped! ...
In this example, default blacklist applies. ldap2pg never drops its connection role.
ldap2pg as Docker Container #
We assume that you are already familiar with Docker and willing to save the setup time.
To run the container simply use the command:
$ docker run --rm dalibo/ldap2pg --help
The Docker image of ldap2pg uses the same configuration options as explained in the Command-Line Interface and ldap2pg.yml
sections. You can mount the ldap2pg.yml
configuration file.
$ docker run --rm -v ${PWD}/ldap2pg.yml:/workspace/ldap2pg.yml dalibo/ldap2pg
You can also export some environmnent variables with the -e
option:
$ docker run --rm -v ${PWD}/ldap2pg.yml:/workspace/ldap2pg.yml -e PGDSN=postgres://postgres@localhost:5432/ -e LDAPURI=ldaps://localhost -e LDAPBINDDN=cn=you,dc=entreprise,dc=fr -e LDAPPASSWORD=pasglop dalibo/ldap2pg
Make sure your container can resolve the hostname you are pointing to. If you use some internal name resolution, be sure to add the -dns=
option to your command pointing to your internal DNS server. You can also find more information here.
Custom ACL #
ldap2pg comes with built-in ACLs for common objects like DATABASE
, SCHEMA
, TABLE
, FUNCTION
, etc. Postgres Pro has a lot of other objects like FOREIGN DATA WRAPPER
, FOREIGN SERVER
, FOREIGN TABLE
, TYPE
, etc. You may also want to manage custom ACL or something else. Writing a custom ACL should help you get the job done.
Note
Writing a custom ACL is quite advanced. You should be familiar with Postgres Pro ACLs and ldap2pg configuration. Ensure you have read Managing Privileges and ldap2pg.yml
File Reference sections, have a good understanding of Postgres Pro ACL documentation and have successfully synchronized privileges with ldap2pg.
Define custom ACL in YAML configuration file.
Use Case #
Say you have a custom enum
type myenum
in your database.
CREATE TYPE public.myenum AS ENUM ('toto', 'titi', 'tata');
We want to manage privileges on this object, eventually other types, with a custom ACL.
Naming #
Name your ACL after the keyword in GRANT
or REVOKE
statement. From GRANT USAGE ON TYPE mytype TO myrole
, name your ACL TYPE
.
acls: TYPE: ...
Scope #
Postgres Pro defines user types per schema. Since we'll hardcode the schema, we will scope our ACL to database.
acls: TYPE: scope: database
Grant and Revoke #
Writing GRANT
and REVOKE
statements is the easiest part. See grant for details on query format. We'll use the object
field of grant to store the name of the type. Public schema is hardcoded, as explained above.
acls: TYPE: scope: database grant: GRANT <privilege> ON TYPE public.<object> TO <grantee>; revoke: REVOKE <privilege> ON TYPE public.<object> FROM <grantee>;
Inspect #
The inspect query is the most difficult part. You need to master aclitem
, aclexplode
and acldefault
Postgres Pro system built-ins. The signature of the inspect query depends on the scope of the ACL.
For instance
scope:
type
: a string describing privilege type as SQL keyword.object
: a string describing object name as SQL identifier.grantee
: a string describing role name as SQL identifier.
For database
scope:
type
: a string describing privilege type as SQL keyword.object
: a string describing object name as SQL identifier.grantee
: a string describing role name as SQL identifier.partial
: a boolean indicating if the grant is partial.
partial
tells ldap2pg to re-grant ALL ... IN SCHEMA
privileges. Since our ACL is handling one object at a time, partial
will always be false
.
ldap2pg sends a single parameter to inspect query: the effective list of privilege types managed by the configuration. This list is an array of text. ldap2pg expects query to filter other privileges out of the list.
For TYPE
ACL, we will inspect privileges on pg_type
system catalog.
acls: TYPE: scope: database grant: GRANT <privilege> ON <acl> public.<object> TO <grantee>; revoke: REVOKE <privilege> ON <acl> public.<object> FROM <grantee>; inspect: | WITH grants AS ( SELECT typname, (aclexplode(COALESCE(typacl, acldefault('T', typowner)))).privilege_type AS priv, (aclexplode(COALESCE(typacl, acldefault('T', typowner)))).grantee::regrole::text AS grantee FROM pg_catalog.pg_type WHERE typnamespace::regnamespace = 'public'::regnamespace AND typtype <> 'b' -- exclude base type. ) SELECT grants.priv AS "privilege", grants.typname AS "object", CASE grants.grantee WHEN '-' THEN 'public' ELSE grants.grantee END AS grantee, FALSE AS partial FROM grants WHERE "priv" = ANY ($1) ORDER BY 2, 3, 1 ;
As you see, it's not an easy query. This query works on Postgres Pro 17.
Usage #
You can now use your custom ACL in a profile. Reference object in profile, not in grant rule.
privileges: custom: - type: USAGE on: TYPE object: myenum rules: - roles: names: - alice grant: privilege: custom role: alice
You must reference all types manually in the profile. Executing ldap2pg should produce changes in your database:
$ ldap2pg ... 16:52:02 CHANGE Would Revoke privileges. grant="USAGE ON TYPE myenum TO public" database=db0 16:52:02 CHANGE Would Grant privileges. grant="USAGE ON TYPE myenum TO alice" database=db0 16:52:02 INFO Comparison complete. searches=0 roles=1 queries=5 grants=1 16:52:02 INFO Use --real option to apply changes. 16:52:02 INFO Done. elapsed=44.345229ms mempeak=1.6MiB ldap=0s inspect=28.992071ms sync=0s
That's it.
Debugging #
If you encounter problem, isolate the issue. Reduce the configuration to your custom ACL. Synchronize a single role, grant to it. Avoid LDAP searches, use only static rules. Synchronize a single database. Enable debug messages with --verbose
option.
ldap2pg works database per database then ACL per ACL. The messages for each ACL are as follow:
First line about your ACL has acl=TYPE
record attribute.
17:13:35 DEBUG Inspecting grants. acl=TYPE scope=database database=db0
Then you have messages for inspection: query and arguments. For each grant returned, a Found grant.
message appears.
17:13:35 DEBUG Executing SQL query: WITH grants AS ( ... WHERE "priv" = ANY ($1) ORDER BY 2, 3, 1 ; arg=[USAGE] 17:13:35 DEBUG Found grant in Postgres instance. grant="USAGE ON TYPE myenum TO public" database=db0
Then, ldap2pg expands grants generated by rule. For each grant generated, a Wants grant.
message is printed.
17:13:35 DEBUG Wants grant. grant="USAGE ON TYPE myenum TO alice" database=db0
Finally, ldap2pg prints changes it would apply.
17:13:35 CHANGE Would Revoke privileges. grant="USAGE ON TYPE myenum TO public" database=db0 17:13:35 DEBUG Would Execute SQL query: REVOKE USAGE ON TYPE public."myenum" FROM "public"; 17:13:35 CHANGE Would Grant privileges. grant="USAGE ON TYPE myenum TO alice" database=db0 17:13:35 DEBUG Would Execute SQL query: GRANT USAGE ON TYPE public."myenum" TO "alice";
At the end, ldap2pg prints a conclusion message, even if no changes are required.
17:13:35 DEBUG Privileges synchronized. acl=TYPE database=db0