Skip to content

Commit

Permalink
add postgres.timeout option to stop unintentionally long queries
Browse files Browse the repository at this point in the history
  • Loading branch information
MKLeb authored and Megan Wilhite committed Nov 1, 2022
1 parent 29d8851 commit 637530a
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog/61433.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add postgres.timeout option to postgres module for limiting postgres query times
24 changes: 20 additions & 4 deletions salt/modules/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
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.
Expand Down Expand Up @@ -68,7 +76,7 @@


_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"
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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()
)
Expand Down
51 changes: 50 additions & 1 deletion tests/pytests/unit/modules/test_postgres.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -28,7 +29,8 @@ def configure_loader_modules():
"file.chown": MagicMock(),
"file.remove": MagicMock(),
},
}
},
configmod: {},
}


Expand Down Expand Up @@ -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)

0 comments on commit 637530a

Please sign in to comment.