前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Docker构建安全的虚拟空间

使用Docker构建安全的虚拟空间

作者头像
FB客服
发布2018-10-25 11:16:00
3.2K0
发布2018-10-25 11:16:00
举报
文章被收录于专栏:FreeBufFreeBuf

*本文作者: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 这个网段。

代码语言:javascript
复制
$ docker network create --driver = bridge --subnet = 172 .22.0.0/16 room_net

2.创建 MySQL 容器:

我们的数据库需要满足:

允许用户远程连接; 允许空间容器连接。

第一点要求,我们通过将数据库容器的 3306 端口映射到 VPS 的开放端口即可,我这里映射到 3307。

第二点要求,只要通过我们刚刚设置的 docker network 即可实现。

所以启动创建容器的命令是的命令是:

代码语言:javascript
复制
$ 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登录

执行:

代码语言:javascript
复制
$ 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 脚本就是:

代码语言:javascript
复制
#/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 脚本:

代码语言:javascript
复制
# 让用户输入 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. 新建空间

到现在我们已经可以创建空间容器了,想一想这个空间要满足什么基本要求呢?

能够外网访问; 能够连接数据库; 挂载用户文件夹内的文件到网站根目录。

那么命令就是:

代码语言:javascript
复制
$ 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 啊!

于是命令变成了下面这样:

代码语言:javascript
复制
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 脚本,发现了这样的内容:

代码语言:javascript
复制
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 脚本又变成了:

代码语言:javascript
复制
# 启动容器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

最后的脚本:

到现在创建空间的过程就结束了,那么贴上最后的脚本

创建空间脚本:

代码语言:javascript
复制
#!/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

删除空间脚本:

代码语言:javascript
复制
#!/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

用法:

代码语言:javascript
复制
# 创建sudo create_room.sh room1 10080  # 用户名 映射到 VPS 的端口# 删除sudo del_room.sh room1

总结

到这里我们就实现通过 docker 搭建较安全的虚拟空间了,当然,如果真的想上线运营,还有很多需要完善的地方,比如 空间大小的限制、用户文件和数据库的定时备份等等,有兴趣的朋友可以去自己完善。

那么到这里我的折腾就结束了,现在去卖空间给同学发福利了!

*本文作者:Li4n06,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FreeBuf 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 实现思路
    • 前期准备
    • 创建空间过程
      • 1. 创建新的 FTP 用户
        • 2. 新建数据库用户&数据库,并为用户赋权
          • 3. 新建空间
            • 最后的脚本:
            • 总结
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档