在 Ubuntu 上配置 AppArmor 实现强制访问控制(MAC)

前言

随着云计算的发展,Linux 发行版像 RHEL、Debian、Ubuntu、SUSE 开始广泛的被使用,在很多新上手的用户可能会查看相关教程或者一键包,一般这些教程、一键包的开头第一件事情可能是升级软件,第二件事情就可能是关闭 SELinux 或 AppArmor,那这两个软件是不是真的就那么影响系统使用吗?这两个软件有什么用?

先说是不是

SELinux 即 Security Enhanced Linux,是由美国国家安全局(NSA)设计出来的一个灵活的强制访问控制系统,来限制标准的权限之外的种种权限,在仍然允许对这个控制模型后续修改的情况下,让进程尽可能以最小权限访问或在系统对象(如文件,文件夹,网络端口等)上执行其他操作。

简而言之就是 SELinux 相对于一键包、教程来说 “太过于安全” 而不是和新手,新手对 Linux 的使用并不稳定,网站建设也不稳定,如果使用了 SElinux 限制权限反而可能导致新手遇到权限不够的问题,比如说 监听不到 mysqld.sock 导致 phpMyAdmin 默认配置下无法登入这样的尴尬,又或者说 监听不到 php.sock 导致 Nginx 无法反代 unix socket 模式的 PHP-FPM。

SELinux 在 RHEL 及其社区版 CentOS 上有着非常的应用,而在 Ubuntu 和 Debian 上,AppArmor 也有着非常好的支持,特别是 Ubuntu Server 上。

AppArmor (Application Armor) 是一个类似于 SELinux 的一个强制访问控制方法,通过它你可以指定程序可以读、写或运行哪些文件,是否可以打开网络端口等。AppArmor 配置比 SELinux 更加方便比较适合学习。

介绍

Apparmor 有两种工作模式:Complain (抱怨)和 Enforcement (强制)

Complain – 即抱怨模式,在这种模式下,配置文件中的限制规则并不执行,仅仅对程序的行为进行记录,所以抱怨还是很贴切的。

Enforcement – 即强制模式,顾名思义在这种模式下,配置文件中的限制规则会执行,并且对违反限制规则的软件行为进行记录。

那 Complain 既然不能限制程序,那为什么还需要这种模式呢? 因为——如果某个程序的行为不符合其配置文件的限制,可以将其行为记录到系统日志,就可以根据日志转换成配置文件,因此 Complain 在一些地方也被称为 Complain/Learning 模式

教程

OpenResty 篇

这里将以 Ubuntu Server 16.04 LTS 并使用 OpenResty 为例,在过程中介绍 AppArmor 的一些使用细节和方法。

下面使用的是 OpenResty 官方提供的二进制软件包,编译教程可以查看:Ubuntu 编译安装 OpenResty 及拓展支持

wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
sudo apt-get -y install software-properties-common
add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main"
sudo apt-get update
sudo apt-get install openresty
安装好的 OpenResty

配置目录

一、创建演示用目录

sudo mkdir -p /data/wwwroot/safe
sudo mkdir -p /data/wwwroot/unsafe

二、创建演示文件

cat >> /data/wwwroot/safe/index.html <<EOF
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Safe 文件</title>
</head>

<body>
    <b>该文件允许被访问</b>
</body>
</html>
EOF
cat >> /data/wwwroot/unsafe/index.html <<EOF
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Unsafe 文件</title>
</head>

<body>
    <b>该文件不允许被访问</b>
</body>
</html>
EOF

修改 nginx.conf

编辑 /usr/local/openresty/nginx/conf/nginx.conf 文件,在末尾 } 前添加:

    server {
        listen 8080;
        server_name  _;
        location / { 
            root /data/wwwroot;
        }
    }

保存后,重新 OpenResty:

systemctl restart openresty

访问 http://yourdomain/safe/http://yourdomain/unsafe/ 就可以看到结果页,目前两个页面均可以访问。

被创建的演示文件

AppArmor 篇

安装 AppArmor

sudo apt-get install apparmor-profiles apparmor-utils

创建配置文件 , 使用 aa-autodep 命令创建 OpenResty 的空白配置文件:

cd /etc/apparmor.d/
sudo aa-autodep openresty

切换为 Complain 模式 ,使用 aa-complain 命令:

sudo aa-complain openresty

重启 Openresty

sudo systemctl restart openresty

配置规则

访问网页 ,访问 http://yourdomain/safe/http://yourdomain/unsafe/ 来触发软件记录相关日志。

配置规则 ,运行 sudo aa-logprof 通过日志的记录来获得日志,例如:

Profile:        /usr/local/openresty/nginx/sbin/nginx
Network Family: inet
Socket Type:    stream

 [1 - #include <abstractions/apache2-common>]
  2 - #include <abstractions/lxc/container-base> 
  3 - #include <abstractions/lxc/start-container> 
  4 - #include <abstractions/nameservice> 
  5 - network inet stream, 
(A)llow / [(D)eny] / (I)gnore / Audi(t) / Abo(r)t / (F)inish
Adding #include <abstractions/apache2-common> to profile.

看到相关提示,一般来说走 AAllow 同意就可以走全程了,不过要让 unsafe 目录无法被访问,所以 unsafe 目录需要 DDeny

全部过了以后查看 /etc/apparmor.d/usr.local.openresty.nginx.sbin.nginx 内容:

root@www.mf8.biz:~# cat /etc/apparmor.d/usr.local.openresty.nginx.sbin.nginx
# Last Modified: Sat Jul 15 15:36:02 2017
# From www.mf8.biz
#include <tunables/global>

/usr/local/openresty/nginx/sbin/nginx flags=(complain) {
  #include <abstractions/apache2-common>
  #include <abstractions/base>

  /data/wwwroot/safe/* r,
  deny /data/wwwroot/unsafe/* r,
  /usr/local/openresty/luajit/lib/libluajit-5.1.so.* r,
  /usr/local/openresty/nginx/conf/mime.types r,
  /usr/local/openresty/nginx/conf/nginx.conf r,
  /usr/local/openresty/nginx/logs/error.log w,
  /usr/local/openresty/nginx/sbin/nginx mr,
  /usr/local/openresty/openssl/lib/libcrypto.so.* r,
  /usr/local/openresty/openssl/lib/libssl.so.* r,
  /usr/local/openresty/openssl/openssl.cnf r,
  /usr/local/openresty/pcre/lib/libpcre.so.* r,
  /usr/local/openresty/zlib/lib/libz.so.* r,

}

不过通过日志判断的内容还是不够用的,我们还要继续遇到错误改正错误。

注: 其实 AppArmor 的 # 不一定是注释,例如:#include <abstractions/base> 就是记载 abstractions/base 文件的规则。

切换为 Enforce 模式

sudo aa-enforce openresty

重启使规则生效

sudo /etc/init.d/apparmor reload
sudo service openresty restart

哈哈,这时候可能会报错,例如:

root@www.mf8.biz:~# systemctl restart  openresty
Job for openresty.service failed because the control process exited with error code. See "systemctl status openresty.service" and "journalctl -xe" for details.

那我们就需要来判断是那里出错了,以 OpenResty 为例,可以查看错误的地方有:

  1. openresty -t 的错误提示
  2. /var/log/syslog 文件中的错误日志
  3. /nginx/logs/error.log 中的错误日志

排错 ,例如我的错误是:

root@www.mf8.biz:~# openresty -t
nginx: the configuration file /usr/local/openresty/nginx/conf/nginx.conf syntax is ok
nginx: [emerg] open() "/usr/local/openresty/nginx/logs/nginx.pid" failed (13: Permission denied)
nginx: configuration file /usr/local/openresty/nginx/conf/nginx.conf test failed

/usr/local/openresty/nginx/logs/nginx.pid 没有权限,

那么编辑 /etc/apparmor.d/usr.local.openresty.nginx.sbin.nginx 文件,加入:

  /usr/local/openresty/nginx/logs/error.log w,

继续重启 apparmor 和 openresty

sudo /etc/init.d/apparmor reload
sudo service openresty restart

还报错:

root@www.mf8.biz:~# openresty -t
nginx: the configuration file /usr/local/openresty/nginx/conf/nginx.conf syntax is ok
nginx: [emerg] open() "/usr/local/openresty/nginx/logs/access.log" failed (13: Permission denied)
nginx: configuration file /usr/local/openresty/nginx/conf/nginx.conf test failed

加入:

  /usr/local/openresty/nginx/logs/access.log w,

重启后,终于正常了,再访问 http://yourdomain/safe/http://yourdomain/unsafe/,发现 http://yourdomain/unsafe/ 403 报错了,也就是无法访问了,很棒!

unsafe 已经无法加载

我的最终规则以供大家参考:

# Last Modified: Sat Jul 15 15:36:02 2017
# From www.mf8.biz
#include <tunables/global>

/usr/local/openresty/nginx/sbin/nginx {
  #include <abstractions/apache2-common>
  #include <abstractions/base>

  /data/wwwroot/safe/* r,
  deny /data/wwwroot/unsafe/* r,
  /usr/local/openresty/luajit/lib/libluajit-5.1.so.* r,
  /usr/local/openresty/nginx/conf/mime.types r,
  /usr/local/openresty/nginx/conf/nginx.conf r,
  /usr/local/openresty/nginx/logs/error.log w,
  /usr/local/openresty/nginx/sbin/nginx mr,
  /usr/local/openresty/openssl/lib/libcrypto.so.* r,
  /usr/local/openresty/openssl/lib/libssl.so.* r,
  /usr/local/openresty/openssl/openssl.cnf r,
  /usr/local/openresty/pcre/lib/libpcre.so.* r,
  /usr/local/openresty/zlib/lib/libz.so.* r,
  /usr/local/openresty/nginx/logs/nginx.pid rw,
  /usr/local/openresty/nginx/logs/access.log w,

}

结语

AppArmor 配置 OpenResty 的教程就到这里结束了,AppArmor 的安全配置还是比较复杂的,因为不同服务器势必用法不同,所以不可能千篇一律所以自己的服务器还是需要自己进行针对性规则配置,而且往往越复杂,功能越多的服务配置规则越麻烦越容易出现小问题。

一般来说,建议在 Complain 模式下积累个一星期的 “抱怨” 再做判断可能会更好。当然还需要多结合日志进行管理。

再解释一下权限的意思:

r: w: m: 存储器映射, k: 文件锁定 l: 创建硬链接 ix: 执行并继承该安全配置 Px: 在清理环境之后,执行并使用其他安全配置 Ux: 在清理环境之后,执行不做限制

发表评论

发表评论

沙发空缺中,还不快抢~