这个问题很奇怪!

我们的虚拟主机使用 LNMP 方案,我的博客使用了域名为 www.heiher.info 的受信证书,在 Firefox 中浏览一切正常。但是在 IE(包含6.0版本)中访问我的博客(https://www.heiher.info)提示证书是受信任的且没有定期,但是域名不匹配,我查看了一下证书,下载的证书竟然是虚拟主机的默认站点的,也就是 vps.heiher.info。

这个问题就很奇怪了,是我的虚拟主机证书配置不对?还是 IE 浏览器的一个 Bug 呢?很郁闷!

Over!

LNMP 虚拟主机内存优化

之前介绍的虚拟主机方案中,PHP-CGI 进程是用户分离的,每个用户有不止一个子进程且每个子进程最大可处理请求次数大于1。这并不是最好的内存资源的共享方法,这里我们以损失一点性能换取较好的内存资源共享。将子进程数调整为 2,子进程最大可处理请求数调整为 1。

修改 PHP-CGI 封装器

#define PHP_FCGI_MAX_REQUESTS	"1"  /* 1024 - > 1 */
#define	PHP_FCGI_CHILDREN		"2"

Over!

映射 Socket 到 Standard IO

演示一下 Unix 系统的 C 程序中如何将 Socket 映射到标准IO。

/* sock2stdio.c
 * Heiher <admin@heiher.info>
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
 
int main(int argc, char * argv[])
{
	int sock, client, addrlen;
	struct sockaddr_in this_addr, peer_addr;
	unsigned short port = 9000;
	pid_t cpid;
 
	addrlen = sizeof(struct sockaddr_in);
	memset(&this_addr, 0, addrlen);
	memset(&peer_addr, 0, addrlen);
 
	this_addr.sin_port = htons(port);
	this_addr.sin_family = AF_INET;
	this_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 
	sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
	bind(sock, (struct sockaddr*)&this_addr, addrlen);
	listen(sock, 5);
 
	while(-1 != (client=accept(sock, (struct sockaddr*)&peer_addr, &addrlen)))
	{
		cpid = fork();
		if(0 > cpid)
		{
			perror("fork() failed!");
 
			return 1;
		}
		else if(0 == cpid)	/* child */
		{
			close(0);
			close(1);
			close(2);
			dup2(client, 0);
			dup2(client, 1);
			dup2(client, 2);
			close(client);
 
			execl("/bin/dd", "/bin/dd", "bs=1", NULL);
 
			return 0;
		}
	}
 
	return 0;
}
gcc -o sock2stdio sock2stdio.c
./sock2stdio
nc localhost 9000
hello  # Input
hello  # Output

Over!

代理之痛

只有这样了

sudo iptables -A OUTPUT -m udp -p udp --dport 53 -j ACCEPT
sudo iptables -A OUTPUT -m tcp -p tcp --dport 80 -j ACCEPT
sudo iptables -A OUTPUT -m tcp -p tcp --dport 443 -j ACCEPT
sudo iptables -A OUTPUT -o lo -m tcp -p tcp --dport 8000 -j ACCEPT
sudo iptables -A OUTPUT -m state --state NEW -m owner --gid-owner fwd -j DROP
sudo iptables -A OUTPUT -m state --state NEW -m owner --gid-owner www-data -j DROP

Over!

LNMP 多用户虚拟主机方案

A. 特点
1. 高效、内存使用少。
2. 权限分离,用户间互不干扰。

B. 应用程序说明
Nginx : 事件驱动的 Web 服务器,采用模块化设计,小巧、高效。
PHP-CGI : PHP的CGI接口版本(本文使用FastCGI高效接口)。

C. 整个架构的简单说明
Nginx 处理所有的 Web 请求,它将 PHP 的请求 Match 出,发送给上游服务器处理,这里的上游服务器就是 PHP-CGI。
PHP-CGI 工作在 FastCGI 模式,它侦听着一个地址端口(或 Unix socket文件,建议组合权限使用 Unix Socket 更安全),Nginx 会连接并发送请求及回收结果并发送给客户浏览器。
Nginx 运行于 www-data 用户环境,这要求 www-data 用户有所有虚拟主机用户的主目录访问权限。每个虚拟主机拥有自己的 PHP-CGI 进程组(PHP-CGI 可工作在多进程模式),运行于自己的用户环境,本方案并没有设计动态的 PHP-CGI 进程管理器用于对资源的负载均衡。
Nginx 使用了 HTTP OwnerMatch 模块,使得它能够控制每个虚拟主机的每个 Location 有哪些用户的文件的访问权限。关于这个模块
Continue reading LNMP 多用户虚拟主机方案

Nginx 模块 HTTP OwnerMatch

我编写了一个 Nginx 模块 HTTP OwnerMatch 解决了 Nginx 虚拟主机间可通过链接型文件(硬链接和符号链接)跨站访问的问题。通过这个模块可以指定每个虚拟主机的每个 Location 可以或不可以访问的哪些用户的文件。

配置文件实例

location / {
          root   html;
          index  index.html index.htm;
          omallow heiher;  # 允许访问隶属于 heiher 的文件 
          omallow guest sftp;  # 允许访问隶属于 guest:sftp 的文件 
          omdeny all;         #  不允许访问其它任何文件
}

源代码仓库

git clone git://github.com/heiher/nginx.git

Over!

新VPS上线,采用LNMP架构的虚拟主机

新VPS上线了,这次没有采用旧VPS上LAMP(PHP-CGI)方案,而是用了更高效的LNMP(Linux + Nginx + MySQL + PHP-CGI)方案实现的虚拟主机。这个方案的好处是静态请求效率高,动态请求内存使用稳定。

虚拟主机用户权限分离
Nginx 由 www-data 用户运行,php-cgi 由客户用户运行,客户的网站主目录只有自己和www-data可以访问,由此实现权限分离。

未解决问题
由于各个用户的主目录 www-data 都可以访问,用户A在自己的主目录创建一个符号链接到用户B的主目录中的文件,在浏览器中访问这个符号链接就可以穿透。

Over!

HevBrowser

HevBrowser 是一个 WebKit 内核的 Mini 浏览器,它可以在页面加载完成后执行用户指定的 js 脚本。

快捷键
Esc : 停止加载当前页面
F5 : 重新加载当前页面
F6 : 切换焦点到地址栏
F7 : 执行用户指定脚本
F8 : 显示结果输出窗口

更新日志
2010-08-16 : 增加字符编码支持
2010-08-15 : 0.0.1

代码仓库
http://git.heiher.info/hevbrowser.git

二进制程序
hevbrowser-0.0.1.tar.gz (用户名: guest 密码: guest*)

Over!

使用 Builder 构造自定义构件

Vala 源代码

/* test.vala
 * Heiher <admin@heiher.info>
 */
 
namespace Hev {
 
public class Window : Gtk.Window {
}
 
static int main(string[] args) {
	Gtk.init(ref args);
 
	try {
		var builder = new Gtk.Builder();
		builder.add_from_file("test.xml");
		builder.connect_signals(null);
 
		Gtk.main();
	} catch(Error e) {
		stderr.printf("Erro: %s\r\n", e.message);
	}
 
	return 0;
}
 
}

test.xml

<?xml version="1.0" encoding="UTF-8" ?>
<interface>
	<object class="HevWindow" id="window_test">
		<property name="visible">TRUE</property>
		<property name="title">Test</property>
		<signal name="delete-event" handler="gtk_main_quit" />
	</object>
</interface>

编译&执行

valac --pkg gtk+-2.0 --pkg gmodule-2.0 test.vala  # gmodule-2.0 必需
./test

Over!