配置安全的共享web服务器(抛砖引玉)

本文所讲的共享web服务器,并非共享文件的服务器,而是多人一起使用的web服务器,各有各自的网站、管理自己的文件,互不干涉,且对系统无影响。鉴于功力较浅,只敢对较信得过的朋友开放这种账号,本文涉及的范围也有限,所以安全漏洞可能还有,请诸位切勿直接用于生产环境。

服务器环境:Ubuntu 8.10, OpenSSH_5.1p1 Debian-3ubuntu1, Apache 2.2.9, PHP 5.2.6-2ubuntu4

登录 – SFTP

传统的 FTP 肯定是不如这个安全,telnet 更不用说了。使用 SFTP 还有一个起始想法是想配置证书自动登录,后来发现 SFTP 客户端(FileZilla)没这功能,就没再作下去,命令行下 scp 的自动登录倒是 和 ssh 的一样很好配置。

网上很多文章介绍把 sftp 用户限制在 $HOME 目录下的方法,使用的是 sshd 的 ChrootGroups 选项,这个选项在我的版本里没有找到,找到另外一篇参考文章使用的是 ChrootDirectory,也很好用。

创建一个用户组,作为所有 sftp 用户的用户组:

$ sudo groupadd sftp

创建用户,设置密码,并归入 sftp 组:

$ sudo useradd -m friend
$ sudo passwd friend
$ sudo usermod -g sftp friend

为了进一步增强安全性,还可以将用户的登录 shell 设置为 /bin/false,是个好习惯,但在本例中并非必须,下面的 sshd 设置也会让用户无法登录 shell (我观察的结果)。

$ sudo usermod -s /bin/false friend

下来就要配置 sshd 了,编辑配置文件 /etc/ssh/sshd_config

# 修改下面这句
#Subsystem sftp /usr/lib/openssh/sftp-server
Subsystem sftp internal-sftp

然后在此配置文件末尾添加:

Match group sftp
    X11Forwarding no
    ChrootDirectory %h
    AllowTcpForwarding no
    ForceCommand internal-sftp

配置含义大概为:凡是 sftp 组的用户,关闭 X 转发,chroot 到 $HOME 目录,关闭 TCP 转发(无法使用隧道了?),强制使用 internal-sftp(这个不明白)。

现在,重启 ssh 服务,用户就只能通过 sftp 访问 /home/friend 下的文件了。

PS: 我发现 sshd 如果配置错误,在 restart 服务的时候会先检查,而不是直接 stop 服务然后在 start 的时候出现错误,搞得服务启不来。大概是考虑到很多人都是远程 ssh 上来进行维护,服务 down 了以后就麻烦了,很贴心的设置。

Apache & PHP

Apache 配置简单,创建 /home/friend/www 目录,约定网站文件都放在这个目录下,然后弄个 Alias 指向就可以了。

但有一个极大的安全隐患需要堵上,用户可以通过编写 PHP 程序,读取系统中任何 www-data 用户有权限访问的文件,包括系统的 shadow 文件,包括 其它用户的网站文件等等。解决这个问题,一种是开启 PHP 的 safe_mode ,安全模式下 PHP 将只能访问 owner 为自己(也就是 www-data)的文件;另外一种是使用 open_basedir,这将限制 PHP 只能打开某一目录树下的文件,并且不可能通过符号链接避开此限制。显然 safe_mode 的副作用太多,后一种方法更适合我的这种情况,配置写到 Apache 的 conf 里就行了:

<Directory /home/friend>
    php_admin_value open_basedir "/home/friend/"
</Directory>

注意open_basedir 后面的参数只代表文件路径的前缀,所以要带上末尾的斜杠,明确指出是目录。

不使用 safe_mode 的另外一个原因是在未来的 PHP6 里就要删掉它了。

缺点

最大的缺点就是 sftp 用户无法自己更改密码,除非自己写个守护程序啥的。这个程序在写的时候要非常小心,因为操作的是系统用户文件,如果遗留有安全漏洞可能会使别人获得其它用户权限。一个折中的方法是写个程序,定期更改密码并通过邮件告知用户,虽不方便但安全性要好一些。

Ubuntu升级到9.04 Jaunty的变化和遇到的问题

  • 长按键盘自动连续击键的间隔缩短了。
  • 显卡驱动没有问题,终于能够摆脱8.10里像涂了墨水一样的中文字乱码了。
  • Firefox的速度好像也快了不少,或许也是显卡驱动的原因?
  • Fluxbox apps文件中Position设置LOWERLEFT/BOTTOMLEFT原来时从屏幕最下方算间距,现在时从工具栏上方开始算,所以原来的值要减去工具栏的高度(25)。
  • 消失很久的启动时的Splash屏又回来了,不过是Xubuntu的小老鼠(我用的WM是Fluxbox),想取消的话,删掉usplash及其相关的包即可。

如果在没有正式发布的时候就升级了,每天的更新比正式发布后要多得多,每天都要下载一大堆包升级,得考虑好,当然你也可以忍着不频繁升级。

Fluxbox任务栏上当前聚焦的窗口和其他窗口的风格是一样的,区分不开了,更换任何styles都无效。

Firefox窗口的标题栏里中文字显示为方块

先这个是Gnome的问题,所有窗口标题栏中包含中文时都是方块,而Fluxbox工具栏上是能够正确显示中文的。尝试更换不同的fluxbox styles发现menu.title.font设置为dejavu字体时窗口标题栏就能正常显示中文了,其他的窗口内容、网页中文全部显示正常。

终于让我找到原因了,又是一个哭笑不得的问题,在我自定义风格里,使用了dejavusans这个字体,而这个字体现在好像在系统中找不到了,因此它就像出错后就不再往下执行了一样,导致后面overlay里定义的新字体也不生效,窗口栏上的中文就成方块了。换其他style之所以能正常显示窗口标题栏上的中文,是因为他们没用dejavusans这个字体。最后的解决方案,把这个自定义style里的dejavusans替换成dejavu -_-!

字体大小dpi优化

字体DPI设置会根据显示器进行优化,而不再局限于默认的96DPI,还可以在System → Preferences → Appearance → Fonts → Details里自行定义。原来是在.Xresources里设置的Xft.dpi:96,不知道还有用没。目前发现的问题是窗口标题栏中的文字比以前大了一些。

我的Fluxbox还遇到了一个问题,屏幕尺寸、位置计算出现了错误,原先我是/etc/gdm/Init/Default中用xrandr -s 1024x768强制重设分辨率,现在把这行禁用后发现桌面的“尺寸”比1024大,鼠标移动到屏幕边缘后会自动移动,但显示不全。

	$ xdpyinfo |grep resolution
	  resolution:    78x78 dots per inch

78是显示器真正的dpi数,但按这个设置又显得字太小了。最后,把xorg.conf里大于1024的分辨率都删掉,这样就可以去掉上面xrandr那句了,显示也正常了,dpi仍然用的是96。

上某些网站中文字模糊(像粗体字那样的模糊)

打开/etc/fonts/conf.d/44-wqy-zenhei.conf,找到下面这行:

	<edit name="antialias" mode="assign"><bool>true</bool></edit>

把true改成false后重启X即可。

Ctrl+Alt+Backspace关闭X的组合键被禁用了

编辑/etc/X11/xorg.conf,在最后加上:

	Section "ServerFlags"
		Option "DontZap" "no"
	EndSection

[Git]真正回滚已上传的更新

首先,抛弃本地的修改应当用:

$ git reset --hard HEAD

使用 git 自身功能来回滚代码,取消上一次的修改应该用:

$ git revert sha1_of_commit

但要注意,虽然代码是实现了回滚,同时也会自动产生一条“回滚代码”的 log 。

有些时候,由于工作人员粗心,错误提交的内容完全无意义且占用空间颇大,就想真正 undo 掉错误的 commit,连历史记录都不想留。以下是我尝试的做法:

准备一份干净的客户端仓库

在客户端,下载服务器上的每个分支,并更新到最新状态。git branch -a查服务器上有哪些分支,挨个 checkout 过去再 pull。

然后回到有错误 commit 的分支,reset 掉错误的 commit:

$ git reset --hard 671475b1ce

这样在客户端就形成了一个已剔除掉错误 commit 的完整状态了。

从客户端仓库生成服务端仓库

这就是 git 分布式源代码管理的优势,客户端也是完整仓库,只是表现形式与服务端的不同罢了,两者之间可以转换。在本地仓库( repo.client )的上级目录中执行:

$ git clone --bare repo.client repo.git

然后把现在服务端仓库中的 hooks, info 目录和 config, description 两个文件拷贝到新生成的服务端仓库当中。

然后备份旧的服务端仓库,删掉用新生成服务端仓库替代,并调整相关文件权限。

基本上就可以了,小结

在客户端 pull:

$ git pull
From ssh://domain.tld/repo
 + 368b15f...671475b master     -> origin/master  (forced update)
Already up-to-date.

客户端仓库的 HEAD 自动被重置到了错误 commit 之前的。

我这种做法,只适用于用户比较小,可以停掉服务慢慢弄的情况,并且会丢失所有错误 commit 之后的改动,所以要慎用。最好的方法还是搞好用户培训,避免产生离谱的错误提交,一些小的错误还是直接用revert好了。

Mysql升级到5.1后库升级失败的问题

一台 mysql 5.0 服务器,升级到 5.1 后,发现原来有个 database 名字变成了 #mysql50#t-2008-zbb ,刚开始没在意想直接 RENAME DATABASE ,结果这个语法由于过渡危险已经取消了,改用ALTER DATABASE db_name UPGRADE DATA DIRECTORY NAME,结果执行错误:

mysql> ALTER DATABASE `#mysql50#db_name` UPGRADE DATA DIRECTORY NAME;
ERROR 1450 (HY000): Changing schema from '#mysql50#db_name' to 'db_name' is not allowed.

原来这里面还有个 BUG ,刚刚修正过来,发行版中肯定还没有呢。幸好,从中得到了提示,因为 View 的存在导致库无法升级的,删掉所有视图后 UPGRADE 成功:

mysql> ALTER DATABASE `#mysql50#db_name` UPGRADE DATA DIRECTORY NAME;
Query OK, 0 rows affected (0.08 sec)

这台服务器还作了双向同步,我还得手工重置同步状态,又是麻烦一连串儿的事情,幸亏这次操作的是测试服务器,下次升级正式服务器之前,记得先把所有 View 删掉,升级完成后再重新创建。

另外 RENAME DATABASE 实在是危险,我执行过程中出错终止了,结果一部分表在新库里、一部分表在旧库中,不小心把未转完的目标库删掉了(不然后面的正常 ALTER DATABASE 无法继续),结果就丢失了这些表的数据。

小巧的编辑器Geany

我对PHP编辑器的要求不高,不过很多软件却不合我意:

首先,最好是免费的,所以挺好用的Zend Studio落选了。并且我发现似乎Zend Studio与ibus或者ubuntu jaunty有点冲突,有时候中文就出不来了。

其次,小巧,java的最好不要,所以Eclipse也落选了。

最后,最小功能集:语法高亮(废话)以及类函数列表(可以帮助我快速定位)。就这两项功能,淘汰了gedit(支持得不好)、vim(无法列出类函数)等工具。

当然,隐含要求,支持中文输入法,所以gphpedit也不能用,正好也不喜欢它的颜色高亮模式(色彩搭配)。

所以,选来选去,今天才算碰到一个比较满意的────Geany,ubuntu源中有,目前版本0.14,看来开发历史并不长。使用gtk2,操作流畅。除了类函数列表以外,还有一些贴心的小功能,比如自动补齐文件末尾的空行、保存时自动删除行尾空格等。

geany screenshot

默认的,Geany的快捷键Complete Word与中文输入法冲突,还好,可以通过设置删掉:双击-->Del-->回车

geany-solve-conflict-with-input-method

可怜的gphpedit也是这种快捷键冲突,不过我没找到设置方法。

其实Geany不仅能够处理PHP文件,还能够编辑多种脚本文件,新建文件时有内置模板可选,能够自动生成文件头的copyright及GPL声明。

总之,到目前的感觉,挺好用的。

Update @ 2009-12-29

ubuntu 中 geany 的版本更新较慢,喜欢追新的可以自行添加它的 PPA源

控制ThinkPad的风扇

我的旧本子X31风扇很久都不转了,今天查了点资料,搞了搞,感觉状态上好,记录如下:

首先要能了解当前温度和风扇转速,安装lm-sensors包就能用了:

$ sensors
acpitz-virtual-0
Adapter: Virtual device
temp1:       +42.0°C  (crit = +91.0°C)                  

thinkpad-isa-0000
Adapter: ISA adapter
fan1:       3614 RPM
temp1:       +42.0°C                                    
temp2:       +48.0°C                                    
temp3:       +48.0°C                                    
temp4:       +42.0°C                                    
temp5:       +32.0°C                                    
ERROR: Can't get value of subfeature temp6_input: Can't read
temp6:        +0.0°C                                    
temp7:       +29.0°C                                    
ERROR: Can't get value of subfeature temp8_input: Can't read
temp8:        +0.0°C      

按说lm-sensors也能控制风扇的,但对我的本子不适用。

然后就是在内核中挂载ThinkPad的高级电源控制模块,在/etc/modprobe.d/options里添加这么一句,后面的参数fan_control很重要:

options thinkpad_acpi fan_control=1

重启,或者手工重新挂载模块:

rmmod thinkpad_acpi
modprobe thinkpad_acpi

好了,控制风扇转速,最基本的方法是(如无意外,本文中的大部分命令都要用root用户操作):

echo level [level] > /proc/acpi/ibm/fan

[level]的取值可以是0-7之间的数字,或者auto,或者disengaged/full-speed,在不同的本子上会有不同的效果,总体来说数字越大转速越高,auto是系统自动判断,但在我的本子上却是不转,disengaged/full-speed是全速。我自己用的本子,设为4左右就能达到温度和噪音的平衡点。

还有人在这个基础上写了一些小工具,比如tp-fancontrol或者ThinkPad Fan Control,主要功能都是定时探测温度,然后安装预先的设定来控制风扇的转速。但由于不同硬件的差异,他们并不适用于所有情况,比如我的X31装上哪一个风扇都是auto不转,只好手工echo然后让风扇常转了。智能一点的,还可以借助cron来个定时开关、定时检测什么的。

参考