Skip to content

Commit

Permalink
Set user and/or group when starting daemons/CLIs
Browse files Browse the repository at this point in the history
Signed-off-by: Pedro Algarvio <[email protected]>
  • Loading branch information
s0undt3ch committed May 3, 2023
1 parent 92d013b commit 4627fd3
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 46 deletions.
4 changes: 2 additions & 2 deletions salt/cli/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import salt.client.netapi
import salt.utils.files
import salt.utils.parsers as parsers
from salt.utils.verify import check_user
from salt.utils.verify import check_group

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -47,7 +47,7 @@ def start(self):
NOTE: Run any required code before calling `super()`.
"""
super().start()
if check_user(self.config["user"]):
if check_group(self.config["user"], group=self.config.get("group")):
log.info("The salt-api is starting up")
self.api.run()

Expand Down
18 changes: 10 additions & 8 deletions salt/cli/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import salt.defaults.exitcodes
import salt.utils.parsers
from salt.config import _expand_glob_path
from salt.utils.verify import check_group


class SaltCall(salt.utils.parsers.SaltCallOptionParser):
Expand Down Expand Up @@ -37,14 +38,15 @@ def run(self):
if self.options.master:
self.config["master"] = self.options.master

caller = salt.cli.caller.Caller.factory(self.config)
if check_group(self.config["user"], group=self.config.get("group")):
caller = salt.cli.caller.Caller.factory(self.config)

if self.options.doc:
caller.print_docs()
self.exit(salt.defaults.exitcodes.EX_OK)
if self.options.doc:
caller.print_docs()
self.exit(salt.defaults.exitcodes.EX_OK)

if self.options.grains_run:
caller.print_grains()
self.exit(salt.defaults.exitcodes.EX_OK)
if self.options.grains_run:
caller.print_grains()
self.exit(salt.defaults.exitcodes.EX_OK)

caller.run()
caller.run()
6 changes: 4 additions & 2 deletions salt/cli/cp.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import salt.utils.platform
import salt.utils.stringutils
import salt.utils.verify
from salt.utils.verify import check_group

log = logging.getLogger(__name__)

Expand All @@ -36,8 +37,9 @@ def run(self):
Execute salt-cp
"""
self.parse_args()
cp_ = SaltCP(self.config)
cp_.run()
if check_group(self.config["user"], group=self.config.get("group")):
cp_ = SaltCP(self.config)
cp_.run()


class SaltCP:
Expand Down
16 changes: 7 additions & 9 deletions salt/cli/daemons.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
try:
import salt.utils.parsers
from salt.utils.network import ip_bracket
from salt.utils.verify import check_user, verify_env, verify_socket
from salt.utils.verify import check_group, verify_env, verify_socket
except ImportError as exc:
if exc.args[0] != "No module named _msgpack":
raise
Expand Down Expand Up @@ -198,7 +198,7 @@ def start(self):
NOTE: Run any required code before calling `super()`.
"""
super().start()
if check_user(self.config["user"]):
if check_group(self.config["user"], group=self.config.get("group")):
self.action_log_info("Starting up")
self.verify_hash_type()
self.master.start()
Expand Down Expand Up @@ -288,8 +288,6 @@ def prepare(self):
self.action_log_info("An instance is already running. Exiting")
self.shutdown(1)

transport = self.config.get("transport").lower()

try:
# Late import so logging works correctly
import salt.minion
Expand Down Expand Up @@ -323,15 +321,15 @@ def start(self):
while True:
try:
self._real_start()
except SaltClientError as exc:
except SaltClientError:
# Restart for multi_master failover when daemonized
if self.options.daemon:
continue
break

def _real_start(self):
try:
if check_user(self.config["user"]):
if check_group(self.config["user"], group=self.config.get("group")):
self.action_log_info("Starting up")
self.verify_hash_type()
self.minion.tune_in()
Expand Down Expand Up @@ -361,7 +359,7 @@ def call(self, cleanup_protecteds):
"""
try:
self.prepare()
if check_user(self.config["user"]):
if check_group(self.config["user"], group=self.config.get("group")):
self.minion.opts["__role"] = kinds.APPL_KIND_NAMES[
kinds.applKinds.caller
]
Expand Down Expand Up @@ -504,7 +502,7 @@ def start(self):
"""
super().start()
try:
if check_user(self.config["user"]):
if check_group(self.config["user"], group=self.config.get("group")):
self.action_log_info("The Proxy Minion is starting up")
self.verify_hash_type()
self.minion.tune_in()
Expand Down Expand Up @@ -594,7 +592,7 @@ def start(self):
NOTE: Run any required code before calling `super()`.
"""
super().start()
if check_user(self.config["user"]):
if check_group(self.config["user"], group=self.config.get("group")):
self.action_log_info("Starting up")
self.verify_hash_type()
try:
Expand Down
4 changes: 2 additions & 2 deletions salt/cli/key.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import salt.utils.parsers
from salt.utils.verify import check_user
from salt.utils.verify import check_group


class SaltKey(salt.utils.parsers.SaltKeyOptionParser):
Expand All @@ -16,5 +16,5 @@ def run(self):
self.parse_args()

key = salt.key.KeyCLI(self.config)
if check_user(self.config["user"]):
if check_group(self.config["user"], group=self.config.get("group")):
key.run()
4 changes: 2 additions & 2 deletions salt/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import salt.utils.parsers
import salt.utils.profile
from salt.exceptions import SaltClientError
from salt.utils.verify import check_user
from salt.utils.verify import check_group


class SaltRun(salt.utils.parsers.SaltRunOptionParser):
Expand All @@ -28,7 +28,7 @@ def run(self):
# Run this here so SystemExit isn't raised anywhere else when
# someone tries to use the runners via the python API
try:
if check_user(self.config["user"]):
if check_group(self.config["user"], group=self.config.get("group")):
pr = salt.utils.profile.activate_profile(profiling_enabled)
try:
ret = runner.run()
Expand Down
4 changes: 4 additions & 0 deletions salt/cli/salt.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
SaltSystemExit,
)
from salt.utils.args import yamlify_arg
from salt.utils.verify import check_group


class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
Expand All @@ -33,6 +34,9 @@ def run(self):

self.parse_args()

if not check_group(self.config["user"], group=self.config.get("group")):
sys.exit(salt.defaults.exitcodes.EX_GENERIC)

try:
# We don't need to bail on config file permission errors
# if the CLI process is run with the -a flag
Expand Down
9 changes: 6 additions & 3 deletions salt/cli/spm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
.. versionadded:: 2015.8.0
"""
import sys


import salt.defaults.exitcodes
import salt.spm
import salt.utils.parsers as parsers
from salt.utils.verify import verify_env
from salt.utils.verify import check_group, verify_env


class SPM(parsers.SPMParser):
Expand All @@ -22,8 +23,10 @@ def run(self):
"""
Run the api
"""
ui = salt.spm.SPMCmdlineInterface()
self.parse_args()
if not check_group(self.config["user"], group=self.config.get("group")):
sys.exit(salt.defaults.exitcodes.EX_GENERIC)
ui = salt.spm.SPMCmdlineInterface()
v_dirs = [
self.config["spm_cache_dir"],
]
Expand Down
5 changes: 5 additions & 0 deletions salt/cli/ssh.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import sys

import salt.client.ssh
import salt.defaults.exitcodes
import salt.utils.parsers
from salt.utils.verify import check_group


class SaltSSH(salt.utils.parsers.SaltSSHOptionParser):
Expand All @@ -15,6 +17,9 @@ def run(self):
# that won't be used anyways with -H or --hosts
self.parse_args()

if not check_group(self.config["user"], group=self.config.get("group")):
sys.exit(salt.defaults.exitcodes.EX_GENERIC)

ssh = salt.client.ssh.SSH(self.config)
try:
ssh.run()
Expand Down
9 changes: 7 additions & 2 deletions salt/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ def _gather_buffer_space():
"key_cache": str,
# The user under which the daemon should run
"user": str,
# The group under which the daemon should run
"group": str,
# The root directory prepended to these options: pki_dir, cachedir,
# sock_dir, log_file, autosign_file, autoreject_file, extension_modules,
# key_logfile, pidfile:
Expand Down Expand Up @@ -1017,6 +1019,7 @@ def _gather_buffer_space():
"master_sign_key_name": "master_sign",
"syndic_finger": "",
"user": salt.utils.user.get_user(),
"group": None,
"root_dir": salt.syspaths.ROOT_DIR,
"pki_dir": os.path.join(salt.syspaths.LIB_STATE_DIR, "pki", "minion"),
"id": "",
Expand Down Expand Up @@ -1307,6 +1310,7 @@ def _gather_buffer_space():
"pub_hwm": 1000,
"auth_mode": 1,
"user": _MASTER_USER,
"group": None,
"worker_threads": 5,
"sock_dir": os.path.join(salt.syspaths.SOCK_DIR, "master"),
"sock_pool_size": 1,
Expand Down Expand Up @@ -2466,6 +2470,7 @@ def syndic_config(
)
),
"user": opts.get("syndic_user", opts["user"]),
"group": opts.get("syndic_group", opts.get("group")),
"sock_dir": os.path.join(
opts["cachedir"], opts.get("syndic_sock_dir", opts["sock_dir"])
),
Expand Down Expand Up @@ -2875,7 +2880,7 @@ def apply_vm_profiles_config(providers, overrides, defaults=None):
vms = {}

for key, val in config.items():
if key in ("conf_file", "include", "default_include", "user"):
if key in ("conf_file", "include", "default_include", "user", "group"):
continue
if not isinstance(val, dict):
raise salt.exceptions.SaltCloudConfigError(
Expand Down Expand Up @@ -3035,7 +3040,7 @@ def apply_cloud_providers_config(overrides, defaults=None):
providers = {}
ext_count = 0
for key, val in config.items():
if key in ("conf_file", "include", "default_include", "user"):
if key in ("conf_file", "include", "default_include", "user", "group"):
continue

if not isinstance(val, (list, tuple)):
Expand Down
18 changes: 6 additions & 12 deletions salt/utils/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
Functions for querying and modifying a user account and the groups to which it
belongs.
"""


import ctypes
import getpass
import logging
Expand All @@ -12,11 +10,9 @@

import salt.utils.path
import salt.utils.platform
import salt.utils.stringutils
from salt.exceptions import CommandExecutionError
from salt.utils.decorators.jinja import jinja_filter

# Conditional imports
try:
import pwd

Expand Down Expand Up @@ -53,14 +49,12 @@ def get_user():
Get the current user
"""
if HAS_PWD:
ret = pwd.getpwuid(os.geteuid()).pw_name
elif HAS_WIN_FUNCTIONS and salt.utils.win_functions.HAS_WIN32:
ret = salt.utils.win_functions.get_current_user()
else:
raise CommandExecutionError(
"Required external library (pwd or win32api) not installed"
)
return salt.utils.stringutils.to_unicode(ret)
return pwd.getpwuid(os.geteuid()).pw_name
if HAS_WIN_FUNCTIONS and salt.utils.win_functions.HAS_WIN32:
return salt.utils.win_functions.get_current_user()
raise CommandExecutionError(
"Required external library (pwd or win32api) not installed"
)


@jinja_filter("get_uid")
Expand Down
41 changes: 37 additions & 4 deletions salt/utils/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,7 @@ def check_user(user):
pwuser = _get_pwnam(user)

try:
if hasattr(os, "initgroups"):
os.initgroups(user, pwuser.pw_gid) # pylint: disable=minimum-python-version
else:
os.setgroups(salt.utils.user.get_gid_list(user, include_default=False))
os.initgroups(user, pwuser.pw_gid)
os.setgid(pwuser.pw_gid)
os.setuid(pwuser.pw_uid)

Expand All @@ -351,6 +348,42 @@ def check_user(user):
return True


def check_group(user, group=None):
"""
Check user and group and assign process uid and gid.
.. versionadded:: 3006.1
"""
if salt.utils.platform.is_windows():
return True

if check_user(user) is False:
return

# Late import after confirming we're not running under windows
import grp

default_group = grp.getgrgid(_get_pwnam(user).pw_gid).gr_name
if group is None:
# If no group is passed, default to the passed user's default group
group = default_group

if group == default_group:
# No need to switch process group
return True

group_gid = grp.getgrgid(group).pw_gid

try:
os.setgid(group_gid)
except OSError:
log.critical(
'Salt configured to run as group "%s" but unable to switch.', group
)
return False
return True


def list_path_traversal(path):
"""
Returns a full list of directories leading up to, and including, a path.
Expand Down

0 comments on commit 4627fd3

Please sign in to comment.