在我開的 Ansible Workshop 中,不管是課前的【許願池】,還是課程現場的 Q&A,總有一個熱門議題:「如何確保系統是安全的?」

這是大哉問,不是單獨一門組態管理課就能探討到令人滿意的地步(資安也不是我所擅長的⋯⋯)。不過,被問到的次數太多了,我還是給個簡單的導引吧。

系統安全有許多層次。以 Learning Linux Security 教學課程的大綱來看,至少涵蓋幾個層次:

  • Booting:作業系統開機環節
  • Kernel:作業系統核心
  • Services:典型系統服務
  • Users and permissions:帳號及權限
  • Firewalls:防火牆
  • Logging and log management:日誌管理
  • Intrusion detection and prevention:入侵偵測及預防
  • Utilities:診斷工具

各個環節,都有各自專門知識及輔助工具(我就說這是大哉問吧⋯⋯)。這些都是傳統 admin 或 ops 的責任區。

不過,即使不是專業的 ops,身為一位 dev,至少有兩點是自己的責任區,需要好好掌握的。

首先是 Users and permissions 環節。近年來流行的軟體設計方式,部署時,常常不會 100% 寄生在 Apache、Nginx、Tomcat 這些 application container 底下,而是像 PHP 的 FPM 模式、Python 的 Gunicorn 模式、Ruby 的 Unicorn 模式,有一個或多個協同作業的 process;甚至像 Node.js 或 Java 硬漢,往往不假外力,直接就把程式本身設計成 daemon 了。此時,開發者就不能逃避,必須考慮如何設定該軟體的帳號及權限,包括 process 的 UID、GID,以及相關的檔案目錄存取權限。

其次是 Services 環節。以 daemon 形式存在的軟體,通常會安裝成 service,透過 SysV initUpstartsystemd 等機制啟動與管理。這時,也必須連帶把前述的帳號權限處理好。有潔癖的,甚至還會加掛 chrootAppArmorSELinux 等嚇人的保護機制。

對於帳號權限不知道該如何設定的人,通常最好的方式是:觀摩別人是怎麼做的,尤其是經典級的 services。

我們就在 Ubuntu 14.04 裡面,好好觀摩一下吧。

以 Apache 為例,如果你是透過 apt-get install apache2 來安裝,可看到它是以 www-data 的身份來執行 worker process:

$ ps aux | grep apache2
root      2734  0.0  0.5  73368  2628 ?        Ss   04:36   0:00 /usr/sbin/apache2 -k start
www-data  2737  0.0  0.4 362532  2240 ?        Sl   04:36   0:00 /usr/sbin/apache2 -k start
www-data  2738  0.0  0.4 362532  2240 ?        Sl   04:36   0:00 /usr/sbin/apache2 -k start

以 Nginx 為例,如果你是透過 apt-get install nginx 來安裝,可看到它也是以 www-data 的身份來執行 worker process:

$ ps aux | grep nginx
root      3313  0.0  0.2  85884  1340 ?        Ss   04:44   0:00 nginx: master process /usr/sbin/nginx
www-data  3314  0.0  0.3  86228  1768 ?        S    04:44   0:00 nginx: worker process
www-data  3315  0.0  0.3  86228  1768 ?        S    04:44   0:00 nginx: worker process
www-data  3316  0.0  0.3  86228  1768 ?        S    04:44   0:00 nginx: worker process
www-data  3317  0.0  0.3  86228  1768 ?        S    04:44   0:00 nginx: worker process

以 MySQL 為例,如果你是透過 apt-get install mysql-server 來安裝,可看到它是以 mysql 的身份來執行:

$ ps aux | grep mysql
mysql     7004  0.5  8.8 623920 44536 ?        Ssl  04:48   0:00 /usr/sbin/mysqld

從以上三個經典案例,可以歸納出一個小結論:「他們都盡可能不以 root 身分執行。」

這些 www-datamysql 帳號,甚至沒有登入的權力:

$ cat /etc/passwd | egrep '(mysql|www-data)'
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
mysql:x:109:116:MySQL Server,,,:/nonexistent:/bin/false

$ cat /etc/group  | egrep '(mysql|www-data)'
www-data:x:33:
mysql:x:116:

也沒有偷開 sudo 後門:

$ sudo cat /etc/sudoers
Defaults    env_reset
Defaults    mail_badpass
Defaults    secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# User privilege specification
root    ALL=(ALL:ALL) ALL
# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
# Allow members of group sudo to execute any command
%sudo    ALL=(ALL:ALL) ALL

所以,這些經典級的服務,他們都非常自制,極度限縮自己的權力。

用更有學問的說法:他們都遵循最小權限原則 (principle of least privilege; POLP)。

維基百科提到 POLP 的歷史淵源,可追溯至 1974 年:

Every program and every privileged user of the system should operate using the least amount of privilege necessary to complete the job.

— Jerome Saltzer, Communications of the ACM

可見這算是歷久彌新的原則了。

如果要將 POLP 套用到目前流行的 web application,我引述 Secure Your Node.js Web Application 一書的說法

From our web application standpoint, we have the following rules:

  1. The web application should not be run with root privileges. It should instead use a limited account that has access to only the required resources.
  2. The database account should not be a root account. The account should have limited privileges over the database tables.
  3. The users of the web application should be given the minimum set of privileges they need.

也引述《Ruby on Rails 實戰聖經》的說法

設定伺服器部署使用者

習慣上我們會在伺服器上開一個專門的帳號,用來放 Rails 應用程式:sudo adduser --disabled-password deploy ⋯⋯

本機執行 cap deploy:check,就會自動登入遠端的伺服器,在登入的帳號下新建 current、releases 和 shared 這三個目錄。

所以,回頭檢視一下自己的軟體部署流程,有沒有遵循最小權限原則吧!