Using sudo with Python script

Question:

I’m trying to write a small script to mount a VirtualBox shared folder each time I execute the script. I want to do it with Python, because I’m trying to learn it for scripting.

The problem is that I need privileges to launch mount command. I could run the script as sudo, but I prefer it to make sudo by its own.

I already know that it is not safe to write your password into a .py file, but we are talking about a virtual machine that is not critical at all: I just want to click the .py script and get it working.

This is my attempt:

#!/usr/bin/env python
import subprocess

sudoPassword = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'

subprocess.Popen('sudo -S' , shell=True,stdout=subprocess.PIPE)
subprocess.Popen(sudoPassword , shell=True,stdout=subprocess.PIPE)
subprocess.Popen(command , shell=True,stdout=subprocess.PIPE)

My python version is 2.6

Asked By: Roman Rdgz

||

Answers:

subprocess.Popen creates a process and opens pipes and stuff. What you are doing is:

  • Start a process sudo -S
  • Start a process mypass
  • Start a process mount -t vboxsf myfolder /home/myuser/myfolder

which is obviously not going to work. You need to pass the arguments to Popen. If you look at its documentation, you will notice that the first argument is actually a list of the arguments.

Answered By: mensi
sudoPassword = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'
p = os.system('echo %s|sudo -S %s' % (sudoPassword, command))

Try this and let me know if it works. 🙂

And this one:

os.popen("sudo -S %s"%(command), 'w').write('mypass')

Answered By: Aniket Inge

sometimes require a carriage return:

os.popen("sudo -S %s"%(command), 'w').write('mypassn')
Answered By: user2095717

Please try module pexpect. Here is my code:

import pexpect
remove = pexpect.spawn('sudo dpkg --purge mytool.deb')
remove.logfile = open('log/expect-uninstall-deb.log', 'w')
remove.logfile.write('try to dpkg --purge mytooln')
if remove.expect(['(?i)password.*']) == 0:
    # print "successfull"
    remove.sendline('mypassword')
    time.sleep(2)
    remove.expect(pexpect.EOF,5)
else:
    raise AssertionError("Fail to Uninstall deb package !")
Answered By: Snap

To pass the password to sudo‘s stdin:

#!/usr/bin/env python
from subprocess import Popen, PIPE

sudo_password = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'.split()

p = Popen(['sudo', '-S'] + command, stdin=PIPE, stderr=PIPE,
          universal_newlines=True)
sudo_prompt = p.communicate(sudo_password + 'n')[1]

Note: you could probably configure passwordless sudo or SUDO_ASKPASS command instead of hardcoding your password in the source code.

Answered By: jfs

Many answers focus on how to make your solution work, while very few suggest that your solution is a very bad approach. If you really want to "practice to learn", why not practice using good solutions? Hardcoding your password is learning the wrong approach!

If what you really want is a password-less mount for that volume, maybe sudo isn’t needed at all! So may I suggest other approaches?

  • Use /etc/fstab as mensi suggested. Use options user and noauto to let regular users mount that volume.

  • Use Polkit for passwordless actions: Configure a .policy file for your script with <allow_any>yes</allow_any> and drop at /usr/share/polkit-1/actions

  • Edit /etc/sudoers to allow your user to use sudo without typing your password. As @Anders suggested, you can restrict such usage to specific commands, thus avoiding unlimited passwordless root priviledges in your account. See this answer for more details on /etc/sudoers.

All the above allow passwordless root privilege, none require you to hardcode your password. Choose any approach and I can explain it in more detail.

As for why it is a very bad idea to hardcode passwords, here are a few good links for further reading:

Answered By: MestreLion

I know it is always preferred not to hardcode the sudo password in the script. However, for some reason, if you have no permission to modify /etc/sudoers or change file owner, Pexpect is a feasible alternative.

Here is a Python function sudo_exec for your reference:

import platform, os, logging
import subprocess, pexpect

log = logging.getLogger(__name__)

def sudo_exec(cmdline, passwd):
    osname = platform.system()
    if osname == 'Linux':
        prompt = r'[sudo] password for %s: ' % os.environ['USER']
    elif osname == 'Darwin':
        prompt = 'Password:'
    else:
        assert False, osname

    child = pexpect.spawn(cmdline)
    idx = child.expect([prompt, pexpect.EOF], 3)
    if idx == 0: # if prompted for the sudo password
        log.debug('sudo password was asked.')
        child.sendline(passwd)
        child.expect(pexpect.EOF)
return child.before
Answered By: Jeremy Kao
  • Use -S option in the sudo command which tells to read the password from ‘stdin’ instead of the terminal device.

  • Tell Popen to read stdin from PIPE.

  • Send the Password to the stdin PIPE of the process by using it as an argument to communicate method. Do not forget to add a new line character, ‘n’, at the end of the password.

sp = Popen(cmd , shell=True, stdin=PIPE)
out, err = sp.communicate(_user_pass+'n')   
Answered By: Hubert Vijay

To limit what you run as sudo, you could run

python non_sudo_stuff.py
sudo -E python -c "import os; os.system('sudo echo 1')"

without needing to store the password. The -E parameter passes your current user’s env to the process. Note that your shell will have sudo priveleges after the second command, so use with caution!

Answered By: crizCraig

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 moreinformation.

#!/usr/bin/env python
import subprocess

sudoPassword = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'.split()

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

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

It works in python 2.7 and 3.8:

from subprocess import Popen, PIPE
from shlex import split

proc = Popen(split('sudo -S %s' % command), bufsize=0, stdout=PIPE, stdin=PIPE, stderr=PIPE)
proc.stdin.write((password +'n').encode()) # write as bytes
proc.stdin.flush() # need if not bufsize=0 (unbuffered stdin)

without .flush() password will not reach sudo if stdin buffered.
In python 2.7 Popen by default used bufsize=0 and stdin.flush() was not needed.

For secure using, create password file in protected directory:

mkdir --mode=700 ~/.prot_dir
nano ~/.prot_dir/passwd.txt
chmod 600 ~/.prot_dir/passwd.txt 

at start your py-script read password from ~/.prot_dir/passwd.txt

with open(os.environ['HOME'] +'/.prot_dir/passwd.txt') as f:
    password = f.readline().rstrip()
Answered By: evan
import os 
os.system("echo TYPE_YOUR_PASSWORD_HERE | sudo -S TYPE_YOUR_LINUX_COMMAND")

Open your ide and run the above code. Please change TYPE_YOUR_PASSWORD_HERE and TYPE_YOUR_LINUX_COMMAND to your linux admin password and your desired linux command after that run your python script. Your output will show on terminal. Happy Coding 🙂

Answered By: Abhinav Singwal

You can use SSHScript . Below are example codes:

## filename: example.spy 
sudoPassword = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'
$$echo @{sudoPassword} | sudo -S @{command}

or, simply one line (almost the same as running on console)

## filename: example.spy 
$$echo mypass | sudo -S mount -t vboxsf myfolder /home/myuser/myfolder

Then, run it on console

sshscript example.spy

Where "sshscript" is the CLI of SSHScript (installed by pip).

Answered By: Singuan Iap
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.