*本文作者:Li4n06,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。

前言

最近上的某水課的作業是出 ctf web題目,然而大多數同學連 php 都沒學過,(滑稽)更別說配置伺服器了,於是我想能不能趁機賺一波外快 造福一下同學,(其實就是想折騰了)。所以打算把我自己的 vps 分成虛擬空間給大家用。但是一般的虛擬空間安全性難以得到保證,一個空間出問題,其他的用戶可能都跟著遭殃,也就是旁站攻擊。更何況我們這個虛擬空間的用處是 ctf web 題目,總不能讓人做出一道題目就能順手拿到所有題目的 flag 吧。於是想到了使用 docker 來構建安全的虛擬空間,其間遇到了不少問題,下面就是折騰的過程了。

實現思路

大體的思路是,在我的 vps 上為每個用戶創建一個文件目錄,然後將目錄掛載到 docker 容器的默認網站目錄,也就是/var/www/html,,用戶可以通過 FTP 將網站源碼上傳到自己的文件目錄,文件也會同步到容器內。這樣就實現了各個空間的環境隔離,避免旁站攻擊。

而資料庫則可以單獨構建一個 mysql 容器,為每個用戶分配一個 user&database;,讓用戶和空間容器來遠程連接。

前期準備

選擇鏡像:

空間使用的鏡像為:mattrayner/lamp:latest-1604 (ubuntu 16.04 + apachd2 + mysql,其實只要有mysql-client 就可以了)

資料庫所使用的鏡像為: mysql:5 (mysql 官方鏡像)

配置FTP:

和配置常規的 FTP 沒什麼區別,這裡特彆強調3點:

一定要開啟 ch_root,防止不同用戶之間可以互相查看文件;

如果使用被動模式,那麼 雲主機的安全組 或者iptables 不要忘了放行埠;

將 umask 設置為 022 (保證用戶上傳的文件默認許可權為755。

選擇一個位置存放用戶文件夾:

我這裡新建一個 ~/rooms/ 來存放用戶的文件夾。

配置資料庫

1. 網路:

要讓虛擬空間的容器能夠遠程連接資料庫,首先要使容器之間在一個網段,那麼我們就需要設置一個橋接模式的 docker network,我這裡使用 172.22.0.0/16 這個網段。

$ docker network create --driver = bridge --subnet = 172 .22.0.0/16 room_net

2.創建 MySQL 容器:

我們的資料庫需要滿足:

允許用戶遠程連接;

允許空間容器連接。

第一點要求,我們通過將資料庫容器的 3306 埠映射到 VPS 的開放埠即可,我這裡映射到 3307。

第二點要求,只要通過我們剛剛設置的 docker network 即可實現。

所以啟動創建容器的命令是的命令是:

$ docker run -d --name room-mysql --network room_net --ip 172 .22.0.1 -p 3307 :3306 -e MYSQL_ROOT_PASSWORD = your_password mysql:5

值得注意的一點是,root 用戶是不需要遠程登錄的,出於安全考慮,我們應該 禁止其通過localhost意外的host登錄

執行:

$ docker exec -it room-mysql /bin/bash -c "mysql -u root -p -e"use mysql;update user set host="localhost" where user="root";drop user where user="root" and host="%";flush privileges;""

創建空間過程

做好前期的準備工作,我們就可以開始構建空間了,出於方便我們將整個過程編寫成 shell 腳本,這樣以後要新建空間的時候,只需要運行一下就可以了。

我們創建空間需要以下幾個步驟:

1. 創建新的 FTP 用戶

這個用戶應該滿足這樣的要求:

可以上傳文件到虛擬空間用戶文件夾 (廢話);

不能訪問除虛擬空間用戶文件夾之外的位置 (在配置 FTP 時通過ch_root 實現);

創建的時候設置一個隨機密碼;

不能通過 ssh 登陸 (其實這也是用戶能通過 ftp 連接 的必須條件。如果不限制的話,ftp登錄時會出現 530 錯誤。

那麼對應的 shell 腳本就是:

#/home/ubuntu/rooms/ 即你的vps上用來存放用戶文件夾的位置  # $1 參數為要設置的用戶名,也是虛擬空間容器&資料庫用戶&資料庫&用戶文件夾的名字useradd -g ftp -d /home/ubuntu/rooms/$1 -m $1 pass=`cat /dev/urandom | head -n 10 | md5sum | head -c 10`  #生成隨即密碼echo $1:$pass | chpasswd                                    #為用戶設置密碼#限制用戶通過 ssh 登錄(如/etc/shells 里沒有/usr/sbin/nologin 需要自己加進去usermod -s /usr/sbin/nologin $1             echo "create ftp user:$1 indentified by $pass"              #輸出用戶名和密碼

2. 新建資料庫用戶&資料庫,並為用戶賦權

這部分操作比較簡單,我們就只需要為用戶新建一個 MySQL 賬戶和一個專屬資料庫就好了。

shell 腳本:

# 讓用戶輸入 mysql 容器的 root 密碼read -sp "請輸入 MySQL 容器的 root 賬戶密碼:" mysql_pass# 創建資料庫docker exec -it mysql-docker /bin/bash -c "mysql -u root -p$mysql_pass -e "create database $1;""# 生成密碼pass=`cat /dev/urandom | head -n 10 | md5sum | head -c 10`# 創建 MySQL 用戶docker exec -it mysql-docker /bin/bash -c "mysql -u root -p$mysql_pass -e "CREATE USER "$1"@"%" IDENTIFIED BY "$pass";""# 為用戶賦予許可權docker exec -it mysql-docker /bin/bash -c "mysql -u root -p$mysql_pass -e "grant all privileges on $1.* to "$1"@"%";flush privileges;""# 輸出賬戶信息echo "create database user:$1@"%" indentified by $pass"

3. 新建空間

到現在我們已經可以創建空間容器了,想一想這個空間要滿足什麼基本要求呢?

能夠外網訪問;

能夠連接資料庫;

掛載用戶文件夾內的文件到網站根目錄。

那麼命令就是:

$ docker run -d --name $1 --network room_net -p $2 :80 -v /home/ubuntu/rooms/$1 /www:/var/www/html mattrayner/lamp:latest-1604

但是作為一個用做虛擬空間的容器,我們還需要考慮 內存 的問題,如果不加限制,docker默認使用的最大內存就是 VPS 本身的內存,很容易被人惡意耗盡主機資源。

所以我們還要限制一下容器的最大使用內存。

關於 docker 容器內存使用的有趣的現象:

在最初,我把容器的內存限制到了 128m,然後訪問網站發現 apache 服務沒有正常啟動,於是我把內存限制上調到了 256m,然後執行 docker stats 發現容器內存使用率接近100%;

有趣的是,當我嘗試限制內存為 128m ,然後手動開啟 apache 服務時,發現服務完全可以被正常啟動,查看內存佔用率,發現只佔用了 30m 左右的內存。

為什麼會出現這種情況呢?我大概猜想是因為容器內還有一些其他服務,當限制內存小於 256m 的時候,這些服務無法被同時啟用,但是我們可以只啟用 apache 啊!

於是命令變成了下面這樣:

docker run -d --name $1 --cpus 0 .25 -m 64m --network room_net -p $2 :80 -v /home/ubuntu/rooms/$1 /www:/var/www/html mattrayner/lamp:latest-1604 docker exec -it $1 /bin/bash -c "service apach2 start;"

最後一步,修改掛載文件夾的所有者:

到這時,理論上我們的空間已經可以正常使用了,可是我用 FTP 連接上去發現,並沒有許可權上傳文件。

經過漫長的 debug 後發現,在容器啟動一段時間後,我們掛載到容器內部的文件夾的所有者發生了改變,於是我查看了容器內部的 run.sh 腳本,發現了這樣的內容:

if [ -n "$VAGRANT_OSX_MODE" ];then    usermod -u $DOCKER_USER_ID www-data    groupmod -g $(($DOCKER_USER_GID + 10000)) $(getent group $DOCKER_USER_GID | cut -d: -f1)    groupmod -g ${DOCKER_USER_GID} staff    chmod -R 770 /var/lib/mysql    chmod -R 770 /var/run/mysqld    chown -R www-data:staff /var/lib/mysql    chown -R www-data:staff /var/run/mysqldelse    # Tweaks to give Apache/PHP write permissions to the app    chown -R www-data:staff /var/www    chown -R www-data:staff /app    chown -R www-data:staff /var/lib/mysql    chown -R www-data:staff /var/run/mysqld    chmod -R 770 /var/lib/mysql    chmod -R 770 /var/run/mysqldfi

可以看到,當沒有設置 $VAGRANT_OSX_MODE 這個環境變數時,容器會修改 /app(/var/www/html 的軟鏈接)文件夾的所有者為 www-data ,那麼我們就需要在啟動容器時,設置這個環境變數值為真。

而 /app 文件夾 的默認所有者是 root 用戶,我們將本地文件夾掛載到容器內的/app,後,本地文件夾的所有者也會變為 root 。所以我們還需要修改本地文件夾的所有者。

於是創建容器的 shell 腳本又變成了:

# 啟動容器docker run -d --name $1 --cpus 0.25 -m 64m --network room_net -p $2:80 -eVAGRANT_OSX_MODE=1 -v /home/ubuntu/rooms/$1/www:/var/www/html mattrayner/lamp:latest-1604# 啟動apache2docker exec -it $1 /bin/bash -c "service apache2 start;"# 修改掛載文件夾的所有者chown $1:ftp -R /home/ubuntu/rooms/$1/www

最後的腳本:

到現在創建空間的過程就結束了,那麼貼上最後的腳本

創建空間腳本:

#!/bin/bash# The shell to create new room# Last modified by Li4n0 on 2018.9.25# Usage: #   option 1: database/dbuser/room/ftpuser/ name#   option 2: port# create new ftp useruseradd -g ftp -d /home/ubuntu/rooms/$1 -m $1pass=`cat /dev/urandom | head -n 10 | md5sum | head -c 10`echo $1:$pass | chpasswdusermod -s /usr/sbin/nologin $1echo "create ftp user:$1 indentified by $pass"# create new databaseread -sp "請輸入 MySQL 容器的 root 賬戶密碼:" mysql_passdocker exec -it mysql-docker /bin/bash -c "mysql -u root -p$mysql_pass -e "create database $1;""pass=`cat /dev/urandom | head -n 10 | md5sum | head -c 10`docker exec -it mysql-docker /bin/bash -c "mysql -u root -p$mysql_pass -e "CREATE USER "$1"@"%" IDENTIFIED BY "$pass";""docker exec -it mysql-docker /bin/bash -c "mysql -u root -p$mysql_pass -e "grant all privileges on $1.* to "$1"@"%";flush privileges;""echo "create database user:$1@"%" indentified by $pass"#create new roomdocker run -d --name $1 --cpus 0.25 -m 64m --network room_net -p $2:80 -eVAGRANT_OSX_MODE=1 -v /home/ubuntu/rooms/$1/www:/var/www/html mattrayner/lamp:latest-1604docker exec -it $1 /bin/bash -c "service apache2 start;"chown $1:ftp -R /home/ubuntu/rooms/$1/www

刪除空間腳本:

#!/bin/bashread -sp "請輸入 MySQL 容器的 root 賬戶密碼:" mysql_pass#drop the databasedocker exec -it mysql-docker /bin/bash -c "mysql -u root -p$mysql_pass -e "drop database $1""#delete dbuserdocker exec -it mysql-docker /bin/bash -c "mysql -u root -p$mysql_pass -e "use mysql;drop user "$1"@"%";flush privileges;""#delete the containerdocker stop $1docker rm $1#delete ftp useruserdel $1rm -rf /home/ubuntu/rooms/$1

用法:

# 創建sudo create_room.sh room1 10080  # 用戶名 映射到 VPS 的埠# 刪除sudo del_room.sh room1

總結

到這裡我們就實現通過 docker 搭建較安全的虛擬空間了,當然,如果真的想上線運營,還有很多需要完善的地方,比如 空間大小的限制、用戶文件和資料庫的定時備份等等,有興趣的朋友可以去自己完善。

那麼到這裡我的折騰就結束了,現在去賣空間給同學發福利了!

*本文作者:Li4n06,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。

推薦閱讀:

相关文章