add postgres.timeout option to stop unintentionally long queries

This commit is contained in:
MKLeb 2022-10-31 21:15:25 -04:00 committed by Megan Wilhite
parent 29d88513b9
commit 637530a4ce
3 changed files with 71 additions and 5 deletions

1
changelog/61433.added Normal file
View file

@ -0,0 +1 @@
Add postgres.timeout option to postgres module for limiting postgres query times

View file

@ -16,6 +16,14 @@ Module to provide Postgres compatibility to salt.
This data can also be passed into pillar. Options passed into opts will
overwrite options passed into pillar
To prevent Postgres commands from running arbitrarily long, a timeout (in seconds) can be set
.. code-block:: yaml
postgres.timeout: 60
.. versionadded:: 3006.0
:note: This module uses MD5 hashing which may not be compliant with certain
security audits.
@ -68,7 +76,7 @@ log = logging.getLogger(__name__)
_DEFAULT_PASSWORDS_ENCRYPTION = "md5"
_DEFAULT_COMMAND_TIMEOUT_SECS = 60
_DEFAULT_COMMAND_TIMEOUT_SECS = 0
_EXTENSION_NOT_INSTALLED = "EXTENSION NOT INSTALLED"
_EXTENSION_INSTALLED = "EXTENSION INSTALLED"
_EXTENSION_TO_UPGRADE = "EXTENSION TO UPGRADE"
@ -155,7 +163,9 @@ def _run_psql(cmd, runas=None, password=None, host=None, port=None, user=None):
kwargs = {
"reset_system_locale": False,
"clean_env": True,
"timeout": _DEFAULT_COMMAND_TIMEOUT_SECS,
"timeout": __salt__["config.option"](
"postgres.timeout", default=_DEFAULT_COMMAND_TIMEOUT_SECS
),
}
if runas is None:
if not host:
@ -256,7 +266,13 @@ def _run_initdb(
__salt__["file.chown"](pgpassfile, runas, "")
cmd.extend(["--pwfile={}".format(pgpassfile)])
kwargs = dict(runas=runas, clean_env=True, timeout=_DEFAULT_COMMAND_TIMEOUT_SECS)
kwargs = dict(
runas=runas,
clean_env=True,
timeout=__salt__["config.option"](
"postgres.timeout", default=_DEFAULT_COMMAND_TIMEOUT_SECS
),
)
cmdstr = " ".join([pipes.quote(c) for c in cmd])
ret = __salt__["cmd.run_all"](cmdstr, python_shell=False, **kwargs)
@ -1207,7 +1223,7 @@ def _verify_password(role, password, verifier, method):
def _md5_password(role, password):
return "md5{}".format(
hashlib.md5(
hashlib.md5( # nosec
salt.utils.stringutils.to_bytes("{}{}".format(password, role))
).hexdigest()
)

View file

@ -1,5 +1,6 @@
import pytest
import salt.modules.config as configmod
import salt.modules.postgres as postgres
from tests.support.mock import MagicMock, patch
@ -28,7 +29,8 @@ def configure_loader_modules():
"file.chown": MagicMock(),
"file.remove": MagicMock(),
},
}
},
configmod: {},
}
@ -132,3 +134,50 @@ def test_has_privileges_with_function():
user="testuser",
runas="user",
)
def test__runpsql_with_timeout():
cmd_run_mock = MagicMock()
postgres_opts = {
"config.option": configmod.option,
"cmd.run_all": cmd_run_mock,
}
kwargs = {
"reset_system_locale": False,
"clean_env": True,
"runas": "saltuser",
"python_shell": False,
}
with patch.dict(postgres.__salt__, postgres_opts):
with patch.dict(
configmod.__opts__, {"postgres.timeout": 60, "postgres.pass": None}
):
postgres._run_psql("fakecmd", runas="saltuser")
cmd_run_mock.assert_called_with("fakecmd", timeout=60, **kwargs)
with patch.dict(configmod.__opts__, {"postgres.pass": None}):
postgres._run_psql("fakecmd", runas="saltuser")
cmd_run_mock.assert_called_with("fakecmd", timeout=0, **kwargs)
def test__run_initdb_with_timeout():
cmd_run_mock = MagicMock(return_value={})
postgres_opts = {
"config.option": configmod.option,
"cmd.run_all": cmd_run_mock,
}
kwargs = {
"clean_env": True,
"runas": "saltuser",
"python_shell": False,
}
cmd_str = "/fake/path --pgdata=fakename --username=saltuser --auth=password --encoding=UTF8"
with patch.dict(postgres.__salt__, postgres_opts):
with patch.object(postgres, "_find_pg_binary", return_value="/fake/path"):
with patch.dict(
configmod.__opts__, {"postgres.timeout": 60, "postgres.pass": None}
):
postgres._run_initdb("fakename", runas="saltuser")
cmd_run_mock.assert_called_with(cmd_str, timeout=60, **kwargs)
with patch.dict(configmod.__opts__, {"postgres.pass": None}):
postgres._run_initdb("fakename", runas="saltuser")
cmd_run_mock.assert_called_with(cmd_str, timeout=0, **kwargs)