Fix runas when using the onedir bundled packages

This commit is contained in:
Megan Wilhite 2022-09-02 12:58:30 -06:00
parent 1a57b8ea79
commit 46ac0767fb
5 changed files with 268 additions and 8 deletions

1
changelog/62565.fixed Normal file
View file

@ -0,0 +1 @@
Fix runas with cmd module when using the onedir bundled packages

View file

@ -25,6 +25,7 @@ import salt.utils.data
import salt.utils.files
import salt.utils.json
import salt.utils.path
import salt.utils.pkg
import salt.utils.platform
import salt.utils.powershell
import salt.utils.stringutils
@ -509,30 +510,51 @@ def _run(
env_cmd.extend(["-s", "--", shell, "-c"])
else:
env_cmd.extend(["-i", "--"])
env_cmd.extend([sys.executable])
elif __grains__["os"] in ["FreeBSD"]:
env_cmd = (
env_cmd = [
"su",
"-",
runas,
"-c",
"{} -c {}".format(shell, sys.executable),
)
]
elif __grains__["os_family"] in ["Solaris"]:
env_cmd = ("su", "-", runas, "-c", sys.executable)
env_cmd = ["su", "-", runas, "-c"]
elif __grains__["os_family"] in ["AIX"]:
env_cmd = ("su", "-", runas, "-c", sys.executable)
env_cmd = ["su", "-", runas, "-c"]
else:
env_cmd = ("su", "-s", shell, "-", runas, "-c", sys.executable)
env_cmd = ["su", "-s", shell, "-", runas, "-c"]
if not salt.utils.pkg.check_bundled():
if __grains__["os"] in ["FreeBSD"]:
env_cmd.extend(["{} -c {}".format(shell, sys.executable)])
else:
env_cmd.extend([sys.executable])
else:
with tempfile.NamedTemporaryFile("w", delete=False) as fp:
if __grains__["os"] in ["FreeBSD"]:
env_cmd.extend(
[
"{} -c {} python {}".format(
shell, sys.executable, fp.name
)
]
)
else:
env_cmd.extend(["{} python {}".format(sys.executable, fp.name)])
fp.write(py_code)
fp.seek(0)
shutil.chown(fp.name, runas)
msg = "env command: {}".format(env_cmd)
log.debug(log_callback(msg))
env_bytes, env_encoded_err = subprocess.Popen(
env_cmd,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
).communicate(salt.utils.stringutils.to_bytes(py_code))
if salt.utils.pkg.check_bundled():
os.remove(fp.name)
marker_count = env_bytes.count(marker_b)
if marker_count == 0:
# Possibly PAM prevented the login

View file

@ -6,6 +6,7 @@ import errno
import logging
import os
import re
import sys
import salt.utils.data
import salt.utils.files
@ -92,3 +93,12 @@ def match_version(desired, available, cmp_func=None, ignore_epoch=False):
):
return candidate
return None
def check_bundled():
"""
Gather run-time information to indicate if we are running from source or bundled.
"""
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
return True
return False

View file

@ -0,0 +1,25 @@
import pytest
import salt.modules.cmdmod as cmdmod
@pytest.fixture(scope="module")
def account():
with pytest.helpers.create_account(create_group=True) as _account:
yield _account
@pytest.fixture(scope="module")
def configure_loader_modules():
return {
cmdmod: {
"__grains__": {"os": "linux", "os_family": "linux"},
}
}
@pytest.mark.skip_on_windows
@pytest.mark.skip_if_not_root
def test_run_as(account, caplog):
ret = cmdmod.run("id", runas=account.username)
assert "gid={}".format(account.info.gid) in ret
assert "uid={}".format(account.info.uid) in ret

View file

@ -844,3 +844,205 @@ def test_cmd_script_saltenv_from_config_windows():
assert mock_cp_get_template.call_args[0][3] == "base"
assert mock_run.call_count == 2
assert mock_run.call_args[1]["saltenv"] == "base"
@pytest.mark.parametrize(
"test_os,test_family",
[
("FreeBSD", "FreeBSD"),
("linux", "Solaris"),
("linux", "AIX"),
("linux", "linux"),
],
)
@pytest.mark.skip_on_darwin
@pytest.mark.skip_on_windows
def test_runas_env_all_os(test_os, test_family):
"""
cmd.run executes command and the environment is returned
when the runas parameter is specified
on all different OS types and os_family
"""
bundled = [False, True]
for _bundled in bundled:
with patch("pwd.getpwnam") as getpwnam_mock:
with patch("subprocess.Popen") as popen_mock:
popen_mock.return_value = Mock(
communicate=lambda *args, **kwags: [b"", None],
pid=lambda: 1,
retcode=0,
)
file_name = "/tmp/doesnotexist"
with patch.dict(
cmdmod.__grains__, {"os": test_os, "os_family": test_family}
):
with patch("salt.utils.pkg.check_bundled", return_value=_bundled):
with patch("shutil.chown"):
with patch("os.remove"):
with patch.object(
tempfile, "NamedTemporaryFile"
) as mock_fp:
mock_fp.return_value.__enter__.return_value.name = (
file_name
)
if sys.platform.startswith(("freebsd", "openbsd")):
shell = "/bin/sh"
else:
shell = "/bin/bash"
_user = "foobar"
cmdmod._run(
"ls",
cwd=tempfile.gettempdir(),
runas=_user,
shell=shell,
)
if not _bundled:
if test_family in ("Solaris", "AIX"):
env_cmd = ["su", "-", _user, "-c"]
elif test_os == "FreeBSD":
env_cmd = ["su", "-", _user, "-c"]
else:
env_cmd = [
"su",
"-s",
shell,
"-",
_user,
"-c",
]
if test_os == "FreeBSD":
env_cmd.extend(
[
"{} -c {}".format(
shell, sys.executable
)
]
)
else:
env_cmd.extend([sys.executable])
assert (
popen_mock.call_args_list[0][0][0]
== env_cmd
)
else:
if test_family in ("Solaris", "AIX"):
env_cmd = ["su", "-", _user, "-c"]
elif test_os == "FreeBSD":
env_cmd = ["su", "-", _user, "-c"]
else:
env_cmd = [
"su",
"-s",
shell,
"-",
_user,
"-c",
]
if test_os == "FreeBSD":
env_cmd.extend(
[
"{} -c {} python {}".format(
shell, sys.executable, file_name
)
]
)
else:
env_cmd.extend(
[
"{} python {}".format(
sys.executable, file_name
)
]
)
assert (
popen_mock.call_args_list[0][0][0]
== env_cmd
)
@pytest.mark.skip_on_darwin
@pytest.mark.skip_on_windows
def test_runas_env_sudo_group():
"""
cmd.run executes command and the environment is returned
when the runas parameter is specified
when group is passed and use_sudo=True
"""
bundled = [False, True]
for _bundled in bundled:
with patch("pwd.getpwnam") as getpwnam_mock:
with patch("subprocess.Popen") as popen_mock:
popen_mock.return_value = Mock(
communicate=lambda *args, **kwags: [b"", None],
pid=lambda: 1,
retcode=0,
)
file_name = "/tmp/doesnotexist"
with patch.dict(
cmdmod.__grains__, {"os": "linux", "os_family": "linux"}
):
with patch("grp.getgrnam"):
with patch(
"salt.utils.pkg.check_bundled", return_value=_bundled
):
with patch("shutil.chown"):
with patch("os.remove"):
with patch.object(
tempfile, "NamedTemporaryFile"
) as mock_fp:
mock_fp.return_value.__enter__.return_value.name = (
file_name
)
if sys.platform.startswith(
("freebsd", "openbsd")
):
shell = "/bin/sh"
else:
shell = "/bin/bash"
_user = "foobar"
_group = "foobar"
cmdmod._run(
"ls",
cwd=tempfile.gettempdir(),
runas=_user,
shell=shell,
group=_group,
)
if not _bundled:
assert popen_mock.call_args_list[0][0][
0
] == [
"sudo",
"-u",
_user,
"-g",
_group,
"-s",
"--",
shell,
"-c",
sys.executable,
]
else:
assert popen_mock.call_args_list[0][0][
0
] == [
"sudo",
"-u",
_user,
"-g",
_group,
"-s",
"--",
shell,
"-c",
"{} python {}".format(
sys.executable, file_name
),
]