Smokeping使用Nginx显示

由于Nginx必须调用第三方FastCGI来处理fcgi程序. 所以在此需要安装spawn-fcgi

spawn-fcgi安装完毕后在/etc/systemd/system/smokeping-fcgi.service创建服务文件

[Unit]
Description=SmokePing FastCGI Service
After=network.target smokeping.service
Wants=smokeping.service

[Service]
StandardOutput=null
StandardError=syslog
ExecStart=/opt/spawn-fcgi/bin/spawn-fcgi -u smokeping -s /var/run/smokeping-fcgi.sock -M 600 -n -U www-data -- /opt/smokeping-www/smokeping.cgi
Restart=always

[Install]
WantedBy=multi-user.target

Nginx调用spawn-fcgi

server {
    listen 10000;

    root /opt/smokeping-www;
    index smokeping.cgi;

    location ~ \.cgi$ {
        include /opt/nginx/conf/fastcgi_params;
        fastcgi_pass unix:/var/run/smokeping-fcgi.sock;
    }
}

ESXi直通NVIDIA GeForce显卡

目前家里的ESXi服务器集成了路由器,写代码,囤东西,下东西,测试之类的作用. 就差个玩游戏就完美了. 所以我就看上了Intel Direct I/O技术. 可以将PCIE上的设备直接映射至虚拟机内从而降低性能的损耗.

不过在ESXi兼容列表内的NVIDIA显卡只有Quadro系列卡. 所以就尝试了在外网寻找如何使用Direct I/O将GeForce系列显卡映射至虚拟机内.

如果在国内搜索相关N卡在ESXi内的相关信息, 只会说明到需要在虚拟机配置文件中添加一行:

hypervisor.cpuid.v0 = FALSE

经过尝试后证实只做这一步是不行的, 在Windows系统能可以看到的确显卡驱动已经正常加载了没有43错误, 但是等一有负载显卡驱动就会立即退出.

由于这个问题我在网上寻找了几天, 最终在一个Reddit一个有关于AMD+NVIDIA平台直通教程内发现了关键的一步:

在ESXi主机/etc/vmware/passthru.map文件内根据PCIE ID添加以下行:

10de 1c02 d3d0 false

其中10de是PCIE设备的供应商ID. 1c02是PCIE设备的设备ID.

当添加完毕之后重启ESXi主机后直通后的显卡就应该可以工作了.

如果重启后任然出现43错误请将ESXi主机彻底关机断电后重试.

已测试平台:
CPU: E3-1230v2
主板: MS-S0121
显卡: GT440 与 GTX1060
驱动版本: 388.71
操作系统: Windows 10
ESXi版本: 6.5.0 Update 1 (Build 7388607)

snapshot.png

参考资料:

Nginx模块开发中使用PCRE正则表达式匹配

Nginx内部对pcre库的常用操作进行了封装. 封装的源码位于nginx/src/core/ngx_regex.c, 同时将pcre内使用的内存池更变为了Nginx的内存池.

  • pcre_compile:

    Nginx封装了pcre_compile方法. 方法名为ngx_regex_compile.

    ngx_regex_compile方法的参数需要传入一个ngx_regex_compile_t来进行编译正则等操作.

    ngx_regex_compile_t结构如下:

    typedef struct {
        ngx_str_t     pattern; // 正则
        ngx_pool_t    *pool;   // 每个request分配的内存池
        ngx_int_t     options; // pcre options
    
        ngx_regex_t   *regex;  // 编译完毕后的pcre实例
        int           captures;
        int           named_captures;
        int           name_size;
        u_char       *names;
        ngx_str_t     err;     // 错误信息
    } ngx_regex_compile_t;`
    

    当编译正则成功时会返回NGX_OK并且会在内部调用pcre_study来进一步提高正则匹配性能. 失败时会返回NGX_ERROR. 同时失败的错误信息会保存在err成员变量中.

  • pcre_exec

    Nginx同样封装了pcre_exec封装后的方法名为:ngx_regex_exec 源码位于nginx/src/core/ngx_regex.h文件中:

    #define ngx_regex_exec(re, s, captures, size)                                \
        pcre_exec(re->code, re->extra, (const char *) (s)->data, (s)->len, 0, 0, \
                  captures, size)
    

    可以看到参数re要求的是ngx_regex_compile_t中的regex成员变量. 而被搜索的字符串被替换成了Nginx内部的字符串类型ngx_str_t. 同时也将Nginx内部不常用的搜索偏移以及选项设置为成0. 如果仍需要使用偏移以及选项的话可以直接使用pcre_exec来跳过Nginx的封装.

Refs:

SS通讯步骤

目前在写一个Socks5转SS的工具(某些奇怪需求需要), 在互联网上翻了几个网站并没有详细说明SS通讯步骤. 所以在这记录下.

密码初始化

本文章aes-256-cfb加密来说明, 当使用AES时需要提供KeyIV(初始化向量). 其中IV类似于给hash加盐, 不过这个盐是基于上一个数据块加密后的出来的盐. 那为啥还需要提供IV呢?因为第一次用的时候没上一块数据只能自己指定一个.

SS生成KeyIV是基于用户指定的密码来生成的, 以下为TypeScript基于密码生成KeyIV代码:

function generateKeyIVByPassword(passwordString: string, keyLength: number, ivLength: number): any {
    var password = new Buffer(passwordString, "binary");
    var hashBuffers: Array<Buffer> = [];
    for (var dataCount = 0, loopCount = 0; dataCount < keyLength + ivLength; loopCount++) {
        var data: any = password;
        if (loopCount > 0) {
            data = Buffer.concat([hashBuffers[loopCount - 1], password]);
        }
        var md5 = crypto.createHash("md5");
        var md5Buffer = md5.update(data).digest();
        hashBuffers.push(md5Buffer);
        dataCount += md5Buffer.length;
    }
    var hashBuffer: Buffer = Buffer.concat(hashBuffers);
    return {
        key: hashBuffer.slice(0, keyLength),
        iv: hashBuffer.slice(keyLength, keyLength + ivLength)
    };
}

从上述代码中可以得知SS作者先将用户指定的密码迭代MD5多次; 其结束条件是迭代多次的MD5结果长度相加, 如果长度大于加密算法所需Key长度 + IV长度则退出循环. 最后将MD5 Hash结果截断来获得KeyIV.

通讯过程(TCP过程)

当客户端连接至服务端后会直接发送 客户端IV + 已被加密的Socks5头 + TCP数据.

IV交换

Client连接后, Client发送的第一个数据前N个字节(取决于加密算法)是AESIV用于来解密来自Client的数据.
然后Server发送Client的第一个数据也是类似. 其结构图如下:
ShadowsocksIVED.png

注意!SS中这样交换IV是不安全做法.

通讯

SS省略了Socks5协商过程直接进入通讯过程. Socks5通讯需要客户端提供 目标IP或域名 + 端口. 当客户端提供完成之后服务端会连接至目标IP并返回是否连接成功; 如果连接成功就能够直接通讯了.

格式如下:
027071B4-415D-4ACE-BF39-EFABB2F7B64A.png

上述说过SS省略了协商过程, 所以这里Socks5请求也是缩水的. 其把前3个字节砍掉了, 也就是说Client只会发来ATYP, DST ADDR, DST PROT. 当连接至DST ADDRDST ADDR发送的数据加密发送给Client, Client发送的数据解密发送给DST ADDR. 这样SS就完成了通讯.

参考资料

CentOS7 LXC网络以及配置

安装EPEL源.

yum install epel-release.noarch -y

禁用Firewalld使用iptable代替.

systemctl stop firewalld
systemctl disable firewalld
yum install iptables iptables-services net-tools -y

创建iptables默认规则.

echo "# sample configuration for iptables service
# you can edit this manually or use system-config-firewall
# please do not ask us to add additional ports/services to this default configuration
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT" > /etc/sysconfig/iptables;
systemctl enable iptables.service
systemctl start iptables.service

安装Linux Container.

yum install lxc lxc-templates -y

为容器设置虚拟交换机.

echo 'DEVICE="virbr0"
ONBOOT="yes"
TYPE="Bridge"
BOOTPROTO=static
IPADDR=10.0.0.1
NETMASK=255.255.255.0' > /etc/sysconfig/network-scripts/ifcfg-lxcbr0;

启用内核转发以及虚拟内存调整.

echo "net.ipv4.ip_forward = 1
vm.swappiness = 10
net.ipv4.tcp_timestamps = 0" >> /etc/sysctl.conf;
sysctl -p

设置iptables nat.

iptables --flush POSTROUTING --table nat
iptables --flush FORWARD
iptables -t nat -A POSTROUTING -o 网卡 -j MASQUERADE
iptables-save > /etc/sysconfig/iptables

应用相关服务.

systemctl start lxc
systemctl enable lxc
systemctl restart network

Q&A

  • Q: 容器启动后一直持续占用CPU.

  • A: 编辑/var/lib/lxc/{container}/config加入:

    lxc.autodev = 1
    lxc.kmsg = 0
    
  • Q: 容器设置静态IP.

  • A:编辑/var/lib/lxc/{container}/config加入:

  • lxc.network.type = veth
    lxc.network.link = virbr0
    lxc.network.flags = up
    lxc.network.name = eth0
    lxc.network.ipv4 = 10.0.0.2/24
    lxc.network.ipv4.gateway = 10.0.0.1
    

常用命令

# 创建指定版本的容器
lxc-create -n centos -t mcentos -- --release 6

# 当目标IP为192.168.0.160且端口为2222 NAT 10.0.0.2:22 
iptables -t nat -A PREROUTING -d 192.168.0.160 -p tcp --dport 2222 -j DNAT --to 10.0.0.2:22

# 当目标网口为ens160且端口为2222时 NAT 10.0.0.2:22
iptables -t nat -A PREROUTING -i ens160 -p tcp --dport 2222 -j DNAT --to-destination 10.0.0.2:22

# 当任意网口目标端口为2222时 NAT 10.0.0.2:22
iptables -t nat -A PREROUTING -p tcp --dport 2222 -j DNAT --to-destination 10.0.0.2:22

# 当目任意网口标端口为2222~4444 NAT 10.0.0.2:2222~4444
iptables -t nat -A PREROUTING -p tcp --dport 2222:4444 -j DNAT --to-destination 10.0.0.2:2222-4444

# 为ens160网口增加一个ip
ifconfig ens160 add 192.168.0.161

# 为ens160网口删除一个ip
ifconfig ens160 del 192.168.0.161

# DMZ
iptables -t nat -A PREROUTING -d 192.168.0.161 -j DNAT --to 10.0.0.2

# 删除DMZ
iptables -t nat -D PREROUTING -d 192.168.0.161 -j DNAT --to 10.0.0.2

# 限速 1m上 2m下
wondershaper ens160 1024 2048

# 解除限速
wondershaper clear ens160