delphij's Chaos
选择chaos这个词是因为~~实在很难找到一个更合适的词来形容这儿了……
我的 blog 在之前一直是用 Apache HTTPD 来作为 CGI 引擎来跑 Movable Type。在迁移到北美之前,使用两个域名/IP来分别运行静态页面 (lighttpd) 和 CGI 内容。后来因为全面转向 nginx,在迁移过程中改成了使用一个域名、使用 nginx 在前面跑静态页,并作为反向代理来连接后端的 Apache 1.3.x 实例(先前是 Apache 2.2.x,由于这个实例只用来跑 CGI,所以采用了较低版本的 Apache 以节省资源)。
之前一直有完全停用 Apache HTTPD 的想法,不过一直没有时间去实作。由于 Apache 1.3.x 早已是不再维护的分支,近期发现的 CVE-2011-3368 虽然和我的用法完全没有关系,但 FreeBSD 的 port 已经被标记为 “FORBIDDEN” 状态,于是最近找时间学习了一下如何在 nginx 上跑这些 cgi。现在,这台服务器已经完全停用 Apache HTTPD 了。
nginx 有很多跑 CGI 的办法,但是没有一种是原生的。官方推荐的方法是使用 thttpd,然而大概看了看代码之后发现 thttpd 虽然代码量很少,但不支持使用 Unix socket 连接,而且我只要能支持 FastCGI 接口就好,并不真的需要封好 HTTP,所以改为用 fcgiwrap (ports/www/fcgiwrap)。
fcgiwrap 没有什么特别的配置。 FreeBSD 上提供了一个 rc.d 脚本,直接拿来用。
对应的 rc.conf.local 配置如下:
fcgiwrap_enable="YES"
fcgiwrap_socket="unix:/var/run/www/fcgiwrap.sock"
fcgiwrap_user="www"
fcgiwrap_group="www"
这里没有使用通常的 /var/run/fcgiwrap.sock, 而是单独建立了一级目录 (/var/run/www/),其 owner 用户和组均为 www。上述配置令 rc.d 系统在启动时使用 www 身份,如果不单独建立目录,则系统会无法创建对应的 socket 文件。
Read more...来自新浪微博:王小山:同意的请转,郑重承诺:拒绝蒙牛一切产品,从我做起,从现在做起:不为蒙牛打工,不使用蒙牛产品,告诉别人蒙牛是垃圾企业,不接蒙牛订单,不接蒙牛广告,不买有蒙牛广告的报纸,不看蒙牛赞助的节目,微博辩论再不说对方"脑残",只说"喝蒙牛长大的吧"。
我们也许没有办法改变监管体制,但我们至少可以拒绝那些有问题的企业。监管部门不让它们倒闭,消费者就必须让它们倒闭!请爱惜自己的家人和生命。
Read more...记一笔,没啥特别。
Unix 系统的分时调度中,nice值(通过 nice(1) 来控制)是管理员告诉调度器的一个参数,这个参数令内核在考虑就绪进程优先级时,根据其值适当增大或减少执行绪的动态优先级。很明显,如果采用抢占式调度,如果执行绪等待时间较长,或者由于某种原因获得了优先级奖励(例如I/O导致的等待),即使有更重要的任务需要执行,内核还是会将这个执行绪调度进来并进行执行;反之,如果执行绪使用的 CPU 很多(计算密集型任务,如压缩等等),即使这个执行绪进行的就是最重要的任务,它仍然可能被置入休眠状态。
在 FreeBSD 的及时调度 (real-time scheduling) 实现中,增加了32个’及时(realtime)‘优先级和32个’闲时调度(idle)‘优先级。用户态进程可以通过 rtprio(2) 或 rtprio_thread(2) 系统调用来为进程或线程指定这些优先级。这些优先级不会动态调整,故在抢占式调度中可以一直获得时间片,或只有在系统闲时才获得时间片。
为了方便管理员使用,FreeBSD提供了与之对应的 rtprio(1) 和 idprio(1) 命令。出于显然的安全考虑,rtprio 和减少 nice 值一样需要超级用户权限,而 idprio 默认也需要超级用户权限(目前,持锁执行绪可能由于优先级太低而被抢断,从而导致死锁),但可以通过将 security.bsd.unprivileged_idprio 设为 1 来允许非特权用户使用它。
典型的用法包括:
将 Xorg 设为 realtime 优先级,以改善其响应。这个命令必须以 root 身份运行。
副作用:如果 Xorg 陷入死循环,将没有机会令其终止。
# rtprio 31 -
pgrep Xorg``
以 idle 优先级运行 make buildworld。需要以 root 身份运行或将 sysctl security.bsd.unprivileged_idprio 设为 1。
# idprio 31 make buildworld
对于单机运行的网站来说,也可以用类似的方法来做后台的日志分析处理。
Read more...大家都喜欢阴谋论,所以今天说个关于密码的。话说,假如你拿到了一个大网站的密码数据库,然后恰好这个数据库里面的密码又都是明文,但这个数据有点旧了,怎么样才能立即得到其中的活跃用户的密码呢?
假设你可以控制若干家网络公司所使用的网络基础设施,例如事先装了一些监控非法活动的防火墙的话,方法就很简单了:公布这个数据库,或者公布至少其中的一部分,然后做简单的监听就可以了。这个过程不但可以知道他们的新密码是什么,而且可以知道旧密码是否是对的,还可以知道他们的安全习惯如何,要知道,假如没有这个事件,绝大多数人可能正靠着他们的简单密码睡大觉。
当然,攻击者显然还会注意到,这些网站,以及很多其他网站,多数还都没有对用户做最起码的保护,即使用 https 来保护登录过程。
Read more...配合 svn 使用 git 时可能会遇到许多问题,例如两个人分别做了 git svn clone,然后希望合并到同一个 git 库中。如何做合并呢?假定两个库分别是 old, new,将这两个库对应的分支和对应的源分支 (svn/*) 通过 .git/config 分别复制到不同的分支名字下面,例如 svn/releng/8.2 和 old-svn/releng/8.2。假定需要合并的分支是 old/my8.2,则做以下操作:
git checkout my8.2 # 签出一份工作副本
git rebase old-svn/releng/8.2 # 更新到原始上游的最新状态
git rebase --onto svn/releng/8.2 old-svn/releng/8.2 # 用新库作为基础重新rebase;
git push origin my8.2 # 将结果push回代码库
把本地新建分支推到上游:
git push origin 分支名
问题:首次push时会收到 git post-receive remote: fatal: Invalid revision range 0000000000000000000000000000000000000000..XXXXXXXXXXX,不过并不影响push本身。这个错误是由于post-receive的hook里面引用了git提供的旧版本,但查了一下似乎没有很好的办法知道这个分支的上游是谁?(基本逻辑应该大致是:和库中已经存在的所有其他分支进行比较并找到公共版本与本分支HEAD距离最近的一个,返回那个版本号)。
删除远程代码库中的分支:
git push origin :分支名
DNSSEC 是一种验证域名信息真实性的协议扩展。个人之前一直觉得这事相当的蛋疼:DNS协议本身是不安全的,而DNSSEC无非是增加了数字签名验证,而这验证还依赖于相当复杂的密钥更新和分发机制,这一切都给它的推广带来了困难。
最近闲着没事把几台自己的机器上的 unbound 配置了 DNSSEC 支持。 unbound 是一款 NLnet Labs 开发和维护的 DNS 缓存(解析)服务器实现,它内建了对 DNSSEC 的验证支持。与比较常用的 BIND 相比, unbound 性能更好,并且在设计时充分考虑了安全问题,在安全方面有很好的记录。
在 FreeBSD 上可以使用 port 来安装 unbound:
# cd /usr/ports/dns/unbound
# make install
在 /etc/rc.conf.local 中启用 unbound:
# echo 'unbound_enable="YES"' >> /etc/rc.conf.local
配置 unbound,如果需要对外提供服务,在 /usr/local/etc/unbound/unbound.conf 中的 server 小节添加:
interface: 0.0.0.0 # 监听所有IPv4地址
interface: ::0 # 监听所有IPv6地址
(20130423增加) 注意! 如果监听所有 IPv4 和 IPv6 地址的话,就必须限制允许访问的 IP 地址范围。假设一台主要针对内网,同时为 202.96.0/24 服务的服务器,应配置如下 ACL:
Read more...近日, netmap 已经正式进入 -CURRENT (r227614)。目前联编带 netmap 的内核需要做下面的操作:
% cd /usr/src/sys
% patch -p0 < dev/netmap/head.diff
然后在内核联编配置中加入:
device netmap
目前,这个补丁包括了对 Intel 万兆以太网适配器 ixgbe(4)、千兆以太网适配器 igb(4) 和 em(4) 以及 Realtek 千兆以太网适配器 re(4) 的支持。由于 netmap 的设计,对其他驱动的修改也相当简单,只需增加大约 15 行左右的代码即可。
在 src/tools/tools/netmap 中提供了一系列用户态的例子,包括一个简化的 libpcap 实现(不完整,但足以支持 tcpdump 运行)、一个用户态网桥、用户态高速收发包测试,以及使用 Click 的例子(需要 netmap 的那个 libpcap 实现)。
Read more...静态分析是一种辅助的代码品质提高手段,简而言之静态分析大致是走一个类似编译的过程(或者走完生成副产品),然后给出代码中可能存在的问题。
FreeBSD Ports Collection 中提供的 lang/clang-devel 内建了 clang 项目的静态分析工具。这个工具的用法非常简单,假设我们原先的联编命令是:
make -j 4 all
那么,配合clang静态分析工具来产生输出的命令便是:
scan-build _-o /tmp/scan_ make -j 4 all
以上, /tmp/scan 是输出目录。scan-build可以设置必要的环境变量来让make使用ccc-analyzer代替${CC}来完成编译工作,并把输出保存到指定目录的一个唯一的子目录中。下面,我们可以运行:
scan-view --allow-all-hosts --no-browser --host 10.4.2.253 /tmp/scan/2011-11-17-1
上面的/tmp/scan/2011-11-17-1是scan-build在最后告诉我们的那个结果,而 10.4.2.253 是本机 IP 地址。用浏览器访问 http://10.4.2.253:8181 就可以看到编译过程中发现的问题了。
需要说明的是,静态分析工具并不总是能给出理想的输出结果。开发者需要充分分析程序逻辑之后再做修改。
Read more...现时 InfiniBand 硬件已经相当廉价。有些 4x 的 InfiniBand 适配器已经可以在 $100 以内拿到。由于 InfiniBand 可以提供高带宽、低延迟的数据传输,而且点对点对联不需要交换机,因此可以以十分低廉的价格满足近实时同步,或分离存储服务这样的需求。
今年年初的时候, Isilon、我厂 和 Panasys 联合资助了 FreeBSD 上的 InfiniBand 协议栈的实现。这个实现基于 OpenFabrics Alliance 的 OFED,对基于 Mellanox 芯片的多种常见的 InfiniBand 卡都提供了支持。这些代码目前已合并至 FreeBSD 的开发主线,并将随 FreeBSD 9.0-RELEASE 发布。
目前关于 InfiniBand 在 FreeBSD 上的使用介绍还很少,并且目前 InfiniBand 相关的支持还没有默认启用,所以在这里进行一些简要的介绍:
首先是编译系统。在 /etc/make.conf 中添加:
WITH_OFED=
相关的内核配置包括:
options OFED # OFED协议栈,必需
options OFED_DEBUG_INIT # 调试
options SDP # SDP (Sockets Direct Protocol) 协议
options SDP_DEBUG # SDP 调试
options IPOIB # IP-over-InfiniBand
options IPOIB_CM # IP-over-InfiniBand Connection Mode
options IPOIB_DEBUG # IPoIB-CM 调试
以及下列设备:
device mlx4ib # ConnectX InfiniBand支持
device mlxen # ConnectX 万兆以太网支持
device mthca # Mellanox Technologies HCA (只有IB)
如果使用的是 ConnectX 卡,则可以用 mlxen 令其呈现为一个网卡。
重新联编、安装内核和world之后,系统会提供下列函数库来供开发使用:
比较常用的是 RDMA CM 和 SDP 的库。其中,通过使用 LD_PRELOAD,libsdp可以让(二进制的) TCP 程序不加修改的直接走 SDP 协议;RDMA 适合对性能要求更高的场合。需要说明的是,如果用 SDP 协议的话,还需要运行子网管理服务 opensm。
总体上使用和 Linux 上的 OFED 差不多,现成的程序不需要做太多修改即可直接运行。
Read more...多年以前在 USENIX HotOS 2003 论文集中看到了 一篇 关于 “Crash only software” 的论文,当时有一些想法,但很多没有认真地实践。最近做的东西用到的这方面的设计方法比较多,总结一下写出来。
在设计系统时,很多时候我们会花很多的精力去考虑正常终止的情况,对于希望尽可能少出现 downtime 的系统来说,这很可能是不必要的。
简而言之,程序有两种终止方式,正常的和不正常的;正常的终止是可预期的,非正常终止不一定发生在什么时候—-因为程序设计缺陷、系统管理员杀掉、硬件故障如电源出现问题,等等。
很多时候处理正常终止所需的代价要比非正常终止来的更大。例如在正常的 OS 关机过程中,内核需要通知所有的服务进程退出(这一点目前还是必要的,因为不是所有的程序都设计了从非正常停机中恢复)、通知其他节点自己下线了、甚至某些早期的 Windows 版本还会关闭网络连接,等等,最后还要把内存中到目前为止还没写盘的数据刷到磁盘上,然后再停止运行。而恢复时,则往往会有一些技术来帮助系统尽快地恢复到可以运行的状态,许多时候,在崩溃后引导系统和在正常关闭后引导系统所需的时间是非常接近的。
这造成了一个很奇怪的现象:一方面,我们花费了相当多的时间精力去撰写正常关机所需的代码,而另一方面,这些代码的存在往往会导致系统恢复的时间变得更慢一些。(这篇论文写于 2003 年,其中举了一些实际的例子,如 RedHat 8 正常重启所需要的时间是104秒,而非正常重启则只需75秒;JBoss 3.0分别是47秒对39秒,Windows XP则是61秒对48秒)。还有就是,增加的这些复杂的代码对于长时间运行的系统实际上是很少用到的,系统能够正常地持续运行的时间越长,这些代码就越少会执行到。
因此,对于很多互联网应用来说,既然有那么多的可能性导致异常终止没办法避免,更好的设计就应该是程序中只去考虑非正常终止的问题。程序把启动过程也作为异常终止之后的恢复来实现,而正常终止直接让程序崩溃就可以了,主要的精力应该放到"让恢复过程变得更快和更可靠"以及"让程序运行的过程中任何一个点发生的异常终止都不会导致恢复出现问题"两件事上。
如何实现
原文中列举了一些实现策略,下面是我自己的一些总结:
做到这些以后直接将正常停止改为 kill -9 即可。当然,尽量避免程序 bug 导致的崩溃仍然是非常重要的。
Read more...