I'm running sudo-1.8.6 on CentOS 6.5. My question is very simple: How do I prevent SHELL from propagating from a user's environment to a sudo environment?
Usually people are going the other way- they want to preserve an environment variable. However, I am having an issue where my user "zabbix" whose shell is /sbin/nologin
tries to run a command via sudo. Sudo is preserving the /sbin/nologin
so that root cannot run subshells. (Update: This part is true, but it is not the SHELL environment variable. It is the shell value that is being pulled from /etc/passwd that is the problem.)
I include a test that illustrates the problem; this is not my real-world use case but it simply illustrates that the calling user's SHELL is preserved. I have a program that runs as user zabbix
. It calls /usr/bin/sudo -u root /tmp/doit
(the programming running as zabbix
is a daemon, so the /sbin/nologin
shell in the password file does not prevent it). /tmp/doit
is a shell script that simply has:
#!/bin/sh
env > /tmp/outfile
(its mode is 755, obviously). In outfile
I can see that SHELL
is /sbin/nologin
. However, at this point the script is running as root, via sudo, so it should not have the previous user's environment variables, right?
Here is my /etc/sudoers:
Defaults requiretty Defaults !visiblepw Defaults always_set_home Defaults env_reset Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS" Defaults env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE" Defaults env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES" Defaults env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE" Defaults env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY" Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin ## Allow root to run any commands anywhere root ALL=(ALL) ALL #includedir /etc/sudoers.d
And here is my /etc/sudoers.d/zabbix
:
Defaults:zabbix !requiretty zabbix ALL=(root) NOPASSWD: /tmp/doit
Edit: A little more information:
The process running the sudo is zabbix_agentd
, from the Zabbix monitoring software. There is an entry in the /etc/zabbix/zabbix_agentd.d/userparameter_disk.conf
file which looks like:
UserParameter=example.disk.discovery,/usr/local/bin/zabbix_raid_discovery
/usr/local/bin/zabbix_raid_discovery
is a Python script. I have modified it to simply do this:
print subprocess.check_output(['/usr/bin/sudo', '-u', 'root', '/tmp/doit'])
/tmp/doit
simply does this:
#!/bin/sh env >> /tmp/outfile
I run the following on my Zabbix server to run the /usr/local/bin/zabbix_raid_discovery
script:
zabbix_get -s client_hostname -k 'example.disk.discovery'
Then I check the /tmp/outfile
, and I see:
SHELL=/sbin/nologin TERM=linux USER=root SUDO_USER=zabbix SUDO_UID=497 USERNAME=root PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin MAIL=/var/mail/root PWD=/ LANG=en_US.UTF-8 SHLVL=1 SUDO_COMMAND=/tmp/doit HOME=/root LOGNAME=root SUDO_GID=497 _=/bin/env
That SHELL
line really bugs me. The file is owned by root, so I know it's being created by the root user, but the shell is from the calling user (zabbix
).
Then answer is that sudo
has a bug. First, the workaround: I put this in my /etc/sudoers.d/zabbix file
:
zabbix ALL=(root) NOPASSWD: /bin/env SHELL=/bin/sh /usr/local/bin/zabbix_raid_discovery
and now subcommands called from zabbix_raid_discovery
work.
A patch to fix this will be in sudo 1.8.15. From the maintainer, Todd Miller:
This is just a case of "it's always been like that". There's not really a good reason for it. The diff below should make the behavior match the documentation. - todd diff -r adb927ad5e86 plugins/sudoers/env.c --- a/plugins/sudoers/env.c Tue Oct 06 09:33:27 2015 -0600 +++ b/plugins/sudoers/env.c Tue Oct 06 10:04:03 2015 -0600 @@ -939,8 +939,6 @@ CHECK_SETENV2("USERNAME", runas_pw->pw_name, ISSET(didvar, DID_USERNAME), true); } else { - if (!ISSET(didvar, DID_SHELL)) - CHECK_SETENV2("SHELL", sudo_user.pw->pw_shell, false, true); /* We will set LOGNAME later in the def_set_logname case. */ if (!def_set_logname) { if (!ISSET(didvar, DID_LOGNAME)) @@ -984,6 +982,8 @@ if (!env_should_delete(*ep)) { if (strncmp(*ep, "SUDO_PS1=", 9) == 0) ps1 = *ep + 5; + else if (strncmp(*ep, "SHELL=", 6) == 0) + SET(didvar, DID_SHELL); else if (strncmp(*ep, "PATH=", 5) == 0) SET(didvar, DID_PATH); else if (strncmp(*ep, "TERM=", 5) == 0) @@ -1039,7 +1039,9 @@ if (reset_home) CHECK_SETENV2("HOME", runas_pw->pw_dir, true, true); - /* Provide default values for $TERM and $PATH if they are not set. */ + /* Provide default values for $SHELL, $TERM and $PATH if not set. */ + if (!ISSET(didvar, DID_SHELL)) + CHECK_SETENV2("SHELL", runas_pw->pw_shell, false, false); if (!ISSET(didvar, DID_TERM)) CHECK_PUTENV("TERM=unknown", false, false); if (!ISSET(didvar, DID_PATH))
Provide default values for $SHELL, $TERM and $PATH if not set.
is: ... if not set.
. Any value set will be preserved by sudo. Who is setting SHELL? — Oct 06, 2015 at 21:18