diff --git a/docs/ENVIRONMENT.rst b/docs/ENVIRONMENT.rst index b08b3dbe..f3fedd67 100644 --- a/docs/ENVIRONMENT.rst +++ b/docs/ENVIRONMENT.rst @@ -89,10 +89,25 @@ PostgreSQL - **PATRONI\_POSTGRESQL\_PGPASS**: path to the `.pgpass `__ password file. Patroni creates this file before executing pg\_basebackup and under some other circumstances. The location must be writable by Patroni. - **PATRONI\_REPLICATION\_USERNAME**: replication username; the user will be created during initialization. Replicas will use this user to access master via streaming replication - **PATRONI\_REPLICATION\_PASSWORD**: replication password; the user will be created during initialization. +- **PATRONI\_REPLICATION\_SSLMODE**: (optional) maps to the `sslmode `__ connection parameter, which allows a client to specify the type of TLS negotiation mode with the server. For more information on how each mode works, please visit the `PostgreSQL documentation `__. The default mode is ``prefer``. +- **PATRONI\_REPLICATION\_SSLKEY**: (optional) maps to the `sslkey `__ connection parameter, which specifies the location of the secret key used with the client's certificate. +- **PATRONI\_REPLICATION\_SSLCERT**: (optional) maps to the `sslcert `__ connection parameter, which specifies the location of the client certificate. +- **PATRONI\_REPLICATION\_SSLROOTCERT**: (optional) maps to the `sslrootcert `__ connection parameter, which specifies the location of a file containing one ore more certificate authorities (CA) certificates that the client will use to verify a server's certificate. +- **PATRONI\_REPLICATION\_SSLCRL**: (optional) maps to the `sslcrl `__ connection parameter, which specifies the location of a file containing a certificate revocation list. A client will reject connecting to any server that has a certificate present in this list. - **PATRONI\_SUPERUSER\_USERNAME**: name for the superuser, set during initialization (initdb) and later used by Patroni to connect to the postgres. Also this user is used by pg_rewind. - **PATRONI\_SUPERUSER\_PASSWORD**: password for the superuser, set during initialization (initdb). +- **PATRONI\_SUPERUSER\_SSLMODE**: (optional) maps to the `sslmode `__ connection parameter, which allows a client to specify the type of TLS negotiation mode with the server. For more information on how each mode works, please visit the `PostgreSQL documentation `__. The default mode is ``prefer``. +- **PATRONI\_SUPERUSER\_SSLKEY**: (optional) maps to the `sslkey `__ connection parameter, which specifies the location of the secret key used with the client's certificate. +- **PATRONI\_SUPERUSER\_SSLCERT**: (optional) maps to the `sslcert `__ connection parameter, which specifies the location of the client certificate. +- **PATRONI\_SUPERUSER\_SSLROOTCERT**: (optional) maps to the `sslrootcert `__ connection parameter, which specifies the location of a file containing one ore more certificate authorities (CA) certificates that the client will use to verify a server's certificate. +- **PATRONI\_SUPERUSER\_SSLCRL**: (optional) maps to the `sslcrl `__ connection parameter, which specifies the location of a file containing a certificate revocation list. A client will reject connecting to any server that has a certificate present in this list. - **PATRONI\_REWIND\_USERNAME**: name for the user for ``pg_rewind``; the user will be created during initialization of postgres 11+ and all necessary `permissions `__ will be granted. - **PATRONI\_REWIND\_PASSWORD**: password for the user for ``pg_rewind``; the user will be created during initialization. +- **PATRONI\_REWIND\_SSLMODE**: (optional) maps to the `sslmode `__ connection parameter, which allows a client to specify the type of TLS negotiation mode with the server. For more information on how each mode works, please visit the `PostgreSQL documentation `__. The default mode is ``prefer``. +- **PATRONI\_REWIND\_SSLKEY**: (optional) maps to the `sslkey `__ connection parameter, which specifies the location of the secret key used with the client's certificate. +- **PATRONI\_REWIND\_SSLCERT**: (optional) maps to the `sslcert `__ connection parameter, which specifies the location of the client certificate. +- **PATRONI\_REWIND\_SSLROOTCERT**: (optional) maps to the `sslrootcert `__ connection parameter, which specifies the location of a file containing one ore more certificate authorities (CA) certificates that the client will use to verify a server's certificate. +- **PATRONI\_REWIND\_SSLCRL**: (optional) maps to the `sslcrl `__ connection parameter, which specifies the location of a file containing a certificate revocation list. A client will reject connecting to any server that has a certificate present in this list. REST API -------- diff --git a/docs/SETTINGS.rst b/docs/SETTINGS.rst index 4038a95d..f238bfcd 100644 --- a/docs/SETTINGS.rst +++ b/docs/SETTINGS.rst @@ -136,13 +136,28 @@ PostgreSQL - **superuser**: - **username**: name for the superuser, set during initialization (initdb) and later used by Patroni to connect to the postgres. - **password**: password for the superuser, set during initialization (initdb). + - **sslmode**: (optional) maps to the `sslmode `__ connection parameter, which allows a client to specify the type of TLS negotiation mode with the server. For more information on how each mode works, please visit the `PostgreSQL documentation `__. The default mode is ``prefer``. + - **sslkey**: (optional) maps to the `sslkey `__ connection parameter, which specifies the location of the secret key used with the client's certificate. + - **sslcert**: (optional) maps to the `sslcert `__ connection parameter, which specifies the location of the client certificate. + - **sslrootcert**: (optional) maps to the `sslrootcert `__ connection parameter, which specifies the location of a file containing one ore more certificate authorities (CA) certificates that the client will use to verify a server's certificate. + - **sslcrl**: (optional) maps to the `sslcrl `__ connection parameter, which specifies the location of a file containing a certificate revocation list. A client will reject connecting to any server that has a certificate present in this list. - **replication**: - **username**: replication username; the user will be created during initialization. Replicas will use this user to access master via streaming replication - **password**: replication password; the user will be created during initialization. + - **sslmode**: (optional) maps to the `sslmode `__ connection parameter, which allows a client to specify the type of TLS negotiation mode with the server. For more information on how each mode works, please visit the `PostgreSQL documentation `__. The default mode is ``prefer``. + - **sslkey**: (optional) maps to the `sslkey `__ connection parameter, which specifies the location of the secret key used with the client's certificate. + - **sslcert**: (optional) maps to the `sslcert `__ connection parameter, which specifies the location of the client certificate. + - **sslrootcert**: (optional) maps to the `sslrootcert `__ connection parameter, which specifies the location of a file containing one ore more certificate authorities (CA) certificates that the client will use to verify a server's certificate. + - **sslcrl**: (optional) maps to the `sslcrl `__ connection parameter, which specifies the location of a file containing a certificate revocation list. A client will reject connecting to any server that has a certificate present in this list. - **rewind**: - **username**: name for the user for ``pg_rewind``; the user will be created during initialization of postgres 11+ and all necessary `permissions `__ will be granted. - **password**: password for the user for ``pg_rewind``; the user will be created during initialization. - **callbacks**: callback scripts to run on certain actions. Patroni will pass the action, role and cluster name. (See scripts/aws.py as an example of how to write them.) + - **sslmode**: (optional) maps to the `sslmode `__ connection parameter, which allows a client to specify the type of TLS negotiation mode with the server. For more information on how each mode works, please visit the `PostgreSQL documentation `__. The default mode is ``prefer``. + - **sslkey**: (optional) maps to the `sslkey `__ connection parameter, which specifies the location of the secret key used with the client's certificate. + - **sslcert**: (optional) maps to the `sslcert `__ connection parameter, which specifies the location of the client certificate. + - **sslrootcert**: (optional) maps to the `sslrootcert `__ connection parameter, which specifies the location of a file containing one ore more certificate authorities (CA) certificates that the client will use to verify a server's certificate. + - **sslcrl**: (optional) maps to the `sslcrl `__ connection parameter, which specifies the location of a file containing a certificate revocation list. A client will reject connecting to any server that has a certificate present in this list. - **on\_reload**: run this script when configuration reload is triggered. - **on\_restart**: run this script when the postgres restarts (without changing role). - **on\_role\_change**: run this script when the postgres is being promoted or demoted. diff --git a/patroni/config.py b/patroni/config.py index b73a6fea..1b454704 100644 --- a/patroni/config.py +++ b/patroni/config.py @@ -15,6 +15,16 @@ from requests.structures import CaseInsensitiveDict logger = logging.getLogger(__name__) +AUTH_ALLOWED_PARAMETERS = ( + 'username', + 'password', + 'sslmode', + 'sslcert', + 'sslkey', + 'sslrootcert', + 'sslcrl', +) + class Config(object): """ @@ -252,7 +262,7 @@ class Config(object): def _get_auth(name): ret = {} - for param in ('username', 'password'): + for param in AUTH_ALLOWED_PARAMETERS: value = _popenv(name + '_' + param) if value: ret[param] = value @@ -353,6 +363,11 @@ class Config(object): if 'superuser' not in pg_config['authentication'] and 'pg_rewind' in pg_config: pg_config['authentication']['superuser'] = pg_config['pg_rewind'] + # handle setting additional connection parameters that may be available + # in the configuration file, such as SSL connection parameters + for name, value in pg_config['authentication'].items(): + pg_config['authentication'][name] = {n: v for n, v in value.items() if n in AUTH_ALLOWED_PARAMETERS} + # no 'name' in config if 'name' not in config and 'name' in pg_config: config['name'] = pg_config['name'] diff --git a/patroni/dcs/__init__.py b/patroni/dcs/__init__.py index 3fa7239a..8acc2354 100644 --- a/patroni/dcs/__init__.py +++ b/patroni/dcs/__init__.py @@ -157,11 +157,12 @@ class Member(namedtuple('Member', 'index,name,session,data')): } self.data['conn_kwargs'] = ret.copy() + # apply any remaining authentication parameters if auth and isinstance(auth, dict): + ret.update(auth) if 'username' in auth: ret['user'] = auth['username'] - if 'password' in auth: - ret['password'] = auth['password'] + del ret['username'] return ret @property diff --git a/patroni/postgresql/__init__.py b/patroni/postgresql/__init__.py index 988577f6..1896906c 100644 --- a/patroni/postgresql/__init__.py +++ b/patroni/postgresql/__init__.py @@ -646,9 +646,11 @@ class Postgresql(object): @contextmanager def get_replication_connection_cursor(self, host='localhost', port=5432, database=None, **kwargs): replication = self.config.replication + extra_kwargs = {k: v for k, v in replication.items() + if k not in ('username', 'password', 'connect_timeout', 'options')} with get_connection_cursor(host=host, port=int(port), database=database or self._database, replication=1, user=replication['username'], password=replication.get('password'), - connect_timeout=3, options='-c statement_timeout=2000') as cur: + connect_timeout=3, options='-c statement_timeout=2000', **extra_kwargs) as cur: yield cur def get_local_timeline_lsn_from_replication_connection(self): diff --git a/patroni/postgresql/config.py b/patroni/postgresql/config.py index 0c6dbf2e..93241542 100644 --- a/patroni/postgresql/config.py +++ b/patroni/postgresql/config.py @@ -187,6 +187,23 @@ class ConfigHandler(object): 'wal_log_hints': ('on', lambda _: False, 90400) }) + # A list of keywords that can be found in a conninfo string. Follows what + # is acceptable by libpq + _CONNINFO_KEYWORDS = ( + 'user', + 'password', + 'host', + 'port', + 'sslmode', + 'sslcompression', + 'sslcert', + 'sslkey', + 'sslrootcert', + 'sslcrl', + 'application_name', + 'krbsrvname', + ) + _RECOVERY_PARAMETERS = { 'archive_cleanup_command', 'restore_command', @@ -377,8 +394,7 @@ class ConfigHandler(object): r = self.primary_conninfo_params(member) if not r: return None - keywords = 'user password host port sslmode application_name krbsrvname'.split() - return ' '.join('{0}={{{0}}}'.format(kw) for kw in keywords if r.get(kw)).format(**r) + return ' '.join('{0}={{{0}}}'.format(kw) for kw in self._CONNINFO_KEYWORDS if r.get(kw)).format(**r) def recovery_conf_exists(self): if self._postgresql.major_version >= 120000: @@ -568,14 +584,18 @@ class ConfigHandler(object): @property def local_connect_kwargs(self): ret = self._local_address.copy() + # add all of the other connection settings that are available + ret.update(self._superuser) + # if the "username" parameter is present, it actually needs to be "user" + # for connecting to PostgreSQL + if 'username' in self._superuser: + ret['user'] = self._superuser['username'] + del ret['username'] + # ensure certain Patroni configurations are available ret.update({'database': self._postgresql.database, 'fallback_application_name': 'Patroni', 'connect_timeout': 3, 'options': '-c statement_timeout=2000'}) - if 'username' in self._superuser: - ret['user'] = self._superuser['username'] - if 'password' in self._superuser: - ret['password'] = self._superuser['password'] return ret def resolve_connection_addresses(self):