how to grant www-data user access to python salt module?

Question:

I’m maintaining a small legacy php5 application (based on CodeIgniter) that acts as a salt web UI that allows me to run salt commands and schedule repeating jobs. The web app runs python scripts which invoke the salt api to execute the commands.

The problem that I’m facing is that python throws a UnboundLocalError when I’m trying to loop through the results. Interestingly, this issue only happens when I run the python script using www-data user. If I use my admin account, the script works fine.

this fails:

sudo su - www-data -s /bin/bash -c '/usr/bin/python /home/system/update-manager/check_reboot_status.py'

Traceback (most recent call last):
  File "/home/system/update-manager/check_reboot_status.py", line 43, in <module>
    main()
  File "/home/system/update-manager/check_reboot_status.py", line 34, in main
    for r in returns:
  File "/usr/lib/python2.7/dist-packages/salt/client/__init__.py", line 563, in cmd_batch
    salt.utils.versions.warn_until(
UnboundLocalError: local variable 'salt' referenced before assignment

this works fine:

sudo /usr/bin/python /home/system/update-manager/check_reboot_status.py
2019-02-21 12:12:04,456 INFO Started the check reboot status script...
2019-02-21 12:12:22,144 INFO Updated the 'Reboot Status' of 92 minions.
2019-02-21 12:12:22,144 INFO End of the check reboot status script.

Initially, I thought this was due to insufficient permission, however my visudo file grants www-data permission to run that command:

www-data ALL = NOPASSWD: /usr/bin/python /home/system/update-manager/check_reboot_status.py

I’m confused that the error revolves around a variable ‘salt’, as I am certain the python module is installed; after all my admin account can execute the script without errors. I wonder if it has to do with the shell environment in which the script is executed. I couldn’t find information about that in the php doc.

What have I left out to try or to investigate? I copied the python and php code below for reference.

php script:

$script = '/home/system/update-manager/check_reboot_status.py';

shell_exec('sudo /usr/bin/python ' . $script . '> /home/system/update-manager/logs/check_reboot_status.log 2>&1 &');

python code

import salt.client
local = salt.client.LocalClient()

linux = 'G@os:Ubuntu'    

# Linux minions
cmd = '[ -f /var/run/reboot-required ] && echo 1 || echo 0'
returns = local.cmd_batch(linux, 'cmd.run', [cmd], bat='1', expr_form='compound')

for r in returns:
    count += 1
    for minion, reboot_required in r.iteritems():
        umb.change_reboot_status(minion, reboot_required)
Asked By: Omnibyte

||

Answers:

tl;dr: Misunderstanding of the error message. Using command with/without sudo caused two different python salt modules. As per API documentation, only the user that launched salt on the salt-master may run commands, in this case: root. The original question doesn’t match the described situation as python never didn’t have access to the module, otherwise an error like “ImportError: No module named salt” would have been thrown.

There was a permission problem that concealed the actual issue.

First I discovered that running python with the two commands used two different files to be loaded:

$ sudo python
>>> import salt.client
>>> salt.client
<module 'salt.client' from' /home/support/.local/lib/python2.7/site-packages/salt/client/__init__.pyc'>

 sudo runuser -l www-data -s /bin/bash -c '/usr/bin/python
>>> import salt.client
>>> salt.client
<module 'salt.client' from '/usr/lib/python2.7/dist-packages/salt/client/__init__.pyc'>

The stacktrace reveiled that the error was coming from the following block: “salt.utils.versions.warn_until()” threw the salt error shown in the question even though that package was imported at the beginning of the file.

#/usr/lib/python2.7/dist-packages/salt/client/__init__.py
if 'expr_form' in kwargs:
            #import salt.utils.versions
--->        salt.utils.versions.warn_until(
                'Fluorine',
                'The target type should be passed using the 'tgt_type' '
                'argument instead of 'expr_form'. Support for using '
                ''expr_form' will be removed in Salt Fluorine.'
            )
            tgt_type = kwargs.pop('expr_form')

        import salt.cli.batc

By adding the import line (shown in comment) the error was resolved, however, another one took its place:

salt.exceptions.SaltClientError: Authentication error occurred.

As the documentation states “Importing and using LocalClient must be done on the same machine as the Salt Master and it must be done using the same user that the Salt Master is running as. […]).”.
This was where I realized that the commands can only be run with the root user.

Answered By: Omnibyte