Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure a sensible timeout for pgsql commands #61433

Merged
merged 4 commits into from
Nov 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
22 changes: 20 additions & 2 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,6 +76,7 @@


_DEFAULT_PASSWORDS_ENCRYPTION = "md5"
_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 @@ -154,6 +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": __salt__["config.option"](
"postgres.timeout", default=_DEFAULT_COMMAND_TIMEOUT_SECS
),
}
if runas is None:
if not host:
Expand Down Expand Up @@ -254,7 +266,13 @@ def _run_initdb(
__salt__["file.chown"](pgpassfile, runas, "")
cmd.extend(["--pwfile={}".format(pgpassfile)])

kwargs = dict(runas=runas, clean_env=True)
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 @@ -1205,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)