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 Enterprise as a separate pre-built package ldap2pg (for the detailed installation instructions, see Chapter 17).
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 Enterprise cluster.
With 2MiB of RAM and one vCPU, ldap2pg can synchronize several thousands of roles in seconds, depending on Postgres Pro Enterprise 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:
--checkCheck mode: exits with 1 if Postgres Pro instance is unsynchronized.
--colorForce color output.
-cstring--configstringPath to YAML configuration file. Use - for stdin.
--directorystringPath to directory containing configuration files.
--helpShow help message and exit (default true).
--ldappassword-filestringPath to LDAP password file.
--quietcountDecrease log verbosity.
--realReal mode. Apply changes to Postgres Pro instance.
-skip-privilegesTurn off privilege synchronization.
--verbosecountIncrease log verbosity.
--versionShow 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.ymlin 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
_queryparameter 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.publicis a special built-in role in Postgres Pro. Ifmanaged_roles_queryreturnspublicrole in the list, ldap2pg will manage privileges onpublic. By default, ldap2pg managespublicprivileges.The following example tells ldap2pg to manage
publicrole,ldap_rolesand 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_queryonly 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 ifcnis 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 DEFAULTandSCHEMA DEFAULTACL 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: TABLEStype#Type of privilege as described in Section 5.8, e.g.,
SELECT,REFERENCES,USAGE, etc.The value can be either a single string or a list of strings. Plural form
typesis 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: SCHEMASon#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
searchwhile the word query is reserved for SQL query.ldapsearchdirectives allow and require LDAP attributes injection inroleandgrantrules using curly braces. See Querying Directory with LDAP for details.base,scopeandfilterThese 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_*) )joinsCustomizes LDAP sub-search. The
joinssection 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 thebaseattribute of sub-search. Likewise, ldap2pg infers attributes of sub-searches fromroleandgrantrules. 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
rolesis 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: NOLOGINcomment#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
namesis 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 asoptionsandparents.commentparameter 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_queryon 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,REPLICATIONandSUPERUSER. 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: yesconfig#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: 100Setting
configtonull(the default) will disable the feature for the role. Ifconfigis a dict, ldap2pg will drop parameter set in cluster but not defined in ldap2pg YAML. To reset all parameters, setconfigto 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
parentsis valid too. Parent role is granted withGRANT ROLE parent TO role;.parentparameter accepts LDAP attributes injection using curly braces. ldap2pg appliesroles_blacklist_queryon this parameter. Parent reference can be local roles not managed by ldap2pg.rules: - role: name: myrole parent: myparentbefore_create#SQL snippet to execute before role creation.
before_createaccepts 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_createaccepts 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
grantsis valid too.rules: - grant: privilege: reader databases: __all__ schema: public role: myroledatabaseScope the grant to one or more databases. May be a list of names. Plural form
databasesis 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).privilegeName of a privilege, within the privileges defined in
privilegesYAML section. May be a list of names. Plural formprivilegesis valid. Required, there is not default value. Accepts LDAP attribute injection using curly braces.roleName 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 formrolesis valid. Accepts LDAP attribute injection using curly braces. ldap2pg appliesroles_blacklist_queryon this parameter.schemaName 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 formschemasis valid. Accepts LDAP attribute injection using curly braces.This parameter is ignored for privileges on
DATABASEand other instance-wide or database-wide privileges.ownerName of role to configure default privileges for. Special value
__auto__fallbacks to managed roles havingCREATEprivilege on the target schema. May be a list of names. Plural formownersis 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.
aclsThe
aclstop 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
instanceordatabase.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
rulesand generate wanted roles list fromrolerules.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,DROPandALTER.
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:
BASEBINDDNPASSWORDREFERRALSSASL_AUTHCIDSASL_AUTHZIDSASL_MECHTIMEOUTTLS_REQCERTNETWORK_TIMEOUTURI
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_domitilleexternal_domitilleexternal_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
rulesand 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.8. 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 SCHEMAACL. 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 PRIVILEGESin 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:
DATABASEPrivilege on database like
CONNECT,CREATE, etc.SCHEMAManage
USAGEandCREATEon schema.LANGUAGEManage
USAGEon procedural languages.ALL FUNCTIONS IN SCHEMAManage
EXECUTEon all functions per schema.ALL SEQUENCES IN SCHEMALike above but for sequences.
ALL TABLES IN SCHEMALike above but for tables and views.
GLOBAL DEFAULTManage default privileges on database.
SCHEMA DEFAULTManage 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:
SEQUENCESFUNCTIONSTABLES
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 20.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.conffollowing Section 20.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.yamlwith 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.yamlto 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,BYPASSRLSandREPLICATION. Thus you won't be able to detect spurious superusers.Ensure
creatorcan 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