Freeshell 添加内存限制留出 4G 余量

由于 freeshell 没有内存限制,近期经常出现 OOM。现在将每个 freeshell 的内存限制为(物理内存 – 4G),这样单个 freeshell 占用大量内存也不会把整个物理节点搞死。

附群发的通知邮件:

This is to notice you that we have enforced memory limit on each freeshell to be (hardware memory – 4G), which is 12G for your freeshell. Don’t worry, your freeshell can still make use of most memory on hardware node.

Previously there was no memory limit on freeshell. Recently there are cases in which some freeshell allocates too much memory and leads to OOM (Out-Of-Memory) of the hardware node, making all freeshells on the hardware node inaccessible before the system kills the memory-demanding process. So this memory limitation is essentially a security measure to ensure freeshell isolation.

GitLab升级至6.9

5月23日 15:10 GitLab 由6.8.1升级至6.9.0,本次升级采用一键式脚本,服务共中断4分13秒。

新版本主要特性:

  • 强化Web编辑器。允许用户在编辑期间diff或预览Markdown
  • 支持发起标签(tag)合并(merge)请求
  • 支持通过API发起合并(merge)请求
  • 允许设置 http/https为不同的ssh主机
  • 若干bug修正
  • 若干细节优化

Gitlab-LUG 用户数据共占用14.3GB,备份共耗时26分钟,备份过程中发现某些长期不使用的项目占用了大量空间(最大的项目有4G),导致备份过程异常缓慢。

小插曲:

为提升更新速度,本打算把rubygems源改为mirrors@ustc。但发现mirrors@ustc速度比官方源还慢:

下午,mirrors磁盘整列由于某些原因异常忙碌,然而rubygems没有被SSD缓存,导致发送http请求后等待数分钟才能接到response。看了一下mirrors的日志,怀疑是rsync占用了过多的IO资源(write wait 超过 10000ms),导致nginx抢不到资源。

本次更新使用了淘宝源

Freeshell 1号节点修复 IPv4 SSH

昨天有用户反馈 freeshell 1号节点的 IPv4 SSH(3xxxx 端口)无法访问,经查是 ssh.freeshell.ustc.edu.cn 与 freeshell 1 号节点之间的 GRE tunnel 似乎被删除又重新建立了,tunnel 删除时导致路由表中相关条目消失,auth.log 中未查明原因。目前已经恢复。

学校移动线路网络中断

2014年5月16日凌晨 03:10,学校移动线路突然中断。此故障导致国内移动线路和国外无法访问科大 LUG 各项网络服务,同时 freeshell、blog 和 LUG VPN 无法访问国内移动线路和国外,LUG Public DNS 服务运行缓慢。目前尚未修复。

Update(05:30): 由于学校移动出口仍然没有恢复,{mirrors,blog,freeshell}.ustc.edu.cn 暂停移动线路 IP 解析,切换到电信线路。LUG VPN 和 Public DNS 改用电信线路出国,服务恢复。

Update(12:50):运营商方面已于上午 9 时许修复路由问题,将 LUG 的 DNS 和路由恢复到原来状态。

Freeshell 优化端口映射方案

之前 freeshell 端口映射是在 ssh.freeshell.ustc.edu.cn 上做 DNAT+SNAT,再在 freeshell 节点上做 DNAT。这种方式使得 freeshell 里看到的用户 IP 地址都是 ssh.freeshell.ustc.edu.cn (202.141.160.99)而非用户的实际 IP 地址,对 fail2ban 等应用有不利影响。为了让 freeshell 看到用户的真实 IP 地址,我们在 ssh.freeshell.ustc.edu.cn 与 freeshell 节点之间建立了 IP-in-IP tunnel,并相应修改路由表。事实上这本来应该是一个二层虚拟网络,但无奈服务器上的内核版本太老,不支持 VXLAN,只好建立多个三层虚拟网络并静态配置路由表了。在 ssh.freeshell.ustc.edu.cn 上可以访问 freeshell 私有 IP 地址(10.10.0.0/16)后,只需要在 ssh.freeshell.ustc.edu.cn 上做一层 DNAT 就可以实现端口转发。

这个修改不对用户界面构成任何影响,原有的端口映射仍可正常使用。欲查看 SSH 访问者的真实 IP 地址,您可以查看 /var/log/auth.log。

实现此功能的相关 commit:https://gitlab.lug.ustc.edu.cn/boj/freeshell/commit/3dec33dcda582b0e55a322e9921c65b1f2c40387 (仅供 freeshell 管理员参考)

手工修改数据库时请使用 mysql -U

刚才进 blog 数据库手工修改一条记录,结果 UPDATE 语句忘了打 WHERE 条件,表里的所有行都被 update 了。受影响的列是用户的 Email。

然后查到 mysql -U 可以使用 safe update mode,会拒绝没有 WHERE 条件和不以 key 作为条件的 UPDATE 和 DELETE,避免误操作造成数据丢失。可以在 ~/.bashrc 中设置 alias mysql=’mysql -U’,这样每次使用 mysql 命令都会使用 safe update mode。

恢复过程如下(如果以后出现误操作可以参考):从 /var/backup/db 中找到当天的加密备份(需要 root 权限才能查看),从 /etc/automysqlbackup/automysqlbackup.conf 中找出备份的密码,执行下列命令备份当前数据库,并将凌晨的数据库恢复回去。

mysqldump -u root -p --databases ustcblog >ustcblog-now.sql
openssl enc -aes-256-cbc -d -in /path/to/backup/file.sql.gz.enc -out ustcblog-backup.sql.gz
gunzip ustcblog-backup.sql.gz
mysql -u root -p ustcblog <ustcblog-backup.sql

比较 ustcblog-now.sql 和 ustcblog-backup.sql,找出今天新增的行,把这些行使用 INSERT 语句逐一添加回去。

使用其他办法恢复被 UPDATE 覆盖的列。例如这次覆盖的是用户的邮箱地址,而这个信息在用户 blog 的数据库里也有存储。例如对于 1000 号用户:

use ub_1000;
select option_value from wp_options where option_name='admin_email';

freeshell修复边信道攻击漏洞

漏洞描述

攻击点比较多。这里以activate.php文件的 token参数为例。

首先以任意邮件地址注册freeshell,然后开始猜验证地址的token:

循环第一位:

  • 第01次尝试:a0000000000000000000000000
  • 第26次尝试:z0000000000000000000000000
  • 第27次尝试:A0000000000000000000000000
  • 第52次尝试:Z0000000000000000000000000
  • 第53次尝试:00000000000000000000000000
  • 第62次尝试:90000000000000000000000000
  • 第63次尝试:a0000000000000000000000000

如此循环,记下每次尝试的时间。会发现某个字符的时间比较长,固定这一位,继续猜第二位,直到猜完30位。猜完freeshell也就注册好了。

漏洞原理

请看比较代码

<code style="color: inherit;">if ($info['token'] !== $_GET['token'])
</code>

我没见过底层是怎么实现的,但可以想象,如果第一位正确,就会比较第二位;如果第一位不正确,那么就直接判断为不相等。

虽然这之间的时间差比较小,但统计学告诉我们,所有微小的差距,在大数据量统计中都会被放大。

测试

我获得的地址是:https://freeshell.ustc.edu.cn/activate.php?appid=1450&token=COf4aFFJsEO73f8P1bjkW7zmFbl7vthX5mS5SlE8

第一位是“C”,测试结果见图(矢量图请见 http://z.gaoyifan.com/side-channel-freeshell/

freeshell-side-channel

可以从图中明显看出,编号28的那位(也就是“C”),花费时间比较久。(注:数据已经经过一次清洗,去除了一些噪点)

在服务器负载比较小的时候,猜测一位大约需要3~5分钟,这也就意味着30位的token一个半小时就破解了

修复

修改为:

<code style="color: inherit;">if(hash_hmac("sha256",$info['token'],$appid) !== hash_hmac("sha256",$_GET['token'],$appid))</code>

这样经过一次hash后,比较的串变成随机的,时间差就被随机化了。

感谢boj深夜修复bug:https://gitlab.lug.ustc.edu.cn/boj/freeshell/commit/74ea87e829ffaacc65b5f4fb1099fc2146cbef98

优化 Freeshell 之间 IPv6 互相访问

3月31日 freeshell 重启之后,由于未知原因,IPv6 路由表内没有 2001:da8:d800:71::/64 dev eth0(IPv6 地址写在 /etc/networking/interfaces 里),不在同一物理节点之间的 freeshell IPv6 互相访问都通过了默认网关 2001:da8:d800:71::1。又由于未知原因(有可能是网关对这种 hairpin 的包进行了限速),传输速度只有 2 MB/s 甚至更慢(但通过同样的默认网关访问 mirrors 等 IPv6 都很快)。添加这条路由规则之后,freeshell 不同物理节点之间的 IPv6 互相访问都通过交换机直接进行,不再通过网关,传输速度与 IPv4 一样可达到千兆线速。

感谢 Yifan Gao 的 bug report。