如何用PHP執行root權限指令? / Run a command as the Root in PHP
PHP中可以用exec()等指令來執行系統指令,但是執行指令時的身份會是運作網頁伺服器的身份,大部分預設就是www-data。而www-data身份是不能執行影響作業系統的功能,如果想要進行像是重新啟動 (reboot)或是重新啟動Apache Tomcat的操作,就必須以root管理者身份操作才行。本篇參考StackOverflow上的解法,在PHP以SSH2連線以root登入後直接進行指令操作,如此就能解決權限上的問題。
範例程式 / A PHP Example
你可以在GitHub上來看看我這個範例的程式碼。
這個範例主要歸功於實作了SSH功能的phpseclib函式庫,它讓我們可以直接用PHP登入伺服器進行任何操作。
PHP程式講解 / Instruction
在這個範例中主要需要修改的只有index.php。程式內容也很短,只有10行。關鍵的部分在於第4行與第5行的登入伺服器設定、以及第9行使用$ssh->write();執行指令。以下是index.php程式碼的內容:
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('localhost'); // 登入伺服器
$ssh->login('root', 'password'); // 輸入登入的帳號與密碼
echo "<pre>" . $ssh->read('[prompt]') . "</pre>"; // 登入訊息
$ssh->write("reboot"); // 執行指令
echo "<pre>" . $ssh->read('[prompt]') . "</pre>"; // 取得指令執行結果
執行結果如上圖。最後一行之前的資訊是登入之後顯示的訊息,最後一行是輸入「reboot」指令後回傳的reboot訊息。這樣子伺服器就真的會強制重新開機了。
安全性議題 / Security Issue
因為這篇只是單純講解如何用PHP連結SSH來執行root權限的指令,所以並沒有為這個程式加上太多複雜的功能。但是目前這樣的程式並不安全,任何人都可以直接用網頁來執行root才能執行的指令,這是非常危險的事情。
如果真的要把這個功能應用在你的系統中,讓你可以直接用瀏覽器來管理作業系統,那麼你可能需要考慮加上幾個功能:
- 為PHP程式加上白名單,像是我之前提過的r-remote-api.php的做法。
- 不要用網頁預設的80連接埠運作你的PHP。可以額外安裝Lighttpd,在不同的連接埠運作這個功能。
- 在伺服器之前搭配防火牆,在防火牆上加上白名單,以限制可以存取此功能的IP。
- 不要登入真正的root帳號,而是登入其他名稱的帳號。然後讓該帳號具有執行指令的權限。偷懶的做法就是在/etc/passwd中將該帳號的UID跟GID改為0,那該帳號就會有root權限。詳細做法請看這篇,但這樣做會帶來其他的安全性問題。
當然,儘管如此,這個程式仍然有根本上的安全性議題:登入伺服器的被以明碼的方式寫在PHP程式中。到這邊為止還沒有很好的辦法能解決明碼問題,目前就先做到這裡吧。
結語 / Conclusion
實作這個功能的原因是為了解決Apache Tomcat重新啟動的問題。
Apache Tomcat時常當機已經是大家的常識。Apache Tomcat跟資料庫的連線並不穩定,時間一久就會無法連線,而且不會自動恢復。雖然我們可以用crontab排程來設定每天自動重開機,但很多時候我們需要緊急讓服務重新運作,就不能等它在排程時間恢復。
除此之外,修改Spring架構底下的語系檔需要重新啟動Apache之後才能生效。之前DSpace的語系檔也是用類似的方式實作語系檔,因此我也做了重新啟動Tomcat的功能,但是實作上常常會有問題,並不像一般在指令列中執行的結果。
這次使用PHP來連線SSH來執行指令,可以確保不會受到原本網頁伺服器權限的限制,而能夠讓原本在指令列執行的指令正常運作。除此之外,似乎還可以登入其他伺服器來操作的樣子,不過這部分我就沒試過了。