计算机安全近日再次成为管理器的热门话题。互联网上冒起了数十个网站,而每个都推荐自己的「完美」设置指南。它们的推荐都拥有常见的钟型分布:好的推荐、一般的推荐、与及那些足以令到你的数据成为一堆废物的推荐。在这里,我们要讨论如何正确地加固一个 CentOS 5 系统。这个「正确」的方法建基於 NSA RHEL5 指南、Steve Grubb 的「加固 RHEL」简报、及其它可靠的数据来源。

在这个 wiki 文章内,我们会假设正在安装一台服务器。笔记本及工作台或许会有不同需求,例如加密的文件系统,是不包含在这里的。同样地,它们或许会应用到 USB 存储器或无线模块,但在这份安全性指南内它们会被停用。

分割文件系统

通过将文件系统配置到不同分区,你可以微调访问权及功能。这样做能够将访问权划分得更细微,而且多加一层安全保障来阻吓任何坏人。

Steve Grubb 颇正确地推荐将用户有权写入的地方放在独立的分区内。这样做令你能防止硬连结越权、新增设置、及其它不受欢迎的行为。

更改 fstab

当你将分区划分好及设置尺寸后,你便可以尽量收窄个别挂载点的权限。只要情况容许,你便应该加入 nodev、nodexec 及 nosuid。下面是一个已经适切地收窄权限的 /etc/fstab 样例档:

/dev/VG_OS/lv_root          /        ext3      defaults     1 1
/dev/VG_OS/lv_tmp           /tmp     ext3      defaults,nosuid,noexec,nodev  1 2
/dev/VG_OS/lv_vartmp        /var/tmp ext3      defaults,nosuid,noexec,nodev 1 2
/dev/data_vol/lv_home       /home    ext3      defaults,nosuid,nodev  1 2
/dev/VG_OS/lv_var           /var     ext3      defaults,nosuid     1 2
/dev/data_vol/lv_web        /var/www ext3      defaults,nosuid,nodev  1 2
/dev/sda1                   /boot    ext3      defaults,nosuid,noexec,nodev  1 2
tmpfs                       /dev/shm tmpfs     defaults 0 0
devpts                      /dev/pts devpts    gid=5,mode=620 0 0
sysfs                       /sys     sysfs     defaults    0 0
proc                        /proc    proc      defaults    0 0
/dev/_VG_OS/lv_swap         swap     swap      defaults    0 0 

明显地,你需要修改这个样例来迎合你的系统。LVM、扇区名称、标签等都可以被更改。请不会只字不漏地复制这个样例档,并期望它适合你使用。

网页服务器的挂载亦可以被设置为 noexec,但这样做会影响到 cgi 类应用程序,与及依赖 execute bit hack 的服务器端包含档。如果你不会应用 cgi 程序,我推荐最低限度测试采用 noexec,并看看会否有副作用。

安装组件

当你在系统上安装组件时,请记得简单就是美。你的系统上越多东西,就意味著越多东西需要追踪漏动及更新,及越多东西有机会妨碍你做想做的事情。由於服务器的工种可以很不同,我甚至不会尝试列出哪些组件应该或不应该安装。反而,我会推荐采用自定组件清单及只选择基本组件这个简单策略。当你这样做之后,列出现时安装了的组件,然后继续清除你不需要的东西。

yum list installed >> ~/installed.txt

x86_64 用户: 如果你不需要 i386/i686 组件来提供兼容性,你或许会想用 yum remove *.i?86 来删除它们,然后在 /etc/yum.conf 内加入 exclude = *.i?86 来防止它们再出现。

定期更新

既然我们已经设置了最细的组件集,我们便须要定时更新它们。我不推荐使用 yum-updatesd,因为它给我太多抢夺资源的坏经验。你可以设立一个 cron 工作来进行更新或检查更新,这亦是 NSA 指南的推荐。你可以简单地每周进行手动更新,或者赴力设置一个 spacewalk 服务器进行跨系统的更新管理。不论你采用哪一种更新政策,它基本上是:适时地进行更新来避免问题发生。另外,订阅 CentOS-Announce 这个邮件列表亦是一个不错的主意。这样,每次有更新时,你都会收到通知,而你便可以按所需加入重要的修正。

一旦你已经充份地修剪及更新你的组件清单,你好应该检查你的服务清单及停用任何在服务器上不需用的服务。再一次,由於每个环境都不同,我不会试图告诉你甚么应该或不应该被停用;然而,你应该自问服务器是否真的需要 bluetooth 服务 :-P

基本加固

既然我们已经完成磁盘分割、收窄权限、及修剪组件清单,现在是时候进到核心部份。是时候将系统加固了。下列某些方法或许不适用於你的环境。你应该考虑每个方法,但你的处境或许需要用到别的方法。

实体保护

关於如何保护 grub,请参阅 BIOS and Boot Loader Security。要令单一用户模式询问 root 的口令,你可以用:

echo "# 引导单一用户模式时要求 root 口令" >> /etc/inittab
echo "~~:S:wait:/sbin/sulogin" >> /etc/inittab
echo "避免人停止服务器"
perl -npe 's/ca::ctrlaltdel:\/sbin\/shutdown/#ca::ctrlaltdel:\/sbin\/shutdown/' -i /etc/inittab

假如你的环境中不会应用到 USB 存储器,现在便停用它

echo "停用 USB 存储器"
echo "blacklist usb-storage" > /etc/modprobe.d/blacklist-usbstorage

用户权限

这算是最大的类型,包含了一些最重要的东西。由於每个机构都有所不同,它们未必全数适用於你,但请考虑它们。

在缺省情况下,用户被赋予不少自由度。你可以借着收窄这些宽限来加固系统。由於 root 拥有最多权力,我们就以限制 root 用户作为一个开始。

限制 root 用户

一旦服务器已经完成开机及在运行中,除非是紧急情况,否则 root 不应该直接登录。这些情况多数须要亲身在控制台前,因此这应该是唯一容许 root 登录的地方。要这样做,我们需要修改 /etc/securetty。此外,除了 root,其它人不应有权进入 root 的主目录。缺省值其实已很接近,但仍不够严格。

echo "tty1" > /etc/securetty
chmod 700 /root

因於我们基本上已经删除 root 从本地控制台以外登录的可能性,使用 su 及 sudo 已经成为必须的。这个做法在多管理员的环境下亦提供了一些次要的好处。

口令政策

我们大致上已经限制了 root,是时候处理其它用户了。首先我们要为新创建的户口设置一些基本原则。

echo "口令每 180 日便失效"
perl -npe 's/PASS_MAX_DAYS\s+99999/PASS_MAX_DAYS 180/' -i /etc/login.defs
echo "口令每日只可更改一次"
perl -npe 's/PASS_MIN_DAYS\s+0/PASS_MIN_DAYS 1/g' -i /etc/login.defs

以下的指令会更新你的系统以 sha512 取代 md5 作口令的保护。这个做法会缓解一些官僚方面有关以 md5 保护口令的安全问题。这样做亦能让多疑的人安心。

authconfig --passalgo=sha512 --update

限制 umask

更改缺省的 umask 可以令事情变得有趣。我们推荐用 077 作为一个安全的 umask,但它会对经常分享文件的用户构成很大不便。如果你决定实施这个方案,请聆听你用户的意见。

perl -npe 's/umask\s+0\d2/umask 077/g' -i /etc/bashrc
perl -npe 's/umask\s+0\d2/umask 077/g' -i /etc/csh.cshrc

由这里开始事情变得有点巧妙。假如一个用户不能输入正确的凭证,pam_tally2 将会拒绝访问直至 unlock_time 时间已过去。换句话说,假若你在 3 次内不能正确登录,你将要等候一段时间才能再次作出尝试。

修改 pam

现在我们需要更新 /etc/pam.d/system-auth

touch /var/log/tallylog

cat << 'EOF' > /etc/pam.d/system-auth
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      pam_env.so
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 500 quiet
auth        required      pam_deny.so
auth        required      pam_tally2.so deny=3 onerr=fail unlock_time=60

account     required      pam_unix.so
account     sufficient    pam_succeed_if.so uid < 500 quiet
account     required      pam_permit.so
account     required      pam_tally2.so per_user 

password    requisite     pam_cracklib.so try_first_pass retry=3 minlen=9 lcredit=-2 ucredit=-2 dcredit=-2 ocredit=-2
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok remember=10
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so
EOF

/var/log/tallylog 是 pam 用来记录失败登录的二进制日志。你可以利用没有引数的 pam_tally2 指令来查看失败了的登录尝试,或者用 pam_tally2 --reset -u <用户名称> 提早将户口解封。

删除闲置用户

现在我们已经限制了服务器的登录选项,让我们删除闲杂人等。要这样做,我们须要利用 /etc/profile 内的一个 bash 变量。当然有些简单的方法可以避过它,但我们想要的是多重保安。

echo "闲置用户在 15 分钟后会被删除"
echo "readonly TMOUT=900" >> /etc/profile.d/os-security.sh
echo "readonly HISTFILE" >> /etc/profile.d/os-security.sh
chmod +x /etc/profile.d/os-security.sh

限制 cron 及 at

在某些情况下,管理员或许会想容让 root 或其它受信任的用户执行 cron 工作或 at 的脚本。要加固它们,你需要在 /etc 内置立含有所有被拒用户名称的 cron.deny 及 at.deny 文件。达至这个目标的一个简单方法就是分析 /etc/passwd。以下的脚本可以为你服务。

echo "加固 Cron"
touch /etc/cron.allow
chmod 600 /etc/cron.allow
awk -F: '{print $1}' /etc/passwd | grep -v root > /etc/cron.deny

echo "加固 AT"
touch /etc/at.allow
chmod 600 /etc/at.allow
awk -F: '{print $1}' /etc/passwd | grep -v root > /etc/at.deny

网络安全

我们已经初步将保护给予操作系统,现在是时候查看基本的网络功能。在这里我们不会太关注在服务上。我们只会从管理的角度看界面本身及 ssh。

内核网络安全

有数过方法只需通过简单的改动,及将某些模块放入黑名单,已经能够改善网络的安全性。

停用无联机网

由於我们在讨论服务器的安全问题,无联机网其实不应在议题之内。如果你需要一个无线网络,你可以略过这部份,因为我们将要停用所有无线驱动程序。你可以遂一查看你的内核在 /lib/modules 里的内容并删除所有无线驱动程序。这样做的确可以停止无联机网,但不是一个永久性的解决办法。每当你将内核升级,它们又会再出现,而你必需重复这个动作。反过来,我们可以用一个简单的回圈,通过 /etc/modprobe.d 内的黑名单文件来停用它们。

for i in $(find /lib/modules/`uname -r`/kernel/drivers/net/wireless -name "*.ko" -type f) ; do echo blacklist $i >> /etc/modprobe.d/blacklist-wireless ; done

sysctl 保安

接下来我们需要看看 /etc/sysctl.conf 的内容并作出一些基本的改动。如果这些行已经存在,请更改它们至下面的模样。假如它们不存在,你只需加入它们。如果你的服务器有多个网络界面,下面某些内容或许会构成问题。请在正式使用它们前进行测试。如果你想知道更多关於这些选项,安装 kernel-doc 这个组件,并参阅 Documentation/networking/ip-sysctl.txt。

net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.tcp_max_syn_backlog = 1280
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_timestamps = 0

利用 TCP 包装函式

TCP 包装函式可以提供一个简便、快捷的方法来控制相关应用程序的访问权。能配合 TCP 包装函式的应用程序包括 sshd 及 portmap。下面是一个有限的样例。这个样例拦截所有不是 ssh 的流量。

echo "ALL:ALL" >> /etc/hosts.deny
echo "sshd:ALL" >> /etc/hosts.allow

强化 iptables

CentOS 缺省的 iptables 规则集比较上是宽松了一些:缺省的政策接纳流量,有些端口被敞开,而流量基本上不必问责。我们可以做得更好。

在编辑器内打开 /etc/sysconfig/iptables,并让我们看看它。在首 3 行内已经有两个问题。INPUT 及 FORWARD 这两条规则链被设置为接纳一切。接下来我们看见 50、51、5353、631 及 22 号端口被打开。我对 22 号端口没有太大问题。但其它端口不应在这里,除非你想 mDNS、cups、及 ipsec 与外界沟通。我一般不喜欢陌生人使用我的打印机。

这里亦未有将恶意扫描或其它不受欢迎的行为记录下来。一个较强的规则集会是这个样子:

#丢弃任何未经特别批准的东西。容许所有对外的流量
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:RH-Firewall-1-INPUT - [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-reply -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp --icmp-type time-exceeded -j ACCEPT

# 接纳 ping
-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -j ACCEPT

# 记录在 eth0 上所有声称来自本地或不须选路的网络的东西
# 如果你使用下面其中一个本地网络,请从以下清单中删除它

-A INPUT -i eth0 -s 10.0.0.0/8 -j LOG --log-prefix "IP DROP SPOOF A: "
-A INPUT -i eth0 -s 172.16.0.0/12 -j LOG --log-prefix "IP DROP SPOOF B: "
-A INPUT -i eth0 -s 192.168.0.0/16 -j LOG --log-prefix "IP DROP SPOOF C: "
-A INPUT -i eth0 -s 224.0.0.0/4 -j LOG --log-prefix "IP DROP MULTICAST D: "
-A INPUT -i eth0 -s 240.0.0.0/5 -j LOG --log-prefix "IP DROP SPOOF E: "
-A INPUT -i eth0 -d 127.0.0.0/8 -j LOG --log-prefix "IP DROP LOOPBACK: "

# 接纳任何已设立的连接
-A RH-Firewall-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# 接纳 ssh 流量。可能的话,限制到已知的 IP 地址。
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT

# 记录并丢弃余下的封包
-A RH-Firewall-1-INPUT -j LOG
-A RH-Firewall-1-INPUT -j DROP
COMMIT

你可能会争议我们既然回答 ping,丢弃流量而不拒绝它们实在骗不到谁。这只是个人首选。如果你宁愿拒绝流量,你可以将 COMMIT 前一行改为:

-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited

防止纂改

由於我们放上了不少时间来加固系统,及设置它按你的方式运作,一般来说,确保别人不会瞒著你纂改它是一个好主意。这一点对於拥有多位管理员的机构,与提防坏蛋来说都同样适用。CentOS 内置了两个非常好的工具来保护你的系统。第一个工具是 aide。aide 有些像 tripwire,它可以定期将你系统内主要的文件与散列对比来找出改动。第二个工具是 auditd。CentOS 内的 audit 子系统会按照你所定的规则,即时监视你的系统。它会记录你要求它所做的一切动作,与及格外的信息。

如果可以的话,你应该考虑在一个非公开的网络界面上设立一个中央的日志收集服务。假若日志被传送到一个不能访问的远程系统,恶意用户便更难隐藏他们的踪迹。

aide

为了免得我重复别人已做的工夫,有关如何设置 aide 请参阅 http://www.server-world.info/en/note?os=CentOS_7&p=aide。然而我们推荐你采用以下 crontab 项目取代该网站的版本。

00 01 * * * /usr/sbin/aide --check | mail -s 'Daily Check by AIDE' root

虽然该网站提及 CentOS 7,这些步骤同样适用于旧的 CentOS 发行版本。

auditd

有待写成

Translation of revision 32

zh/HowTos/OS Protection (last edited 2016-02-07 11:59:54 by TimothyLee)