BUGFIX: don't leak password when running pg_rewind (#1321)

In addition to that:
* enforce security settings from `postgresql.authention`
* update release notes
* bump version
* close https://github.com/zalando/patroni/issues/1320
This commit is contained in:
Alexander Kukushkin
2019-12-05 18:19:38 +01:00
committed by GitHub
parent b542e4b5f0
commit 08d6e5e50e
5 changed files with 29 additions and 17 deletions
+14
View File
@@ -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 <https://github.com/zalando/patroni/pull/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
-------------
+4 -11
View File
@@ -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()
+8 -3
View File
@@ -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()):
+1 -1
View File
@@ -1 +1 @@
__version__ = '1.6.2'
__version__ = '1.6.3'
+2 -2
View File
@@ -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'}))