5

When I ssh into a remote server and run env I get back the following PATH:

PATH=/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/myusername/bin

In contrast, when I execute the ansible command ansible -a "env", I get the following PATH:

PATH=/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin

As can be imagined, this causes problems when trying to run sbin commands like service, ntpdate, etc. because it requires typing out the full path to the sbin command. (Yes, I know there are ansible modules that can do this, but I'm trying to solve why the PATH is getting shortened/truncated.)

Anyone know why this is happening?

EDIT: The reason I'm asking about this is that I'm following the examples in the book Ansible for DevOps and some of the examples aren't working because of this reason.

The example I'm trying to get to work is from page 28 in the book:

$ ansible multi -s -a "service ntpd stop"
$ ansible multi -s -a "ntpdate -q 0.rhel.pool.ntp.org"
$ ansible multi -s -a "service ntpd start"

These commands always raise an error. But if I change them to include the full path, they work.

$ ansible multi -s -a "/sbin/service ntpd stop"
$ ansible multi -s -a "/usr/sbin/ntpdate -q 0.rhel.pool.ntp.org"
$ ansible multi -s -a "/sbin/service ntpd start"

Naturally, I don't want to have to type in the full path for each and every ad-hoc command I issue. (I don't know the paths of every command.)

Is there any way to get the normal path that's in my shell included with ansible? Is there a path variable that I can add to the inventory file or ansible.cfg file that will enable this to happen?

2
+50

Any time i used ansible it ran as a different user..under a different shell. should be able to see what user by doing:

ansible -a "id"

you can then see the user shell defined in: cat /etc/passwd

Best practice to add to the path would be by adding a line in: /home/<username>/.profile or ~/.bash_profile

| improve this answer | |
  • And this works! I added export PATH=$PATH:/usr/local/sbin:/usr/sbin:/sbin to the TOP of my ~/.bashrc file and now I can execute the commands like in the book. I imagine that I can do the same for /etc/bashrc if I want it to work system wide for all users. – Pretzel Aug 5 '16 at 15:09
  • /etc/bashrc contains system-wide definitions , yes. – cormpadre Aug 5 '16 at 15:10
  • I don't think adding the sbin/ directories to all user paths would be considered a good idea. The binaries in sbin are for system administrative tasks and maintenance. – Tom H Aug 5 '16 at 16:35
  • 1
    @TomH - In general it's not the worst idea, though... If a nonprivileged user tries running a service command, it won't work (similar for other administrative tasks). IMO, it's more of a convenience thing. Many default configs include sbin in the $PATH. – geerlingguy Aug 6 '16 at 3:27
  • @geerlingguy I don't see sbin added to the path for non-root users on any of the recent ubuntu, centos or fedora for non-login/non-interactive commands. If I run ssh someserver env | grep PATH it always comes back with something like /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games. I think the OP has found his answer, but personally changing the system wide behaviour for all users running commands in simple command shells is likely to produce unexpected results. – Tom H Aug 6 '16 at 3:51
10

ansible -a is using the command module and this doesn't use a shell to execute the command.

http://docs.ansible.com/ansible/intro_adhoc.html
Normally commands also take a -m for module name, but the default module name is ‘command’.

The components of the path, such as /usr/sbin are added to the path by by the shell sourcing the /etc/profile file, and the command module doesn't create login shell so it won't read that file.

http://linux.die.net/man/1/bash
When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. The --noprofile option may be used when the shell is started to inhibit this behavior.

The ansible docs say that if you want a shell, to use the shell module. However it doesn't seem that there is an option to start a login shell from the shell module (by passing the --login option) anyway.

In fact, it is mentioned in various places that the ansible policy is to not trust the environment, and to set any VARS explicitly when the task is defined.

However it is possible to set the path when using ansible in ad-hoc mode. These are the nearest I could do to replicate the PATH you see in an interactive /bin/bash shell;

$ ansible raspberrypi -m shell \
-a 'PATH=$PATH:/usr/local/sbin:/usr/sbin:$HOME/bin  env' \
 | grep PATH

PATH=/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/local/sbin:
/usr/sbin:/home/pi/bin

or alternatively, you can source the /etc/profile file;

 $ ansible raspberrypi -m shell \
 -a 'executable=/bin/bash source /etc/profile ; env | grep PATH'

 PATH=/opt/consul/bin:/usr/local/sbin:/usr/local/bin:
 /usr/sbin:/usr/bin:/sbin:/bin

this seems to work also;

$ ansible raspberrypi -a '/bin/bash -l -c "env"'

 PATH=/opt/consul/bin:/usr/local/sbin:/usr/local/bin:
 /usr/sbin:/usr/bin:/sbin:/bin
| improve this answer | |
  • That's interesting... So I tried ansible -m shell -a "env" and still got the same truncated PATH. (You're probably right about /etc/profile added those 3 paths, though.) – Pretzel Jul 27 '16 at 17:41
  • I tried ansible raspberrypi -m shell -a 'echo $SHELL ; env | grep PATH' and that uses /bin/bash, but it also doesn't load the profile, so it looks like bash needs the --login option, but the shell module won't let me pass that to the shell... lol. I would have assumed that was possible :-( – Tom H Jul 27 '16 at 18:06
  • Dang. That's frustrating. All I want to do is follow along with the exercises in this book and they don't work properly. I tried writing to the author, but haven't heard back yet. Thanks for the help, though. – Pretzel Jul 27 '16 at 18:08
  • so this is what I came up with; ansible raspberrypi -m shell -a 'executable=/bin/bash source /etc/profile; echo $SHELL ; env | grep PATH' though I would probably set the path explicitly with PATH=$PATH:/sbin as there is no guarantee that /etc/profile will be the same on all systems.. – Tom H Jul 27 '16 at 18:14
2

So given your question is ultimately about running commands under sudo with ansible, I had another look at how the PATH is set when using ansible -s -a env. I think its important to notice that when using the sudo -s flag in ansible, the $PATH is set by a different mechanism to when using only ansible -a env.

When using sudo command or ansible -s the users environment is reset, and $PATH is controlled by the Default secure_path setting in /etc/sudoers and this is explained in the man sudoers documentation;

By default, the env_reset option is enabled. This causes commands to be executed with a new, minimal environment. The new environment contains the TERM, PATH, HOME, MAIL, SHELL, LOGNAME, USER, USERNAME and SUDO_* variables in addition to variables from the invoking process permitted by the env_check and env_keep options.

If you don't set a value for Default secure_path, then these values are hard coded into the sshd binary, see this answer for details.

However by default on fedora/centros/ubuntu the value for $PATH is taken from secure_path set in /etc/sudoers;

secure_path - Path used for every command run from sudo.

For example on my fedora localhost, it has the following line in /etc/sudoers

Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin

and if I run the env command remotely by ssh and sudo I get that exact path back;

$ ssh localhost "sudo env" | grep PATH
PATH=/sbin:/bin:/usr/sbin:/usr/bin

and I get the same thing using ansible;

$ ansible localhost -s -a 'env' | grep PATH
PATH=/sbin:/bin:/usr/sbin:/usr/bin

and I've tried the same thing on a raspbian(debian) instance, and I get similar behaviour;

$ ansible raspberrypi -s -a 'cat /etc/sudoers' | grep secure
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

and that is the path that ansible sees with the -s -a options;

$ ansible raspberrypi -s -a env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

So in respect of your original question, I would take a look at the /etc/sudoers file, and check the value of Defaults secure_path="/some/path/here".

If you do not have a value there which includes the appropriate sbin/ directory, then the following command in ansible is not going to work without a full path given;

ansible multi -s -a "service ntpd stop"
| improve this answer | |
  • I think you were on the right track, but user cormpadre summarized it perfectly. Thanks for all your help, man. – Pretzel Aug 5 '16 at 15:10
  • Did you check your sudoers file? – Tom H Aug 5 '16 at 16:34
  • We're using LDAP. Sudoers file doesn't come into play here. – Pretzel Aug 8 '16 at 15:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.