running a command as a super user from a python script

Question:

So I’m trying to get a process to be run as a super user from within a python script using subprocess. In the ipython shell something like

proc = subprocess.Popen('sudo apach2ctl restart',
                        shell=True, stdin=subprocess.PIPE,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)

works fine, but as soon as I stick it into a script I start getting: sudo: apach2ctl: command not found.

I would guess this is due to the way sudo handles environments on ubuntu. (I’ve also tried sudo -E apche2ctl restart and sudo env path=$PATH apache2ctl restart with no avail)

So my question is basically, if I want to run apache2ctl restart as super user that prompts the user for the super user password when required, how should I go about doing this? I have no intention of storing passwords in the script.

Edit:

I’ve tried passing in the commands as both a string and tokenized into a list. In the python interpreter, with a string I’ll get the password prompt properly (still doesnt work in a python script as in my original problem), a list just gives the help screen for sudo.

Edit 2:

So what I gather is that while Popen will work with some commands just as strings when shell=True, it takes

proc = subprocess.Popen(['sudo','/usr/sbin/apache2ctl','restart'])

without ‘shell=True’ to get sudo to work.

Thanks!

Asked By: Silfheed

||

Answers:

You have to use Popen like this:

cmd = ['sudo', 'apache2ctl', 'restart']
proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

It expects a list.

Answered By: Harley Holcombe

Try giving the full path to apache2ctl.

Answered By: dwc

Try:

subprocess.call(['sudo', 'apach2ctl', 'restart'])

The subprocess needs to access the real stdin/out/err for it to be able to prompt you, and read in your password. If you set them up as pipes, you need to feed the password into that pipe yourself.

If you don’t define them, then it grabs sys.stdout, etc…

Answered By: Mike Boers

Another way is to make your user a password-less sudo user.

Type the following on command line:

sudo visudo

Then add the following and replace the <username> with yours:

<username> ALL=(ALL) NOPASSWD: ALL

This will allow the user to execute sudo command without having to ask for password (including application launched by the said user. This might be a security risk though

Answered By: Michael Loo

I tried all the solutions, but did not work. Wanted to run long running tasks with Celery but for these I needed to run sudo chown command with subprocess.call().

This is what worked for me:

To add safe environment variables, in command line, type:

export MY_SUDO_PASS="user_password_here"

To test if it’s working type:

echo $MY_SUDO_PASS
 > user_password_here

To run it at system startup add it to the end of this file:

nano ~/.bashrc  

#.bashrc
...
existing_content:

  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi
...

export MY_SUDO_PASS="user_password_here"

You can add all your environment variables passwords, usernames, host, etc here later.

If your variables are ready you can run:

To update:

echo $MY_SUDO_PASS | sudo -S apt-get update

Or to install Midnight Commander

echo $MY_SUDO_PASS | sudo -S apt-get install mc

To start Midnight Commander with sudo

echo $MY_SUDO_PASS | sudo -S mc

Or from python shell (or Django/Celery), to change directory ownership recursively:

python
>> import subprocess
>> subprocess.call('echo $MY_SUDO_PASS | sudo -S chown -R username_here /home/username_here/folder_to_change_ownership_recursivley', shell=True)

Hope it helps.

Answered By: jturi

I used this for python 3.5. I did it using subprocess module.Using the password like this is very insecure.

The subprocess module takes command as a list of strings so either create a list beforehand using split() or pass the whole list later. Read the documentation for more information.

What we are doing here is echoing the password and then using pipe we pass it on to the sudo through ‘-S’ argument.

#!/usr/bin/env python
import subprocess

sudo_password = 'mysecretpass'
command = 'apach2ctl restart'
command = command.split()

cmd1 = subprocess.Popen(['echo',sudo_password], stdout=subprocess.PIPE)
cmd2 = subprocess.Popen(['sudo','-S'] + command, stdin=cmd1.stdout, stdout=subprocess.PIPE)

output = cmd2.stdout.read().decode() 
Answered By: Nandesh

The safest way to do this is to prompt for the password beforehand and then pipe it into the command. Prompting for the password will avoid having the password saved anywhere in your code and it also won’t show up in your bash history. Here’s an example:

from getpass import getpass
from subprocess import Popen, PIPE

password = getpass("Please enter your password: ")
# sudo requires the flag '-S' in order to take input from stdin
proc = Popen("sudo -S apach2ctl restart".split(), stdin=PIPE, stdout=PIPE, stderr=PIPE)
# Popen only accepts byte-arrays so you must encode the string
proc.communicate(password.encode())
Answered By: Josh Correia

To run a command as root, and pass it the password at the command prompt, you could do it as so:

import subprocess
from getpass import getpass

ls = "sudo -S ls -al".split()
cmd = subprocess.run(
    ls, stdout=subprocess.PIPE, input=getpass("password: "), encoding="ascii",
)
print(cmd.stdout)

For your example, probably something like this:

import subprocess
from getpass import getpass

restart_apache = "sudo /usr/sbin/apache2ctl restart".split()
proc = subprocess.run(
    restart_apache,
    stdout=subprocess.PIPE,
    input=getpass("password: "),
    encoding="ascii",
)
Answered By: clamytoe

You can use this way and even catch errors, even can add variables to your commands. –

val = 'xy
response = Popen(f"(sudo {val})", stderr=PIPE, stdout=PIPE, shell=True)
output, errors = response.communicate()

Hope this helps.

Answered By: ashraf minhaj
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.