在 Python Shell Script 裡使用 sudo

免不了有些時候要從 Python script 裡呼叫外部程式,但外部程式如果需要透過 sudo 執行,過程中就會被提示要輸入密碼。如果不想手動輸入 sudo password,要如何避開它呢?

當然,將密碼直接寫在 script 的做法並不安全,因此比較建議的做法是修改 /etc/sudoers 讓某些使用者透過 sudo 執行某些指令時不會被問密碼。例如在 Linux/Mac 下要用 route 修改 routing table 時需要 root 的權限:

以 Linux 為例
$ whoami; which route
jeremy
/sbin/route

$ route add -host 168.95.1.1 dev eth0
SIOCADDRT: Operation not permitted
$ sudo route add -host 168.95.1.1 dev eth0
[sudo] password for jeremy:

$ netstat -nr | grep 168.95.1.1
168.95.1.1      0.0.0.0         255.255.255.255 UH        0 0          0 eth0

只要用 sudo visudo 開啟 /etc/sudoers,並加上類似下面的設定:

# <USER> <FROM>=<ACTAS> NOPASSWD: <CMDS>
jeremy  ALL=(ALL) NOPASSWD: /sbin/route

之後該使用者執行 sudo route 就不會再被問密碼。

Tip /etc/sudoers 的語法可以解讀成 “<誰> <從哪裡來>=<可以用誰的身份> <執行哪些指令>"。

上面的做法當然使用者要有權限修改 /etc/sudoers 才行,但如果沒有這個條件怎麼辦?這時候我們就可以搬出 Pexpect,它可以在外部程式執行起來後模擬使用者繼續跟它互動-根據不同的 output pattern 餵進不同的 input。

下面的 function 可以在被問到 sudo password 時提供事先準備好的密碼:

sudo_exec.py
import platform, os, logging
import subprocess, pexpect

log = logging.getLogger(__name__)

def sudo_exec(cmdline, passwd):
    osname = platform.system() # 1
    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) # 2
    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
1 不同的 OS 提示輸入密碼的文字不同。
2 輸入 sudo password 後,會快取一段時間,所以接下來的 sudo 可能不會被問密碼。

簡單測試一下:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> from sudo_exec import sudo_exec
>>>
>>> sudo_exec('sudo whoami', 'mypasswd')
DEBUG:sudo_exec:sudo password was asked. 1
'\r\nroot\r\n'
>>> sudo_exec('sudo whoami', 'mypasswd')
'root\r\n'
>>>
1 第一次使用 sudo 時才會被問密碼,接下來一段時間都不會再被問。

總結一下在 Python script 裡 sudo 用法:

  1. 優先考慮修改 /etc/sudoers 讓系統對某些指令不要問 sudo password。
  2. 如果礙於權限不能修改 /etc/sudoers,才考慮透過 Pexpect 動態幫忙輸入 sudo password。
廣告

發表迴響

Please log in using one of these methods to post your comment:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s