diff --git a/docs/releases.rst b/docs/releases.rst index e10a94ed..4e26233a 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -3,6 +3,20 @@ Release notes ============= +Version 1.6.3 +------------- + +**Bugfixes** + +- Don't expose password when running ``pg_rewind`` (Alexander Kukushkin) + + Bug was introduced in the `#1301 `__ + +- Apply connection parameters specified in the ``postgresql.authentication`` to ``pg_basebackup`` and custom replica creation methods (Alexander) + + They were relying on url-like connection string and therefore parameters never applied. + + Version 1.6.2 ------------- diff --git a/patroni/postgresql/bootstrap.py b/patroni/postgresql/bootstrap.py index 2f693443..16266c70 100644 --- a/patroni/postgresql/bootstrap.py +++ b/patroni/postgresql/bootstrap.py @@ -5,9 +5,8 @@ import tempfile import time from patroni.dcs import RemoteMember -from patroni.utils import deep_compare, uri +from patroni.utils import deep_compare from six import string_types -from six.moves.urllib.parse import quote_plus logger = logging.getLogger(__name__) @@ -123,19 +122,13 @@ class Bootstrap(object): cmd = config.get('post_bootstrap') or config.get('post_init') if cmd: r = self._postgresql.config.local_connect_kwargs - - if 'host' in r: - # '/tmp' => '%2Ftmp' for unix socket path - host = quote_plus(r['host']) if r['host'].startswith('/') else r['host'] - else: - host = '' - + connstring = self._postgresql.config.format_dsn(r, True) + if 'host' not in r: # https://www.postgresql.org/docs/current/static/libpq-pgpass.html # A host name of localhost matches both TCP (host name localhost) and Unix domain socket # (pghost empty or the default socket directory) connections coming from the local machine. r['host'] = 'localhost' # set it to localhost to write into pgpass - connstring = uri('postgres', (host, r['port']), r['database'], r.get('user')) env = self._postgresql.config.write_pgpass(r) if 'password' in r else None try: @@ -168,9 +161,9 @@ class Bootstrap(object): if clone_member and clone_member.conn_url: r = clone_member.conn_kwargs(self._postgresql.config.replication) - connstring = uri('postgres', (r['host'], r['port']), r['database'], r['user']) # add the credentials to connect to the replica origin to pgpass. env = self._postgresql.config.write_pgpass(r) + connstring = self._postgresql.config.format_dsn(r, True) else: connstring = '' env = os.environ.copy() diff --git a/patroni/postgresql/config.py b/patroni/postgresql/config.py index 9939536f..db9926fd 100644 --- a/patroni/postgresql/config.py +++ b/patroni/postgresql/config.py @@ -480,17 +480,22 @@ class ConfigHandler(object): def format_dsn(self, params, include_dbname=False): # A list of keywords that can be found in a conninfo string. Follows what is acceptable by libpq - keywords = ('user', 'passfile' if params.get('passfile') else 'password', 'host', 'port', 'sslmode', + keywords = ('dbname', 'user', 'passfile' if params.get('passfile') else 'password', 'host', 'port', 'sslmode', 'sslcompression', 'sslcert', 'sslkey', 'sslrootcert', 'sslcrl', 'application_name', 'krbsrvname') if include_dbname: params = params.copy() params['dbname'] = params.get('database') or self._postgresql.database - keywords = ('dbname',) + keywords + # we are abusing information about the necessity of dbname + # dsn should contain passfile or password only if there is no dbname in it (it is used in recovery.conf) + skip = {'passfile', 'password'} + else: + skip = {'dbname'} def escape(value): return re.sub(r'([\'\\ ])', r'\\\1', str(value)) - return ' '.join('{0}={1}'.format(kw, escape(params[kw])) for kw in keywords if params.get(kw) is not None) + return ' '.join('{0}={1}'.format(kw, escape(params[kw])) for kw in keywords + if kw not in skip and params.get(kw) is not None) def _write_recovery_params(self, fd, recovery_params): for name, value in sorted(recovery_params.items()): diff --git a/patroni/version.py b/patroni/version.py index 4a9b9788..4574cc89 100644 --- a/patroni/version.py +++ b/patroni/version.py @@ -1 +1 @@ -__version__ = '1.6.2' +__version__ = '1.6.3' diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 904ec26b..29c38127 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -209,13 +209,13 @@ class TestBootstrap(BaseTestPostgresql): mock_cancellable_subprocess_call.assert_called() args, kwargs = mock_cancellable_subprocess_call.call_args self.assertTrue('PGPASSFILE' in kwargs['env']) - self.assertEqual(args[0], ['/bin/false', 'postgres://127.0.0.2:5432/postgres']) + self.assertEqual(args[0], ['/bin/false', 'dbname=postgres host=127.0.0.2 port=5432']) mock_cancellable_subprocess_call.reset_mock() self.p.config._local_address.pop('host') self.assertTrue(self.b.call_post_bootstrap({'post_init': '/bin/false'})) mock_cancellable_subprocess_call.assert_called() - self.assertEqual(mock_cancellable_subprocess_call.call_args[0][0], ['/bin/false', 'postgres://:5432/postgres']) + self.assertEqual(mock_cancellable_subprocess_call.call_args[0][0], ['/bin/false', 'dbname=postgres port=5432']) mock_cancellable_subprocess_call.side_effect = OSError self.assertFalse(self.b.call_post_bootstrap({'post_init': '/bin/false'}))