Quantcast
Channel: polygun2000的博客
Viewing all 55 articles
Browse latest View live

使用Linux抵御TCP Flooding类型的DDoS攻击

$
0
0
我们做如下讨论的前提是你的带宽足够,如果带宽被打满了,也谈不上什么抵御了。

TCP Flooding类型的DDoS攻击,其原理是攻击TCP的3次握手过程,其主要目的是耗尽被攻击者的资源。

那么对于Linux内核来说,这种攻击是如何产生效果的呢?

对于Linux内核来说,每秒钟可以新创建多少个TCP连接直接与每一个进入"listen"状态的socket锁直接相关。
"listen"状态下的socket锁不止会在收到SYN包时会产生,在收到SYN-ACK,ACK(3次握手的最后一个包)时也会产生。
在SYN Flood攻击的时候,攻击者发送大量虚假的SYN包,促使被攻击者产生大量"listen"状态的socket锁,使系统资源耗尽,不能再产生新的TCP连接。可以参考下图的TCP状态机。



针对上述攻击的原理,我们需要一个方法来过滤这些虚假的数据包,在攻击包使系统进入"listen"状态前就将其DROP掉。

一、使用conntrack状态机制过滤SYN-ACK Flood和ACK Flood攻击

Linux内核中的conntrack状态机制,可以很有效的防范SYN-ACK和ACK类型的攻击。但是conntrack模块的低效一直是大家诟病的重点。

conntrack本身的查找效率其实是相当高的,所谓的低效是由于中心自旋锁"nf_conntrack_lock"导致的,即每插入或删除一条conntrack的entry,都会有锁产生,所以效率低下。
但是自3.15版本的内核开始,这个中心自旋锁已经被移除了。
其效率已经有了质的飞跃,所以再说conntrack效率低已经是老黄历了,具体数据对比见参考文档。

conntrack的状态机制,可以轻松处理除SYN Flood以外所有的TCP Flooding攻击。

具体使用如下两条命令:

第一个命令用来捕捉所有被系统认为是"INVALID"的数据包,予以丢弃
# iptables -A INPUT -m state --state INVALID -j DROP

第二条命令用来使conntrack系统判断更为严格,禁止ACK包产生新的连接
这个参数只对防范ACK Flood类型攻击有效果,对于SYN-ACK Flood类型攻击没有几乎没有影响。
# /sbin/sysctl -w net/netfilter/nf_conntrack_tcp_loose=0

二、使用SYNPROXY防范SYN Flood类型攻击

使用conntrack的状态机制不能阻止SYN Flood类型的攻击,因为即使去掉了nf_conntrack_lock中心锁,SYN类型的包依然会导致"listen"状态的socket锁产生。
现有的SYN-cookies方法不能有效解决这个问题,首先SYN-cookies做SHA计算的代价非常大,另外SYN-cookies本身依然是处于"listen" socket锁下被发送出去的。

Redhat的工程师Jesper Dangaard Brouer提出了SYNPROXY方法来解决这个问题。
其工作原理如下图:



使用SYNPROXY稍微有点复杂,如果不能理解,就按黑魔法的方法来使用吧(照抄的文雅说法)

第一步: 在raw这张表中,确保我们需要保护的连接的SYN包不会在conntrack中产生表项
# iptables -t raw -I PREROUTING -i $DEV -p tcp -m tcp --syn --dport $PORT -j CT --notrack

netfilter中,"raw"表可以看到原始的网络数据,其存在的主要目的就是为了针对特定的数据包关闭连接跟踪(conntrack)。"CT"这个target就是"conntrack",加上"--notrack" 选项的就表示不进行连接跟踪。

第二步: 设置严格的conntrack规则
# /sbin/sysctl -w net/netfilter/nf_conntrack_tcp_loose=0

第三步: 将INVALID(ACK)和UNTRACKED(SYN)状态的数据包导向SYNPROXY模块
# iptables -A INPUT -i $DEV -p tcp -m tcp --dport $PORT -m state --state INVALID,UNTRACKED -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460

第四步: 丢弃INVALID的数据包
# iptables -A INPUT -m state --state INVALID -j DROP

第五步: 打开内核TCP timestamps选项
# /sbin/sysctl -w net/ipv4/tcp_timestamps=1

第六步: 根据攻击强度,对conntrack做一些优化,这个可以参考我之前的blog文章
# echo 1000000 > /sys/module/nf_conntrack/parameters/hashsize
# /sbin/sysctl -w net/netfilter/nf_conntrack_max=2000000

本例中200万个连接大概占用内存为:  288Bytes*200W = 576MB
系统的hashsize,每个hash列表的占用8Bytes,100W个表项只占用8MB,这个hashsize的内存使用尽量小于你的CPU L3缓存大小。

第七步: 使用了上述方法以后,还是不能完全阻止攻击,因此我们必须提高攻击者的代价

使用hashlimit模块限制每个/24子网,每秒最多发起200个SYN连接

iptables -t raw -A PREROUTING -i $DEV \
 -p tcp -m tcp --dport 80 --syn \
 -m hashlimit \
 --hashlimit-above 200/sec --hashlimit-burst 1000 \
 --hashlimit-mode srcip --hashlimit-name syn \
 --hashlimit-htable-size 2097152 \
 --hashlimit-srcmask 24 -j DROP

从其他人的文档中扒了一张测试数据图,供参考。

参考文档
http://people.netfilter.org/hawk/presentations/nfws2014/nfws2014-conntrack-locking.pdf
http://people.netfilter.org/hawk/presentations/devconf2014/iptables-ddos-mitigation_JesperBrouer.pdf
http://rhelblog.redhat.com/2014/04/11/mitigate-tcp-syn-flood-attacks-with-red-hat-enterprise-linux-7-beta/
https://github.com/firehol/firehol/wiki/Working-with-SYNPROXY
https://r00t-services.net/knowledgebase/14/Homemade-DDoS-Protection-Using-IPTables-SYNPROXY.html
https://kb.isc.org/article/AA-01183/0/Linux-connection-tracking-and-DNS.html

 

haproxy中使用map处理大量主机头

$
0
0
先吐槽一下haproxy的官方文档,这个文档真是个神奇的东西,所有需要的东西都在里边,但是如果你想象力不够丰富,就是不知道怎么使用。以下文档,是我简单翻译的aloha blog中的文档而来,加了一点自己的理解。
原文地址见参考文档部分。

我有一个做售后服务的朋友,遇到过一家客户,他们只有一个公网IP,但是要承担几十个不同域名的网站。当时他们使用的是F5 BIGIP,是通过irule取不同主机头,分配到后端不同的pool中去。

haproxy面对同样的问题,也采取了类似的方法,通过ACL来匹配请求中的主机头,然后分配给不同的backend。例子如下:

frontend ft_allapps
 [...]
 use_backend bk_app1 if { hdr(Host) -i app1.domain1.com app1.domain2.com }
 use_backend bk_app2 if { hdr(Host) -i app2.domain1.com app2.domain2.com }
 default_backend bk_default

这个方法对于少量主机头当然OK,但是要把情况极端化一点,例如上百个主机头的情况呢?
那估计配置文件会让人发疯的。

但在haproxy 1.5版以后,haproxy引入了converter这个功能,其中有一种map类型的converter。
这个map converter的功能就是将input和output数据1:1做预先对应。
实际使用中,只要调用map converter,输入map中预先定义好的input值,就会得到对应的output值。

map实际存在于一个包含格式的文本文件中,这个文件在haproxy启动时被加载进来。该文件有两列,左边是input串,右边是output串 。

那么对应前述例子,我们可以预先定义一个map文件,假设文件为/etc/hapee-1.5/domain2backend.map,其内容如下:
#domainname  backendname
app1.domain1.com bk_app1
app1.domain2.com bk_app1
app2.domain1.com bk_app2
app2.domain2.com bk_app2

第一列是主机头,第二列是对应分配的backend名称。

然后在haproxy的配置文件中,做如下配置:
frontend ft_allapps
 [...]
 use_backend %[req.hdr(host),lower,map(/etc/hapee-1.5/domain2backend.map,bk_default)]

haproxy会按照如下步骤操作:

1.req.hdr(host) ==> 从HTTP请求中取得主机头
2.lower ==> 转换所有字符为小写
3.map(/etc/hapee-1.5/domain2backend.map,bk_default) ==>在这个map文件中查找小写的主机头,如果找到对应项,则返回对应的backend名称,如果没找到返回bk_default这个backend名称。
4.将请求流量发送给map返回的backend服务器。

这样即使面对成百上千的主机头,也只是需要在map文件中操作,然后reload即可。
即不需要写正则表达式,也不需要在haproxy配置文件中写入大量的ACL判断。

另外,map数据是以"树"的数据结构存储,所以其查找速度比起查ACL来说要快多了。

简单就是美!!

2016.01.22增补

如果是https网站,则需要从sni中获取hostname
use_backend %[ssl_fc_sni,lower,map(/etc/hapee-1.5/domain2backend.map,bk_default)]

参考文档:
http://blog.haproxy.com/2015/01/26/web-application-name-to-backend-mapping-in-haproxy/

 

haproxy的stick-table实际应用探索

$
0
0
    haproxy的stick-table,很有用,但很抽象,不好理解。之前生吞活剥翻译了一份haproxy官方blog的文档,自己都觉得非常不满意,因此删除了那篇blog文章,我又参考了几份老外的文档,仔细琢磨了一下stick-table,在此把我的理解写一下,希望能帮到有需要的人,转载请注明来自 http://blog.sina.com.cn/polygun2000 的博客.

第一部分: 理解haproxy的stick-table
  
1.1 如何理解stick-table

因为编程水平low,没有去阅读源代码,我理解的stick-table就是在内存中的一个key-value形式存储的表。

能作为key的类型有: 
ip,ipv6,integer,string,binary

而value类似一个set,可以同时存放多个值,值的类型有:
server_id
gpc0,gpc0_rate
conn_cnt,conn_cur,conn_rate
sess_cnt,sess_rate
http_req_cnt,http_req_rate
http_err_cnt,http_err_rate
bytes_in_cnt,bytes_in_rate
bytes_out_cnt,bytes_out_rate

针对每个frontend或backend目前只可以创建一个stick-table。

如果你的需求就是这么任性, 说我就需要2个stick-table怎么办?haproxy文档中提到了可以建立一个"dummy proxy",可以看文末参考文档中的permalink.gmane.org那一篇,其中haproxy的作者willy给举了例子如何这么使用,这可算是最正的根儿了。

1.2 如何定义一个sitck-table


type 定义了这个table的key的类型,可以是 ip,ipv6,integer,string,binary之一
"type ip" 用于存储IPv4类型的IP地址,每个entry约占用50 bytes内存空间,主要用途是存放客户端源IP。
"type ipv6" 用于存储IPv6类型的IP地址,每个entry约占用60 bytes内存空间,主要用途是存放客户端源IP。
"type integer" 用于存储32位整数
"type string [len ]" 用于存储字符串,如果设置了len,则匹配设置的长度,多的字符会被截断,如不指定len,则默认长度是32个字符。
"type binary [len ]" 用于存储二进制块,如果设置了len,则最大存储指定长度,如果数据少于len,则使用0填充。默认长度是32 bytes。

size 定义了这个table可以存储的entry最大数量.
[nopurge] 设置禁止清除旧的entry,默认动作是当stick-table满了以后,haproxy会自动清除最老的entry以便腾出空间.
[peers ] 用于多台haproxy同步stick-table的peers名字,在haproxy.cfg中定义的,这个同步会另文来说。
[expire ] 定义entry在stick-table中的过期时间,默认大概24天.如果不指定expire这个参数,千万不要指定nopurge!

[store ] 用于存储其他额外信息,这些信息可以用于ACL.用于控制各种与客户端活动相关的标准。多种data type可以用逗号分隔,写在store后边。除了"server_id"是系统自动启用和检测以外,其他data type必须明确声明。如果一个ACL引用了一个未声明的data type,则ACL会直接返回不匹配。

- server_id : 一个整数,request被分配到的server ID
- gpc0 : 通用计数器,一个正的32位整数
- conn_cnt : connection计数器,一个正的32位整数,记录了匹配当前entry的,从一个客户端处接收到的连接的绝对数量,这个数量并不意味着被accepted的连接数量,单纯就是收到的数量。
- conn_cur : 当前connection数,一个正32位整数,当一新connection匹配指定的entry的时候,这个数值增加,当connection结束的时候,这个数值减少。通过这个值可以了解一个entry上任意时间点的准确的连接数。
- conn_rate() : connection的连接频率 ,指定时间范围内(毫秒为单位)建立connection的频率。
- sess_cnt : session计数器,一个正32位整数。记录了匹配当前entry的,从一个客户端处接收到的session的绝对数量。一个session指的是一个已经被layer 4规则接受的connection。
- sess_rate() : session的连接频率,指定时间范围内(毫秒为单位)建立session的频率。
                          这个数值可以通过ACL匹配一些规则。
                          这个数值可以通过ACL匹配一些规则。
- http_req_cnt : HTTP请求计数器,一个正32位整数。 记录了匹配当前entry的,从一个客户端接受到的HTTP请求的绝对数量。 无论这个请求是合法还是非法。当开启了客户端keep-alive的时候,这个数值与sess_cnt计数器的值就会不同。
- http_req_rate() : HTTP的请求频率,指定时间范围内(毫秒为单位)收到的HTTP请求的频率。 无论这个请求是合法还是非法。当开启了客户端keep-alive的时候,这个数值与sess_rate() 数器的值就会不同。
- http_err_cnt : HTTP错误计数器,一个正32位整数。 记录了匹配这个entry的HTTP错误的绝对数量,包含: 无效的、被截断的请求 被拒绝的或封堵的请求 认证失败 4xx错误 
- http_err_rate() : HTTP的请求错误频率,指定时间范围内(毫秒为单位)匹配的entry产生的HTTP错误的频率。
- bytes_in_cnt : 一个匹配entry的客户端发往服务器的字节数,形式为一个正64位整数。headers也包含在统计中,通常用于图片或者video服务器限制上传文件。
- bytes_in_rate() : 收到字节频率计数器,指定时间范围内(毫秒为单位)收到的字节数的频率,通常用于防止用户上传太快上传太多内容。这个计数器不建议使用,因为会有不公平情况,建议使用byte_in_cnt。
- bytes_out_cnt : 服务器发往客户端的字节数,一个正64位整数。headers也包含在统计中,通常用于防止机器人爬站。
- bytes_out_rate() : 发送字节频率计数器,指定时间范围内(毫秒为单位)服务器发送给客户端的字节数的频率。通常用于防止用户下载太快太多内容。这个计数器也不建议使用,因为会有不公平情况,建议使用byte_out_cnt。

下边举几个定义stick-table的实际例子:

stick-table type ip size 100k expire 30s store conn_cur
stick-table type ip size 200k expire 3m store conn_rate(100s),bytes_out_rate(60s)
stick-table type ip size 1m expire 10s store gpc0,http_req_rate(10s)

1.3 如何直观的查看stick-table里的内容

1.3.1 使用EPEL源,安装socat。
yum install socat
1.3.2 在haproxy.cfg中开启stat
stats socket /var/run/haproxy.stat mode 600 level operator

# socat readline /var/run/haproxy.stat
prompt
> set timeout cli 1d
> show table
table: http, type: 0, size:2048000, used:1
table: test1, type: 0, size:2048000, used:1
table: test2, type: 0, size:2048000, used:0

> show table http
table: http, type: 0, size:2048000, used:1
0x8370aac: key=127.0.0.1 use=0 exp=592347 gpc0=0

> show table test1
table: test1, type: 0, size:2048000, used:1
0x8370a68: key=127.0.0.1 use=0 exp=19020 conn_rate(100000)=2 bytes_out_rate(60000)=986

> help
Unknown command. Please enter one of the following commands only :
  clear counters : clear max statistics counters (add 'all' for all counters)
  clear table    : remove an entry from a table
  help           : this message
  prompt         : toggle interactive mode with prompt
  quit           : disconnect
  show info      : report information about the running process
  show stat      : report counters for each proxy and server
  show errors    : report last request and response errors for each proxy
  show sess [id] : report the list of current sessions or dump this session
  show table [id]: report table usage stats or dump this table's contents
  get weight     : report a server's current weight
  set weight     : change a server's weight
  set timeout    : change a timeout setting
  disable server : set a server in maintenance mode
  enable server  : re-enable a server that was previously in maintenance mode

1.4 应用stick-table的一般思路

    stick-table从名字上看,原本用途是用于将客户的request(或connection)与backend"粘"在一起,确保同一客户之后的request也会一直与同一backend通讯。而在1.5版以后,这个功能得到了扩展,可以用于存储更多类型的用户请求(或连接)数据的相关信息,利用这些信息,我们可以accept,block或route这些请求。这里想象力就非常重要了

    在haproxy中,要做"粘滞"操作有多种方法,例如cookie insert,source ip hash等。这些都基于简单的规则,但有一些协议或者应用需要比这几种更复杂的"粘滞"规则,不能简单依靠cookie或者source ip hash。

    haproxy 提供了以下几个语句,用于创建复杂的"粘滞"规则。



    1,2分别用于定义一个"pattern",从request和response中抽取数据,用这个数据在stick-table中作为key创建一个entry。
    3用于匹配之前定义的"pattern",用以创建"粘滞"关系。

因为新浪blog不让写"尖括号",所以改用@代替

@pattern@ 是fetch sample的表达式规则,在官方配置文档的7.3节有详细说明。
对于一个请求或连接来说,一旦确定了由哪个backend server来负责处理
这个表达式规则将会决定request或connection中哪些元素需要被分析,抽取然后被存储到stick-table中。

@table@   stick-table表的名字,可选。
如不指定,则默认是使用stick store-request语句所在的backend的stick-table
如指定,可以使用其他backend中定义的stick-table的名字,如果使用了其他backend的stick-table,则server_id是用那个backend里边的。默认情况下,对于每个backend来说,其server_ID都是从1开始,server的顺序是应该是定好的。但为了避免不确定性,强烈建议使用id关键字来自己设置server_ID。

关于stick store-request定义的数量没有明确限制,但对于每个request或response同时只能有8个stick store-request定义。"store-request"规则会在server连接established以后才开始计算,所以stick-table中会包含处理这个request的real server。

@condition@ 可选的存储条件,如果设置了过滤条件,那么可以只储存那些符合(或不符合)条件的内容。

使用stick-table的一般思路如下:
  1. 首先声明一个stick-table,用于存储特定类型的请求(或连接)的相关数据;
  2. 使用stick store-requset,stick store-response,tcp-request content track-sc0 ,tcp-request connection track-sc0等语句将数据送入之前定义的stick-table中; 
  3. 在需要取出数据的地方,用之前存储的key(例如源ip,cookie串等),去stick-table中去取出key对应的数据,利用这些数据做acl判断来决定如何处理后续的用户请求。

需要keep in mind的是: 

默认情况下,stick-table的声明本身只是创建一个sitck-table,并不做任何其他事情。向stick-table中存储数据,需要其他语句。

与之类似的是acl,acl声明语句并不会做任何真正的"action/check",声明语句的作用仅仅是将"acl名字"与"action/check"动作关联起来而已。"action/check"真正被执行是在该acl在其他部分被真正调用的时候。
haproxy中的acl通常是作为某种判断条件,但却不只限于此,有时声明一个acl只是为了完成某个动作,例如GPC计数器的自增操作等,就完全脱离了acl原始概念,这种我们可以认为它是"伪acl"。

[2016.1.19] 增补

有网友提到sc0,sc1,sc2这3个计数器的引入目的,可以先看下官方文档关于这3个计数器的说明。
官方文档提到了这个目的,就是针对一个connection可以在多处同时进行追踪,官方建议sc0计数器用于frontend部分,而sc1用于backend部分。这样可以针对同一个连接同时监控两个指标。

- { track-sc0 | track-sc1 | track-sc2 } [table] :
      enables tracking of sticky counters from current connection. These
      rules do not stop evaluation and do not change default action. Two sets
      of counters may be simultaneously tracked by the same connection. The
      first "track-sc0" rule executed enables tracking of the counters of the
      specified table as the first set. The first "track-sc1" rule executed
      enables tracking of the counters of the specified table as the second
      set. The first "track-sc2" rule executed enables tracking of the
      counters of the specified table as the third set. It is a recommended
      practice to use the first set of counters for the per-frontend counters
      and the second set for the per-backend ones. But this is just a
      guideline, all may be used everywhere.

文末的参考文档haproxy作者willy给了这样一个案例:

backend per-ip
      stick-table type ip size 50k expire 120m store gpc0,http_req_rate(120s)

   frontend
      tcp-request connection track-sc1 src table per-ip
      tcp-request connection reject if { sc1_get_gpc0 gt 0 }
      ...
      use_backend foo...

   backend foo
      tcp-request content track-sc2 src table per-ip if METH_POST
      acl bruteforce_detection  sc2_http_req_rate gt 5
      acl block sc1_inc_gpc0 gt 0
      http-request deny if bruteforce_detection block

这个案例解释了"dummy proxy"如何定义,同时也侧面说明了sc0,sc1,sc2的作用。
a. 首先定义了一个dummy proxy 名字叫per-ip
b. frontend追踪用户源ip(sc1)
c. backend同时追踪这个源ip的所有方法是POST的请求(sc2),并且根据判断情况标记用户是否是攻击用户(acl block sc1_inc_gpc0 gt 0)
d. fontend部分做检查,如果sc1的gpc0技术器大于0,则拒绝这个ip之后的请求。

我们另外再来看几个实际的例子。

例子1:

# 定义一个ip类型作为key的stick-table,用于存储这个ip相关的计数器
stick-table type ip size 500k expire 30s store  conn_cur,conn_rate(10s),http_req_rate(10s),http_err_rate(10s)

# 打开源ip追踪,用以向stick-table写入数据
tcp-request content track-sc0 src


# echo "show table fxa-https" | socat unix:./haproxy/stats -
# table: fxa-https, type: ip, size:512000, used:1
0x1aa3358: key=1.10.2.10 use=1 exp=29957 conn_rate(10000)=43 conn_cur=1 http_req_rate(10000)=42 http_err_rate(10000)=42

例子2:

# 从request或response中抽取SSL session ID,用这个ID将requset与backend"粘住" 
# SSL session ID可能由client提供,也可能由server提供

backend https
    mode tcp
    balance roundrobin
 
# 最大SSL session ID长度是32 bytes.
    stick-table type binary len 32 size 30k expire 30m
 
# 定义两个acl,分别匹配ssl client hello和ssl server hello ,在这个位置,ACL并没有被执行,仅仅是将ACL名字与判断条件关联起来而已。
    acl clienthello req.ssl_hello_type 1
    acl serverhello res.ssl_hello_type 2
 
# 使用tcp content accept来检测ssl client hello和ssl server hello.
# 因为client ssl hello包含在request中,因此必须等待5秒,确保tcp连接的数据都进入了buffer
    tcp-request inspect-delay 5s
    tcp-request content accept if clienthello      # 这里,acl的check操作才真正执行
 
# ssl server hello 由服务器端发出,所以不需要延迟,直接判断
    tcp-response content accept if serverhello
 
# SSL session ID 的长度为1 byte,位置在tcp payload的 offset 43之后。
# client hello在请求中,server hello在response中
# 取出payload_lv(43,1),作为key存入stick-table
    stick store-request payload_lv(43,1) if clienthello
    stick store-response payload_lv(43,1) if serverhello

# 用payload_lv(43,1)做key去stick-table中查找
    stick match payload_lv(43,1)
    server s1 192.168.1.1:443
    server s2 192.168.1.2:443






第二部分: 实际应用-用haproxy防范攻击

2.1 慢查询攻击

slowloris类型的攻击,客户端用非常非常慢的速度发送请求到服务器上。
通常是一个包头接一个包头或者更夸张的一个字符接一个字符,而每个包之间等待非常长的时间。
这样服务器端就不得不等待所有请求全部接收完毕才能返回响应。
这个攻击的目的是阻止正常用户访问我们提供的服务,所有的服务器资源都被用来等待处理慢查询了。

应对这种攻击,方法是在HAProxy中加入选项: "timeout http-request"
可以将这个值设置成5秒钟,应该已经足够长了。
这个参数告诉HAProxy最多等待5秒钟让客户端发送完整的HTTP请求,如果超过5秒,则HAProxy会切断连接并返回错误。

01    # On Aloha, the global section is already setup for you
02    # and the haproxy stats socket is available at /var/run/haproxy.stats
03    global
04      stats socket ./haproxy.stats level admin
05    
06    defaults
07      option http-server-close
08      mode http
09      timeout http-request 5s
10      timeout connect 5s
11      timeout server 10s
12      timeout client 30s
13    
14    listen stats
15      bind 0.0.0.0:8880
16      stats enable
17      stats hide-version
18      stats uri     /
19      stats realm   HAProxy\ Statistics
20      stats auth    admin:admin
21    
22    frontend ft_web
23      bind 0.0.0.0:8080
24    
25      # Spalreadylit static and dynamic traffic since these requests have different impacts on the servers
26      use_backend bk_web_static if { path_end .jpg .png .gif .css .js }
27    
28      default_backend bk_web
29    
30    # Dynamic part of the application
31    backend bk_web
32      balance roundrobin
33      cookie MYSRV insert indirect nocache
34      server srv1 192.168.1.2:80 check cookie srv1 maxconn 100
35      server srv2 192.168.1.3:80 check cookie srv2 maxconn 100
36    
37    # Static objects
38    backend bk_web_static
39      balance roundrobin
40      server srv1 192.168.1.2:80 check maxconn 1000
41      server srv2 192.168.1.3:80 check maxconn 1000

为了测试这个选项的效果,可以使用telnet连接到HAProxy的端口,然后等待5秒钟,输出类似下边:
telnet 127.0.0.1 8080
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
HTTP/1.0 408 Request Time-out
Cache-Control: no-cache
Connection: close
Content-Type: text/html

408 Request Time-out
Your browser didn't send a complete request in time.

Connection closed by foreign host.

2.2.Unfair用户

何谓一个"Unfair"用户?就是指这个用户(或者脚本)有不同于正常用户的行为:

打开太多连接
建立新连接的频率太快
http请求的频率太快
使用太多的带宽
客户端不遵守RFC协议(IE for SMTP)

正常的浏览器行为?

在保护我们站点不受怪异的行为伤害之前,我们需要了解正常的浏览器行为是什么样子的。
首先,用户会打开Chrome, Firefox, Internet Explorer, Opera之间的一种,然后输入URL。
浏览器首先去请求DNS解析IP地址,然后它会建立一个和服务器的连接,下载首页,分析其中的内容。
然后根据页面的HTML代码的连接去下载不同的对象:javascript, css, 图片等等。
为了下载对象,对于每个域名,浏览器会打开6或7个TCP连接。
等到所有对象下载完毕,浏览器会将这些对象叠加渲染出整个页面。

2.2.1 限制每个用户的连接数

根据之前的说明,每个用户浏览器会与webserver之间非常快的打开6或7个TCP连接,那么我们可以认为如果
一个用户打开了超过10个TCP连接就是不正常的行为。

以下示例作了每用户连接数的限制,重点在于25-32行。

01    # On Aloha, the global section is already setup for you
02    # and the haproxy stats socket is available at /var/run/haproxy.stats
03    global
04      stats socket ./haproxy.stats level admin
05    
06    defaults
07      option http-server-close
08      mode http
09      timeout http-request 5s
10      timeout connect 5s
11      timeout server 10s
12      timeout client 30s
13    
14    listen stats
15      bind 0.0.0.0:8880
16      stats enable
17      stats hide-version
18      stats uri     /
19      stats realm   HAProxy\ Statistics
20      stats auth    admin:admin
21    
22    frontend ft_web
23      bind 0.0.0.0:8080
24    
25      # Table definition  
26      stick-table type ip size 100k expire 30s store conn_cur
27    
28      # Allow clean known IPs to bypass the filter
29      tcp-request connection accept if { src -f /etc/haproxy/whitelist.lst }
30      # Shut the new connection as long as the client has already 10 opened
31      tcp-request connection reject if { src_conn_cur ge 10 }
32      tcp-request connection track-sc1 src
33    
34      # Split static and dynamic traffic since these requests have different impacts on the servers
35      use_backend bk_web_static if { path_end .jpg .png .gif .css .js }
36    
37      default_backend bk_web
38    
39    # Dynamic part of the application
40    backend bk_web
41      balance roundrobin
42      cookie MYSRV insert indirect nocache
43      server srv1 192.168.1.2:80 check cookie srv1 maxconn 100
44      server srv2 192.168.1.3:80 check cookie srv2 maxconn 100
45    
46    # Static objects
47    backend bk_web_static
48      balance roundrobin
49      server srv1 192.168.1.2:80 check maxconn 1000
50      server srv2 192.168.1.3:80 check maxconn 1000

配置文件解说:

25      # 定义一个以ip类型为key,大小100K,过期时间30秒的stick-table,用于存储该IP对应的当前connection数量
26      stick-table type ip size 100k expire 30s store conn_cur
27    
28      # 允许白名单用户的IP绕过限制
29      tcp-request connection accept if { src -f /etc/haproxy/whitelist.lst }

30      # 如果当前客户端ip的connection数量已经超过10个,则拒绝其发起的新连接
31      tcp-request connection reject if { src_conn_cur ge 10 }
32      tcp-request connection track-sc1 src

src_conn_cur  是ACL layer 4的fetch method,作用是用当前发起连接的客户端源ip地址做key,去stick-table中查找对应的当前connection数量,如果查到则返回实际数值,查不到则返回0。

注意:
1.如果HAProxy用于代理多个域名,则需要增加conn_cur的数量,因为每个域名会有5-7个连接。
2.如果多个用户在同一NAT设备后边,则这个限制会对他们产生不利影响,原因显而易见。

为了测试限制的效果,我们做如下测试:

使用Apache bench打开10个连接:
ab -n 50000000 -c 10 http://127.0.0.1:8080/

观察haproxy的stats输出:
echo "show table ft_web" | socat unix:./haproxy.stats -
# table: ft_web, type: ip, size:102400, used:1
0x7afa34: key=127.0.0.1 use=10 exp=29994 conn_cur=10

我们打开telnet尝试建立新连接:
telnet 127.0.0.1 8080
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Connection closed by foreign host.

发现已经被HAProxy拒绝了。

2.2.2 限制每用户建立连接的频率

我们可以认为3秒钟之内建立超过20个连接的用户就是非正常访问。
以下示例作了每用户建立连接的频率,重点在于25-32行。

01    # On Aloha, the global section is already setup for you
02    # and the haproxy stats socket is available at /var/run/haproxy.stats
03    global
04      stats socket ./haproxy.stats level admin
05    
06    defaults
07      option http-server-close
08      mode http
09      timeout http-request 5s
10      timeout connect 5s
11      timeout server 10s
12      timeout client 30s
13    
14    listen stats
15      bind 0.0.0.0:8880
16      stats enable
17      stats hide-version
18      stats uri     /
19      stats realm   HAProxy\ Statistics
20      stats auth    admin:admin
21    
22    frontend ft_web
23      bind 0.0.0.0:8080
24    
25      # Table definition  
26      stick-table type ip size 100k expire 30s store conn_rate(3s)
27    
28      # Allow clean known IPs to bypass the filter
29      tcp-request connection accept if { src -f /etc/haproxy/whitelist.lst }
30      # Shut the new connection as long as the client has already 10 opened
31      tcp-request connection reject if { src_conn_rate ge 10 }
32      tcp-request connection track-sc1 src
33    
34      # Split static and dynamic traffic since these requests have different impacts on the servers
35      use_backend bk_web_static if { path_end .jpg .png .gif .css .js }
36    
37      default_backend bk_web
38    
39    # Dynamic part of the application
40    backend bk_web
41      balance roundrobin
42      cookie MYSRV insert indirect nocache
43      server srv1 192.168.1.2:80 check cookie srv1 maxconn 100
44      server srv2 192.168.1.3:80 check cookie srv2 maxconn 100
45    
46    # Static objects
47    backend bk_web_static
48      balance roundrobin
49      server srv1 192.168.1.2:80 check maxconn 1000
50      server srv2 192.168.1.3:80 check maxconn 1000

注意:如果多个用户在同一NAT设备后边,则这个限制也会对他们产生不利影响,原因同样显而易见。

为了测试限制的效果,我们做如下测试:

ab -n 10 -c 1 -r http://127.0.0.1:8080/

echo "show table ft_web" | socat unix:./haproxy.stats -
# table: ft_web, type: ip, size:102400, used:1
0x11faa3c: key=127.0.0.1 use=0 exp=28395 conn_rate(3000)=10

telnet 127.0.0.1 8080
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Connection closed by foreign host.

2.2.3 限制HTTP请求的频率

01    # On Aloha, the global section is already setup for you
02    # and the haproxy stats socket is available at /var/run/haproxy.stats
03    global
04      stats socket ./haproxy.stats level admin
05    
06    defaults
07      option http-server-close
08      mode http
09      timeout http-request 5s
10      timeout connect 5s
11      timeout server 10s
12      timeout client 30s
13    
14    listen stats
15      bind 0.0.0.0:8880
16      stats enable
17      stats hide-version
18      stats uri     /
19      stats realm   HAProxy\ Statistics
20      stats auth    admin:admin
21    
22    frontend ft_web
23      bind 0.0.0.0:8080
24    
25      # Use General Purpose Couter (gpc) 0 in SC1 as a global abuse counter
26      # Monitors the number of request sent by an IP over a period of 10 seconds
27      stick-table type ip size 1m expire 10s store gpc0,http_req_rate(10s)
28      tcp-request connection track-sc1 src
29      tcp-request connection reject if { src_get_gpc0 gt 0 }
30    
31      # Split static and dynamic traffic since these requests have different impacts on the servers
32      use_backend bk_web_static if { path_end .jpg .png .gif .css .js }
33    
34      default_backend bk_web
35    
36    # Dynamic part of the application
37    backend bk_web
38      balance roundrobin
39      cookie MYSRV insert indirect nocache
40    
41      # If the source IP sent 10 or more http request over the defined period,
42      # flag the IP as abuser on the frontend
43      acl abuse src_http_req_rate(ft_web) ge 10
44      acl flag_abuser src_inc_gpc0(ft_web)
45      tcp-request content reject if abuse flag_abuser
46    
47      server srv1 192.168.1.2:80 check cookie srv1 maxconn 100
48      server srv2 192.168.1.3:80 check cookie srv2 maxconn 100
49    
50    # Static objects
51    backend bk_web_static
52      balance roundrobin
53      server srv1 192.168.1.2:80 check maxconn 1000
54      server srv2 192.168.1.3:80 check maxconn 1000

测试方法同上边。

2.2.4 检测漏洞扫描

如果有人尝试对我们的站点进行漏洞扫描,那么通过HAProxy可以追踪到不同的错误。

HAProxy可以监控每个用户产生错误的频率,并且根据这个频率决定进一步的操作。

01    # On Aloha, the global section is already setup for you
02    # and the haproxy stats socket is available at /var/run/haproxy.stats
03    global
04      stats socket ./haproxy.stats level admin
05    
06    defaults
07      option http-server-close
08      mode http
09      timeout http-request 5s
10      timeout connect 5s
11      timeout server 10s
12      timeout client 30s
13    
14    listen stats
15      bind 0.0.0.0:8880
16      stats enable
17      stats hide-version
18      stats uri     /
19      stats realm   HAProxy\ Statistics
20      stats auth    admin:admin
21    
22    frontend ft_web
23      bind 0.0.0.0:8080
24    
25      # Use General Purpose Couter 0 in SC1 as a global abuse counter
26      # Monitors the number of errors generated by an IP over a period of 10 seconds
27      stick-table type ip size 1m expire 10s store gpc0,http_err_rate(10s)
28      tcp-request connection track-sc1 src
29      tcp-request connection reject if { src_get_gpc0 gt 0 }
30    
31      # Split static and dynamic traffic since these requests have different impacts on the servers
32      use_backend bk_web_static if { path_end .jpg .png .gif .css .js }
33    
34      default_backend bk_web
35    
36    # Dynamic part of the application
37    backend bk_web
38      balance roundrobin
39      cookie MYSRV insert indirect nocache
40    
41      # If the source IP generated 10 or more http request over the defined period,
42      # flag the IP as abuser on the frontend
43      acl abuse src_http_err_rate(ft_web) ge 10
44      acl flag_abuser src_inc_gpc0(ft_web)
45      tcp-request content reject if abuse flag_abuser
46    
47      server srv1 192.168.1.2:80 check cookie srv1 maxconn 100
48      server srv2 192.168.1.3:80 check cookie srv2 maxconn 100
49    
50    # Static objects
51    backend bk_web_static
52      balance roundrobin
53      server srv1 192.168.1.2:80 check maxconn 1000
54      server srv2 192.168.1.3:80 check maxconn 1000

我们通过如下方法测试:

ab -n 10 http://127.0.0.1:8080/dlskfjlkdsjlkfdsj

echo "show table ft_web" | socat unix:./haproxy.stats -
# table: ft_web, type: ip, size:1048576, used:1
0x8a9770: key=127.0.0.1 use=0 exp=5866 gpc0=1 http_err_rate(10000)=11.

再次执行上边的ab命令,会得到如下错误:
apr_socket_recv: Connection reset by peer (104)

说明HAProxy已经block这个IP。

参考文档:
http://blog.exceliance.fr/2012/02/27/use-a-load-balancer-as-a-first-row-of-defense-against-ddos/
http://brokenhaze.com/blog/2014/03/25/how-stack-exchange-gets-the-most-out-of-haproxy/
https://blog.codecentric.de/en/2014/12/haproxy-http-header-rate-limiting/
https://github.com/jvehent/haproxy-aws
http://cbonte.github.com/haproxy-dconv/configuration-1.5.html#stick-table
http://blog.serverfault.com/2010/08/26/1016491873/
http://blog.gnuers.org/?p=120
http://permalink.gmane.org/gmane.comp.web.haproxy/13285
http://serverfault.com/questions/678882/is-there-a-way-to-rate-limit-connections-with-haproxy-using-multiple-thresholds
http://blog.haproxy.com/2013/04/26/wordpress-cms-brute-force-protection-with-haproxy/

 

haproxy的log相关

$
0
0
当haproxy部署在复杂网络环境下时,一旦出现问题,我们需要log来帮助我们分析定位故障。haproxy的log提供了非常详尽的信息,本文作为读文档的笔迹,会边读边改,希望最后能形成一份有用的东西。

一、hparoxy日志中的termination_state

haproxy的tcplog或httplog提供了一个"termination_state"字段,这个字段提供了一个session是如何中断的指示器。在tcplog中是2个字符,在httplog中是4个字符, 通常我们初步定位故障是用前两个字符。

  - 第一个字符的含义,是什么事件导致了session中断:

        C : TCP session 由于client原因意外终止.

        S : TCP session 由于server原因意外终止,或者被server明确拒绝.

        P : session过早的被proxy终止。原因可以是实施了connection limit,或者被匹配了一个DENY acl,或者由于server回应的信息中带有某些危险的错误,没有通过安全检查,而被阻止(例如: 可缓存的cookie)
         
        L : session被haproxy本地处理,没有传递给后端server。这个标志通常发生在stats和redirect操作.

        R : proxy上的某个资源已经耗尽(内存, socket, 源端口...). 通常这个标志出现在connection阶段,系统日志内也会包含一个同样的错误信息,如果出现了这样的错误,那么必须尽快处理。

        I : proxy自检出现内部错误。这个错误应该永远不会出现,如果你在log中发现这个错误,那么通常是haproxy出现了bug。通常的解决方法是冷重启haproxy。

        D : session被haproxy关闭,因为后端server被检测到宕机。

        U : session被haproxy关闭,当后端server failback的时候关闭backup server的连接.

        K : session被haproxy的管理操作主动关闭.

        c : client-side timeout,等待client收发数据超时.

        s : server-side timeout,等待server手法数据超时.

        - : session正常结束.

  - 第二个字符的含义,当session被关闭时,该session当时所处的状态:

        R : proxy正等待连接完成,等待client的合法的REQUEST. 此时尚未向任何server发送过数据.(仅用于HTTP mode)
        H : proxy正等待连接完成,等待server的合法respone HEADERS.(仅用于HTTP mode)
        Q : proxy正在队列中等待进行连接。这个标志当server设置了'maxconn'参数会出现.当持续试图redispatch请求到一个垂死的server的时候,也有可能出现在全局queue中。 如果没有redispatch发生,则不会与任何server试图建立连接.

        C : proxy正等待与server建立连接.

        D : session处于DATA阶段.

        L : proxy正在向client传送最后的data,此时server已经完成传送。这个状态很罕见,只会发生在client在接收最后的数据包时突然死掉。

        T : requset被故意延迟了,连接在整个"timeout tarpit"期间一直保持打开或者直到client关闭,两者都会被计入"Tw"计时器项目中.

        - : session正常结束.

参考文档:

 

Centos 7的最小化安装

$
0
0
针对生产环境,删除一些Centos 7上不需要的包,算是针对我们生产环境的真正minimal安装吧。

%packages --nobase
@core --nodefaults
-aic94xx-firmware*
-alsa-*
-avahi-*
-biosdevname
-btrfs-progs*
-dhclient
-dhcp*
-dracut-network
-iprutils
-irqbalance*
-ivtv*
-iwl*firmware
-libertas*
-kexec-tools
-ModemManager*
-NetworkManager*
-plymouth*
-postfix
-wpa_supplicant-*
%end

 

Centos 7中的网卡一致性命名规则

$
0
0
一致性网络设备命名,即Consistent Network Device Naming

一、为什么需要这个

服务器通常有多块网卡,有板载集成的,同时也有插在PCIe插槽的。
Linux系统的命名原来是eth0,eth1这样的形式,但是这个编号往往不一定准确对应网卡接口的物理顺序。
为解决这类问题,dell开发了biosdevname方案。
systemd v197版本中将dell的方案作了进一步的一般化拓展。
目前的Centos既支持dell的biosdevname,也支持systemd的方案。


二、Centos7中的命名策略

Scheme 1: 如果从BIOS中能够取到可用的,板载网卡的索引号,则使用这个索引号命名,例如: eno1,如不能则尝试Scheme 2
Scheme 2: 如果从BIOS中能够取到可以用的,网卡所在的PCI-E热插拔插槽的索引号,则使用这个索引号命名,例如: ens1,如不能则尝试Scheme 3
Scheme 3:如果能拿到设备所连接的物理位置信息,则使用这个信息命名,例如:enp2s0,如不能则尝试Scheme 5
Scheme 5:传统的kernel命名方法,例如: eth0,这种命名方法的结果不可预知的,即可能第二块网卡对应eth0,第一块网卡对应eth1。

Scheme 4 使用网卡的MAC地址来命名,这个方法一般不使用。

三、biosdevname和net.ifnames两种命名规范

net.ifnames的命名规范为:   设备类型+设备位置+数字

设备类型:
en 表示Ethernet
wl 表示WLAN
ww 表示无线广域网WWAN

设备位置:


实际的例子:
eno1 板载网卡
enp0s2  pci网卡
ens33   pci网卡
wlp3s0  PCI无线网卡
wwp0s29f7u2i2   4G modem
wlp0s2f1u4u1   连接在USB Hub上的无线网卡
enx78e7d1ea46da pci网卡
----------------------------------------
biosdevname的命名规范为



实际的例子:
em1 板载网卡
p3p4 pci网卡
p3p4_1 虚拟网卡

四、systemd中的实际执行顺序

按照如下顺序执行udev的rule
1./usr/lib/udev/rules.d/60-net.rules
2./usr/lib/udev/rules.d/71-biosdevname.rules
3./lib/udev/rules.d/75-net-description.rules
4./usr/lib/udev/rules.d/80-net-name-slot.rules

60-net.rules 
使用/lib/udev/rename_device这个程序,去查询/etc/sysconfig/network-scripts/下所有以ifcfg-开头的文件
如果在ifcfg-xx中匹配到HWADDR=xx:xx:xx:xx:xx:xx参数的网卡接口
则选取DEVICE=yyyy中设置的名字作为网卡名称。

71-biosdevname.rules
如果系统中安装了biosdevname,且内核参数未指定biosdevname=0,且上一步没有重命名网卡,则按照biosdevname的命名规范,从BIOS中取相关信息来命名网卡。
主要是取SMBIOS中的type 9 (System Slot) 和 type 41 (Onboard Devices Extended Information)
不过要求SMBIOS的版本要高于2.6,且系统中要安装biosdevname程序。

75-net-description.rules
udev通过检查网卡信息,填写如下这些udev的属性值
ID_NET_NAME_ONBOARD
ID_NET_NAME_SLOT
ID_NET_NAME_PATH
ID_NET_NAME_MAC 

80-net-name-slot.rules
如果在60-net.rules ,71-biosdevname.rules这两条规则中没有重命名网卡,且内核未指定net.ifnames=0参数
则udev依次尝试使用以下属性值来命名网卡,如果这些属性值都没有,则网卡不会被重命名。
ID_NET_NAME_ONBOARD
ID_NET_NAME_SLOT
ID_NET_NAME_PATH

上边的71-biosdevname.rules 是实际执行biosdevname的policy
75-net-description.rules和80-net-name-slot.rules实际执行Scheme 1,2,3

根据上述的过程,可见网卡命名受 biosdevname和net.ifnames这两个内核参数影响。
这两个参数都可以在grub配置中提供。
biosdevname=0是系统默认值(dell服务器默认是1),net.ifnames=1是系统默认值:

#vi /boot/grub/grub.conf
kernel /boot/vmlinuz biosdevname=1
initrd /boot/initrd.img 

第二节所说的Scheme的策略顺序是系统默认的。
系统BIOS符合要求,且系统中安装了biosdevname,且biosdevname=1启用,则biosdevname优先;
如果BIOS不符合biosdevname要求或biosdevname=0,则仍然是systemd的规则优先。
如果用户自己定义了udev rule来修改内核设备名字,则用户规则优先。

内核参数组合使用的时候,其结果如下:
默认内核参数(biosdevname=0,net.ifnames=1):  网卡名 "enp5s2"
biosdevname=1,net.ifnames=0:网卡名 "em1"
biosdevname=0,net.ifnames=0:网卡名 "eth0" (最传统的方式,eth0 eth1 傻傻分不清)

参考文档:
http://benjr.tw/93340
https://docs.google.com/viewer?url=http://domsch.com/linux/lpc2010/lpc2010-network-device-naming.pdf
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Networking_Guide/
http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/
http://blog.chinaunix.net/uid-14735472-id-4195429.html

 

Centos7的网卡teaming

$
0
0
对于链路聚合,linux中已有bonding驱动,在CentOS 7中又引入一个teaming驱动。
相比bonding,teaming更强调是在用户空间配置聚合,也比bonding支持更多特性。
具体可以参考官方的Networking guide文档。

teaming驱动本身工作在内核里,创建聚合端口要靠teamd(或者自己写代码调用libteam)。
teaming本身可以不依靠交换机配置创建聚合,支持roundrobin,activebackup,loadbalance等形式。
如果交换机参与,则交换机和linux主机都要支持lacp协议。

下边用H3C S5500-HI交换机举例,说明一下如何配置teaming

一、H3C S5500-HI端配置

(1)# 创建二层聚合接口1,并配置该接口为动态聚合模式。
[DeviceA] interface bridge-aggregation 1
[DeviceA-Bridge-Aggregation1] link-aggregation mode dynamic
[DeviceA-Bridge-Aggregation1] quit

(2)# 分别将端口GigabitEthernet1/0/1至 GigabitEthernet1/0/2加入到聚合组1中。
# 配置端口的LACP超时时间为短超时(即1秒)
[DeviceA] interface gigabitethernet 1/0/1
[DeviceA-gigabitethernet1/0/1] port link-aggregation group 1
[Sysname-GigabitEthernet1/0/1] lacp period short
[DeviceA-gigabitethernet1/0/1] quit
[DeviceA] interface gigabitethernet 1/0/2
[DeviceA-gigabitethernet1/0/2] port link-aggregation group 1
[Sysname-GigabitEthernet1/0/1] lacp period short
[DeviceA-gigabitethernet1/0/2] quit

(3)# 配置二层聚合接口1为Trunk端口,并允许VLAN 10和20的报文通过。
注:这里只是为了说明问题,如果不需要Trunk,可以配置成普通接口即可。
[DeviceA] interface bridge-aggregation 1
[DeviceA-Bridge-Aggregation1] port link-type trunk
[DeviceA-Bridge-Aggregation1] port trunk permit vlan 10 20
[DeviceA-Bridge-Aggregation1] quit

(4)# 配置全局按照报文的源MAC地址和目的MAC地址进行聚合负载分担。
[DeviceA] link-aggregation load-sharing mode source-mac destination-mac

# 查看交换机上所有聚合组的摘要信息。
[Switch] display link-aggregation summary
 
Aggregation Interface Type:
BAGG -- Bridge-Aggregation, RAGG -- Route-Aggregation
Aggregation Mode: S -- Static, D -- Dynamic
Loadsharing Type: Shar -- Loadsharing, NonS -- Non-Loadsharing
Actor System ID: 0x8000, 000f-e2ff-0001
 
AGG         AGG       Partner ID               Select Unselect   Share
Interface   Mode                               Ports  Ports      Type
-------------------------------------------------------------------------------
BAGG1       D         0x8000, 000f-e2ff-0002   2      0          Shar
以上信息表明,聚合组1为负载分担类型的二层动态聚合组,包含有2个选中端口。

# 查看交换机上全局采用的聚合负载分担类型。
[Switch] display link-aggregation load-sharing mode
 
Link-Aggregation Load-Sharing Mode:
  destination-mac address, source-mac address
以上信息表明,所有聚合组都按照报文的源MAC地址和目的MAC地址进行聚合负载分担。

二、Linux主机配置

(0)# 安装teamd
~]# yum install teamd

(1)# 创建team接口配置文件
~]# vi /etc/sysconfig/network-scripts/ifcfg-team0

DEVICE=team0
DEVICETYPE=Team
ONBOOT=yes
BOOTPROTO=none
TEAM_CONFIG='{
   "runner": {
       "name": "lacp",
       "active": false,
       "fast_rate": true,
       "tx_hash": ["eth"]
   },
     "link_watch": {"name": "ethtool"},
     "ports": {"em1": {}, "em2": {}}
}'

注意: 
fast_rate配置LACP超时要和H3C交换机上的对应
tx_hash的方法选择"eth",即使用源MAC和目的MAC做hash计算,也与H3C交换机配置对应

(2)# 创建成员接口配置文件
~]# vi /etc/sysconfig/network-scripts/ifcfg-eth1

DEVICE=eth1
HWADDR=D4:85:64:01:46:9E
DEVICETYPE=TeamPort
ONBOOT=yes
TEAM_MASTER=team0

~]# vi /etc/sysconfig/network-scripts/ifcfg-eth2

DEVICE=eth2
HWADDR=D4:85:64:01:46:9F
DEVICETYPE=TeamPort
ONBOOT=yes
TEAM_MASTER=team0

(3)配置team接口的VLAN子接口,以VLAN 10为例
~]# vi /etc/sysconfig/network-scripts/ifcfg-team0.10

DEVICE=team0.10
IPADDR=192.168.99.88
NETMASK=255.255.255.0
ONBOOT=static
VLAN=yes

(4)# 重新启动网络服务,如果报错,可能需要reboot一下
~]# systemctl restart network

# 确认team接口成员
~]# teamnl team0 ports

# 确认team接口状态
~]# teamdctl team0 state

CentOS 7支持多种网卡命名方式,此处网卡的命名规则,我选择了原始的eth方式,主要是因为习惯了。
系统启动后会首先查看/usr/lib/udev/rules.d/60-net.rules,通知/lib/udev/rename_device去查看所有的ifcfg文件。如果发现一个接口MAC地址匹配到一个ifcfg文件中的HWADDR项,则使用该ifcfg文件中的DEVICE项的名字来命名该接口。

参考文档:
http://www.h3c.com.cn/Service/Document_Center/Switches/Catalog/S5500/S5500-EI/Configure/Operation_Manual/H3C_S5500-SI[EI]_CG-Release_2208-6W100/03/201101/707252_30005_0.htm
http://www.h3c.com.cn/Service/Document_Center/Switches/Catalog/S5500/S5500-EI/Command/Command_Manual/H3C_S5500-EI[SI]_CR-Release_2220-6W100/03/201503/858785_30005_0.htm
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Networking_Guide/ch-Introduction_to_RHEL_Networking.html
https://github.com/jpirko/libteam/wiki/Tutorial
http://techgnat.blogspot.hk/2014/12/centos-teaming-with-vlans.html
http://ceyes.github.io/2014-07/Study-Linux-Team/
http://vinzlinux.blogspot.hk/2014/12/configuring-network-teaming-using-nmcli.html
https://ervikrant06.wordpress.com/2014/06/06/how-to-configure-network-teaming-in-rhel-7/
https://blog.headdesk.me/2015/02/teaming-nics-with-teamd/
https://www.mankier.com/5/teamd.conf
https://docs.fedoraproject.org/en-US/Fedora/23/html/Networking_Guide/sec-Configuring_a_VLAN_over_a_Bond.html
http://blog.vikki.in/network-teaming-rhel7/


 

centos7+nginx+php-fpm+zabbix安装文档

$
0
0
测试用的快速安装文档,纯粹流水账。

1.安装epel,nginx,zabbix,percona的官方源
# yum install epel-release
# rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
# rpm -Uvh http://repo.zabbix.com/zabbix/2.4/rhel/7/x86_64/zabbix-release-2.4-1.el7.noarch.rpm
# rpm -Uvh http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm

2.更新系统到最新状态,关闭SELinux
# yum update
# vi /etc/selinux/config 
  ===> SELINUX=disabled
# reboot

3.安装nginx
# yum install nginx
# systemctl start nginx.service
# systemctl enable nginx.service

打开浏览器,输入http://your_server_IP_address/  可以看到"welcome to nginx"页面

4.安装php、php-fpm、zendopcache
# yum install php-fpm php-cli php-pecl-zendopcache

# vi /etc/php.ini
  ===> cgi.fix_pathinfo=0

#  vi /etc/php-fpm.d/www.conf
  ===> listen = /var/run/php-fpm/php-fpm.sock
  ===> listen.owner = nobody
  ===> listen.group = nobody
  ===> user = nginx
  ===> group = nginx

创建php使用的session存储目录,并设置正确的属主和权限  
# mkdir -p /var/lib/php/session
# chown nginx:nginx /var/lib/php/session
# chmod 755 /var/lib/php/session

# systemctl start php-fpm.service
# systemctl enable php-fpm.service

5.配置nginx使用php-fpm
# /etc/nginx/conf.d/default.conf
server {
    listen       80;
    server_name  server_domain_name_or_IP;

    # note that these lines are originally from the "location /" block
    root   /usr/share/nginx/html;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

# systemctl restart nginx

6.测试php工作正常
# vi /usr/share/nginx/html/info.php



打开浏览器访问,http://your_server_IP_address/info.php
如果出现PHP版本等信息,则说明php工作正常。

# rm /usr/share/nginx/html/info.php

5.安装percona,并初始化
# yum install Percona-Server-server-56 Percona-Server-client-56
# systemctl start mysqld.service
# mysql_secure_installation
# systemctl enable mysqld.service

6.安装zabbix
# yum install php-mysql php-gd php-xml php-bcmath php-mbstring
# yum install zabbix-get zabbix-server-mysql zabbix-agent 

7.初始化zabbix数据库
# mysql -u root -p
mysql> create database zabbixdb character set utf8;
mysql> grant all privileges on zabbixdb.* to 'zabbixuser'@'localhost' identified by 'password';
mysql> flush privileges;
mysql> exit

为了避免history等大表查询慢的问题,从开始建表就做好partition工作。
history,history_log,history_str,history_text,history_uint 这5张表,每天一个partition
trends,trends_uint 这2张表,每月一个partition

我们需要修改一下zabbix自带的建库SQL文件schema.sql,因为太长没法贴,说明一下:
history,history_log,history_str,history_text,history_uint 这5张表的建库SQL修改结尾为:

ENGINE=InnoDB
PARTITION BY RANGE(clock)
(
PARTITION p2016_02_13 VALUES LESS THAN (UNIX_TIMESTAMP("2016-02-13 00:00:00")) ENGINE = InnoDB,
PARTITION p2016_02_14 VALUES LESS THAN (UNIX_TIMESTAMP("2016-02-14 00:00:00")) ENGINE = InnoDB
);

trends,trends_uint 这2张表的建库SQL修改结尾为:
ENGINE=InnoDB
PARTITION BY RANGE (clock)
(
PARTITION p2016_01 VALUES LESS THAN (UNIX_TIMESTAMP("2016-01-01 00:00:00")) ENGINE = InnoDB,
PARTITION p2016_02 VALUES LESS THAN (UNIX_TIMESTAMP("2016-02-01 00:00:00")) ENGINE = InnoDB
);

history_log这张表的建库SQL:
删除这一行====> PRIMARY KEY (id)
增加这一行====> CREATE INDEX `history_log_0` ON `history_log` (`id`);
删除这一行====> CREATE UNIQUE INDEX `history_log_2` ON `history_log` (`itemid`,`id`);

history_text这张表的建库SQL:
删除这一行====> PRIMARY KEY (id)
增加这一行====> CREATE INDEX `history_text_0` ON `history_text` (`id`);
删除这一行====> CREATE UNIQUE INDEX `history_text_2` ON `history_text` (`itemid`,`id`);

housekeeper这张表的建库SQL:
修改====> ENGINE=BLACKHOLE;

注意: NOT NULL后边的逗号(,) , 一定要注意,多了就会报语法错误了。

# mysql -u zabbixuser -p zabbixdb < /usr/share/doc/zabbix-server-mysql-2.4.7/create/schema.sql
# mysql -u zabbixuser -p zabbixdb < /usr/share/doc/zabbix-server-mysql-2.4.7/create/images.sql
# mysql -u zabbixuser -p zabbixdb < /usr/share/doc/zabbix-server-mysql-2.4.7/create/data.sql

建立存储过程,定时自动增加新分区,删除旧分区,对zabbix官方wiki提供的SQL做了一点修改,主要点如下:
删除====> USE `zabbix`$$
删除zabbix.,修改库名为zabbixdb====> CALL drop_partitions('zabbixdb');
删除zabbix.,修改库名为zabbixdb====> CALL create_next_partitions('zabbixdb')
SQL文件详细见文末),

# mysql -u zabbixuser -p zabbixdb < auto_partition.sql

开启event_scheduler
mysql>  SHOW GLOBAL VARIABLES LIKE 'event_scheduler';
mysql>  SET GLOBAL event_scheduler = ON;

# vi /etc/my.cnf
[mysqld]
 ====> event-scheduler=ON

注意: 因为partition要求时间顺序严格递增,所以务必使用ntp确保时间准确,否则event_scheduler会执行失败。

8.修改zabbix配置文件
# vi /etc/zabbix/zabbix_server.conf
  ===> DBHost=localhost
  ===> DBName=zabbixdb
  ===> DBUser=zabbixuser
  ===> DBPassword=password
  
# vi /etc/zabbix/zabbix_agentd.conf
  ===> Server=127.0.0.1
  ===> ServerActive=127.0.0.1
  ===> Hostname=127.0.0.1

# vi /etc/php.ini
  ===> max_execution_time = 600
  ===> max_input_time = 600
  ===> memory_limit = 256M
  ===> post_max_size = 32M
  ===> upload_max_filesize = 16M
  ===> date.timezone = Asia/Shanghai

# systemctl start zabbix-server 
# systemctl start zabbix-agent
# systemctl enable zabbix-server
# systemctl enable zabbix-agent

9.安装zabbix的webGUI
从zabbix官网下载源码包,复制包中的php目录到nginx文档目录下
# mkdir /usr/share/nginx/html/zabbix
# tar xvzf zabbix-2.4.7.tar.gz
# cd frontends/php
# cp -a . /usr/share/nginx/html/zabbix
# chown -R nginx.nginx /usr/share/nginx/html/zabbix
# systemctl restart mysqld.service
# systemctl restart php-fpm.service
# systemctl restart nginx

打开浏览器,访问http://your_server_IP_address/zabbix完成安装。
默认用户名: admin
默认密码: zabbix

10.zabbix server本身的监控配置

激活zabbix server本身的监控
Configuration -> Hosts -> 勾选(zabbix server) -> 选择"Enable selected" -> Go

等几分钟以后,查看zabbix是否有收集到监控数据
Monitoring -> Latest data
Monitoring -> Screens

11.关闭Housekeeping功能
Administration-->General-->Housekeeping
取消所有的Enable internal housekeeping,然后Update

12.被监控的主机安装zabbix agent
# rpm -Uv  http://repo.zabbix.com/zabbix/2.4/rhel/7/x86_64/zabbix-release-2.4-1.el7.noarch.rpm
# yum install zabbix-agent

# vi /etc/zabbix/zabbix_agentd.conf
Server=192.168.1.150
ServerActive=192.168.1.150
Hostname=server2.unixmen.local

# systemctl start zabbix-agent
# systemctl enable zabbix-agent

[安装TokuDB引擎参考如下步骤,可选]
-----------------开始-----------------------
# yum install jemalloc
# vi /etc/my.cnf
[mysqld_safe]
  ===> malloc-lib=/usr/lib64/libjemalloc.so.1

使用tokudb需要关闭transparent_hugepage支持,可以如下确认
# cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never

修改grub内核参数,关闭transparent_hugepage
# vi /etc/default/grub
  ===> GRUB_CMDLINE_LINUX_DEFAULT="transparent_hugepage=never" 

重新生成grub配置文件
# grub2-mkconfig -o /boot/grub2/grub.cfg
# reboot

安装tokudb引擎
# yum install Percona-Server-tokudb-56

启用tokudb引擎
# ps_tokudb_admin --enable -uroot -p

确认tokudb引擎状态和版本
mysql> SHOW ENGINES;
...
 | TokuDB | YES | Tokutek TokuDB Storage Engine with Fractal Tree(tm) Technology | YES | YES | YES |
...
mysql> SELECT @@tokudb_version;
+------------------+
| @@tokudb_version |
+------------------+
| 5.6.28-76.1      |
+------------------+
1 row in set (0.00 sec)
----------------结束-------------------

[自动分区的SQL文件,auto_partition.sql]
这些SQL来源于zabbix官方wiki,https://zabbix.org/wiki/Docs/howto/mysql_partitioning
做了写小改动。
-----------------开始-----------------------
DROP TABLE IF EXISTS `manage_partitions`;
CREATE TABLE `manage_partitions` (
  `tablename` VARCHAR(64) NOT NULL COMMENT 'Table name',
  `period` VARCHAR(64) NOT NULL COMMENT 'Period - daily or monthly',
  `keep_history` INT(3) UNSIGNED NOT NULL DEFAULT '1' COMMENT 'For how many days or months to keep the partitions',
  `last_updated` DATETIME DEFAULT NULL COMMENT 'When a partition was added last time',
  `comments` VARCHAR(128) DEFAULT '1' COMMENT 'Comments',
  PRIMARY KEY (`tablename`)
) ENGINE=INNODB;

INSERT INTO manage_partitions (tablename, period, keep_history, last_updated, comments) VALUES ('history', 'day', 30, now(), '');
INSERT INTO manage_partitions (tablename, period, keep_history, last_updated, comments) VALUES ('history_uint', 'day', 30, now(), '');
INSERT INTO manage_partitions (tablename, period, keep_history, last_updated, comments) VALUES ('history_str', 'day', 120, now(), '');
INSERT INTO manage_partitions (tablename, period, keep_history, last_updated, comments) VALUES ('history_text', 'day', 120, now(), '');
INSERT INTO manage_partitions (tablename, period, keep_history, last_updated, comments) VALUES ('history_log', 'day', 120, now(), '');
INSERT INTO manage_partitions (tablename, period, keep_history, last_updated, comments) VALUES ('trends', 'month', 24, now(), '');
INSERT INTO manage_partitions (tablename, period, keep_history, last_updated, comments) VALUES ('trends_uint', 'month', 24, now(), '');

DELIMITER $$
DROP PROCEDURE IF EXISTS `create_next_partitions`$$
CREATE PROCEDURE `create_next_partitions`(IN_SCHEMANAME VARCHAR(64))
BEGIN
    DECLARE TABLENAME_TMP VARCHAR(64);
    DECLARE PERIOD_TMP VARCHAR(12);
    DECLARE DONE INT DEFAULT 0;
 
    DECLARE get_prt_tables CURSOR FOR
        SELECT `tablename`, `period`
            FROM manage_partitions;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
 
    OPEN get_prt_tables;
 
    loop_create_part: LOOP
        IF DONE THEN
            LEAVE loop_create_part;
        END IF;
 
        FETCH get_prt_tables INTO TABLENAME_TMP, PERIOD_TMP;
 
        CASE WHEN PERIOD_TMP = 'day' THEN
                    CALL `create_partition_by_day`(IN_SCHEMANAME, TABLENAME_TMP);
             WHEN PERIOD_TMP = 'month' THEN
                    CALL `create_partition_by_month`(IN_SCHEMANAME, TABLENAME_TMP);
             ELSE
            BEGIN
                            ITERATE loop_create_part;
            END;
        END CASE;
 
                UPDATE manage_partitions set last_updated = NOW() WHERE tablename = TABLENAME_TMP;
    END LOOP loop_create_part;
 
    CLOSE get_prt_tables;
END$$
DELIMITER ;

DELIMITER $$
DROP PROCEDURE IF EXISTS `create_partition_by_day`$$
CREATE PROCEDURE `create_partition_by_day`(IN_SCHEMANAME VARCHAR(64), IN_TABLENAME VARCHAR(64))
BEGIN
    DECLARE ROWS_CNT INT UNSIGNED;
    DECLARE BEGINTIME TIMESTAMP;
        DECLARE ENDTIME INT UNSIGNED;
        DECLARE PARTITIONNAME VARCHAR(16);
        SET BEGINTIME = DATE(NOW()) + INTERVAL 1 DAY;
        SET PARTITIONNAME = DATE_FORMAT( BEGINTIME, 'p%Y_%m_%d' );
 
        SET ENDTIME = UNIX_TIMESTAMP(BEGINTIME + INTERVAL 1 DAY);
 
        SELECT COUNT(*) INTO ROWS_CNT
                FROM information_schema.partitions
                WHERE table_schema = IN_SCHEMANAME AND table_name = IN_TABLENAME AND partition_name = PARTITIONNAME;
 
    IF ROWS_CNT = 0 THEN
                     SET @SQL = CONCAT( 'ALTER TABLE `', IN_SCHEMANAME, '`.`', IN_TABLENAME, '`',
                                ' ADD PARTITION (PARTITION ', PARTITIONNAME, ' VALUES LESS THAN (', ENDTIME, '));' );
                PREPARE STMT FROM @SQL;
                EXECUTE STMT;
                DEALLOCATE PREPARE STMT;
        ELSE
        SELECT CONCAT("partition `", PARTITIONNAME, "` for table `",IN_SCHEMANAME, ".", IN_TABLENAME, "` already exists") AS result;
        END IF;
END$$
DELIMITER ;

DELIMITER $$
DROP PROCEDURE IF EXISTS `create_partition_by_month`$$
CREATE PROCEDURE `create_partition_by_month`(IN_SCHEMANAME VARCHAR(64), IN_TABLENAME VARCHAR(64))
BEGIN
    DECLARE ROWS_CNT INT UNSIGNED;
    DECLARE BEGINTIME TIMESTAMP;
        DECLARE ENDTIME INT UNSIGNED;
        DECLARE PARTITIONNAME VARCHAR(16);
        SET BEGINTIME = DATE(NOW() - INTERVAL DAY(NOW()) DAY + INTERVAL 1 DAY + INTERVAL 1 MONTH);
        SET PARTITIONNAME = DATE_FORMAT( BEGINTIME, 'p%Y_%m' );
 
        SET ENDTIME = UNIX_TIMESTAMP(BEGINTIME + INTERVAL 1 MONTH);
 
        SELECT COUNT(*) INTO ROWS_CNT
                FROM information_schema.partitions
                WHERE table_schema = IN_SCHEMANAME AND table_name = IN_TABLENAME AND partition_name = PARTITIONNAME;
 
    IF ROWS_CNT = 0 THEN
                     SET @SQL = CONCAT( 'ALTER TABLE `', IN_SCHEMANAME, '`.`', IN_TABLENAME, '`',
                                ' ADD PARTITION (PARTITION ', PARTITIONNAME, ' VALUES LESS THAN (', ENDTIME, '));' );
                PREPARE STMT FROM @SQL;
                EXECUTE STMT;
                DEALLOCATE PREPARE STMT;
        ELSE
        SELECT CONCAT("partition `", PARTITIONNAME, "` for table `",IN_SCHEMANAME, ".", IN_TABLENAME, "` already exists") AS result;
        END IF;
END$$
DELIMITER ;


DELIMITER $$
DROP PROCEDURE IF EXISTS `drop_partitions`$$
CREATE PROCEDURE `drop_partitions`(IN_SCHEMANAME VARCHAR(64))
BEGIN
    DECLARE TABLENAME_TMP VARCHAR(64);
    DECLARE PARTITIONNAME_TMP VARCHAR(64);
    DECLARE VALUES_LESS_TMP INT;
    DECLARE PERIOD_TMP VARCHAR(12);
    DECLARE KEEP_HISTORY_TMP INT;
    DECLARE KEEP_HISTORY_BEFORE INT;
    DECLARE DONE INT DEFAULT 0;
    DECLARE get_partitions CURSOR FOR
        SELECT p.`table_name`, p.`partition_name`, LTRIM(RTRIM(p.`partition_description`)), mp.`period`, mp.`keep_history`
            FROM information_schema.partitions p
            JOIN manage_partitions mp ON mp.tablename = p.table_name
            WHERE p.table_schema = IN_SCHEMANAME
            ORDER BY p.table_name, p.subpartition_ordinal_position;
 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
 
    OPEN get_partitions;
 
    loop_check_prt: LOOP
        IF DONE THEN
            LEAVE loop_check_prt;
        END IF;
 
        FETCH get_partitions INTO TABLENAME_TMP, PARTITIONNAME_TMP, VALUES_LESS_TMP, PERIOD_TMP, KEEP_HISTORY_TMP;
        CASE WHEN PERIOD_TMP = 'day' THEN
                SET KEEP_HISTORY_BEFORE = UNIX_TIMESTAMP(DATE(NOW() - INTERVAL KEEP_HISTORY_TMP DAY));
             WHEN PERIOD_TMP = 'month' THEN
                SET KEEP_HISTORY_BEFORE = UNIX_TIMESTAMP(DATE(NOW() - INTERVAL KEEP_HISTORY_TMP MONTH - INTERVAL DAY(NOW())-1 DAY));
             ELSE
            BEGIN
                ITERATE loop_check_prt;
            END;
        END CASE;
 
        IF KEEP_HISTORY_BEFORE >= VALUES_LESS_TMP THEN
                CALL drop_old_partition(IN_SCHEMANAME, TABLENAME_TMP, PARTITIONNAME_TMP);
        END IF;
        END LOOP loop_check_prt;
 
        CLOSE get_partitions;
END$$
DELIMITER ;

DELIMITER $$
DROP PROCEDURE IF EXISTS `drop_old_partition`$$
CREATE PROCEDURE `drop_old_partition`(IN_SCHEMANAME VARCHAR(64), IN_TABLENAME VARCHAR(64), IN_PARTITIONNAME VARCHAR(64))
BEGIN
    DECLARE ROWS_CNT INT UNSIGNED;
 
        SELECT COUNT(*) INTO ROWS_CNT
                FROM information_schema.partitions
                WHERE table_schema = IN_SCHEMANAME AND table_name = IN_TABLENAME AND partition_name = IN_PARTITIONNAME;
 
    IF ROWS_CNT = 1 THEN
                     SET @SQL = CONCAT( 'ALTER TABLE `', IN_SCHEMANAME, '`.`', IN_TABLENAME, '`',
                                ' DROP PARTITION ', IN_PARTITIONNAME, ';' );
                PREPARE STMT FROM @SQL;
                EXECUTE STMT;
                DEALLOCATE PREPARE STMT;
        ELSE
        SELECT CONCAT("partition `", IN_PARTITIONNAME, "` for table `", IN_SCHEMANAME, ".", IN_TABLENAME, "` not exists") AS result;
        END IF;
END$$
DELIMITER ;


DELIMITER $$
CREATE EVENT IF NOT EXISTS `e_part_manage`
       ON SCHEDULE EVERY 1 DAY
       STARTS '2016-01-01 21:30:00'
       ON COMPLETION PRESERVE
       ENABLE
       COMMENT 'Creating and dropping partitions'
       DO BEGIN
            CALL drop_partitions('zabbixdb');
            CALL create_next_partitions('zabbixdb');
       END$$
DELIMITER ; 
----------------结束-------------------

【确认分区自动添加成功】
-----------------开始-----------------------
mysql> select * from manage_partitions;   
+--------------+--------+--------------+---------------------+----------+
| tablename    | period | keep_history | last_updated        | comments |
+--------------+--------+--------------+---------------------+----------+
| history      | day    |           30 | 2016-02-15 21:30:00 |          |
| history_log  | day    |          120 | 2016-02-15 21:30:00 |          |
| history_str  | day    |          120 | 2016-02-15 21:30:00 |          |
| history_text | day    |          120 | 2016-02-15 21:30:00 |          |
| history_uint | day    |           30 | 2016-02-15 21:30:00 |          |
| trends       | month  |           24 | 2016-02-15 21:30:00 |          |
| trends_uint  | month  |           24 | 2016-02-15 21:30:00 |          |
+--------------+--------+--------------+---------------------+----------+
7 rows in set (0.00 sec)

mysql> show create table history;
......
(PARTITION p2016_02_13 VALUES LESS THAN (1455339600) ENGINE = InnoDB,
 PARTITION p2016_02_14 VALUES LESS THAN (1455426000) ENGINE = InnoDB,
 PARTITION p2016_02_16 VALUES LESS THAN (1455685200) ENGINE = InnoDB)
----------------结束-------------------

参考文档:
http://www.unixmen.com/how-to-install-zabbix-server-on-centos-7/
https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-centos-7
http://www.itzgeek.com/how-tos/linux/centos-how-tos/nginx-php-fpm-mariadb-on-centos-7-rhel-7.html
http://www.unixmen.com/install-lamp-server-apache-mariadb-php-centosrhelscientific-linux-7/
https://www.zabbix.org/wiki/Docs/howto/high_availability_Zabbix_Frontend
https://www.scalescale.com/tips/nginx/install-php-zend-opcache-cache-web-stats-nginx/
http://eddmann.com/posts/storing-php-sessions-file-caches-in-memory-using-tmpfs/
https://www.webfoobar.com/node/31
http://www.server-world.info/en/note?os=CentOS_7&p=zabbix
http://8838848.blog.51cto.com/8828848/1583960
https://bignosekates.wordpress.com/2013/05/07/zabbix-server-is-not-running/
http://www.jincon.com/archives/169/
http://www.cnblogs.com/yyhh/p/4792830.html
https://support.zabbix.com/browse/ZBX-9063
http://php.net/manual/en/opcache.installation.php
https://zabbix.org/wiki/Docs/howto/mysql_partitioning
http://zabbixzone.com/zabbix/history-tables-housekeeper/

 

根据MAC反查交换机端口的思路

$
0
0
公司CMDB系统有个需求,在已知MAC的情况下,找出该MAC所在的交换机端口。
简单的思路如下,用程序实现即可。

STEP 1. 获取当前交换机的MAC地址表(BRIDGE-MIB提供),dot1dTpFdbPort
# snmpwalk -v2c -c public 10.10.10.1 .1.3.6.1.2.1.17.4.3.1.2
SNMPv2-SMI::mib-2.17.4.3.1.2.0.0.12.7.172.32 = INTEGER: 51
SNMPv2-SMI::mib-2.17.4.3.1.2.0.0.12.7.172.33 = INTEGER: 51
SNMPv2-SMI::mib-2.17.4.3.1.2.0.5.152.17.81.16 = INTEGER: 39
…..
取到的内容说明:
SNMPv2-SMI::mib-2.17.4.3.1.2.是dot1dTpFdbPort前缀
0.0.12.7.172.32是MAC地址的10进制格式,对应16进制应该为   00:00:0C:07:AC:20
51是端口索引号, dot1dBasePort的值

STEP 2. 将端口索引转为接口编号
# snmpwalk -v2c -c public 10.10.10.1 .1.3.6.1.2.1.17.1.4.1.2
SNMPv2-SMI::mib-2.17.1.4.1.2.1 = INTEGER: 10101
SNMPv2-SMI::mib-2.17.1.4.1.2.2 = INTEGER: 10102
…….
SNMPv2-SMI::mib-2.17.1.4.1.2.51 = INTEGER: 10047
SNMPv2-SMI::mib-2.17.1.4.1.2.52 = INTEGER: 10048

可以看到SNMPv2-SMI::mib-2.17.1.4.1.2.51 = INTEGER: 10047 ,51接口对应的接口编号是10047

STEP 3. 将接口编号转换为可读的接口名字
# snmpget -v2c -c public 10.10.10.1 ifName.10047
IF-MIB::ifName.10047 = STRING: Fa0/47

得到10047接口的名字是Fa0/47

以上结果可以这么解读:  MAC地址00:00:0C:07:AC:20,是从交换机10.10.10.1的,Fa0/47端口上学习到的。

参考文档:
http://networkengineering.stackexchange.com/questions/2900/using-snmp-to-retrieve-the-arp-and-mac-address-tables-from-a-switch
http://serverfault.com/questions/441072/how-do-i-poll-the-arp-table-with-an-snmp-oid
http://networkengineering.stackexchange.com/questions/2990/translating-snmpwalk-output-into-human-readable-format

 

用nginx架设webdav服务器

$
0
0
业务部门的新需求,要和国外同事交换文件,原计划使用ftp,但使用sureroute加速有问题,因此改用webdav方式。没啥可说的,流水账记录一下。

1.安装nginx源码包,nginx-dav-ext-module源码
]# useradd builder
]# rpm -ivh http://nginx.org/packages/centos/7/SRPMS/nginx-1.8.1-1.el7.ngx.src.rpm
]# curl -O https://codeload.github.com/arut/nginx-dav-ext-module/zip/master
]# unzip nginx-dav-ext-module-master.zip
]# mv nginx-dav-ext-module-master /root/rpmbuild/BUILD/nginx-dav-ext-module

2.修改nginx的SPEC文件
]# cd /root/rpmbuild/SPECS
]# patch -p0 < nginx-dav-ext.patch

3.创建nginx的rpm包
]# rpmbuild -bb nginx.spec

4.安装新生成的rpm包
]# yum localinstall /root/rpmbuild/RPMS/nginx-1.8.1-1.el7.centos.ngx.x86_64.rpm

5.创建webdav的配置文件
]# vi /etc/nginx/conf.d/webdav.conf 
server {
    listen       8080;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        root /home/swapzone;
        autoindex on;
        dav_methods PUT DELETE MKCOL COPY MOVE;
        dav_ext_methods PROPFIND OPTIONS;
        create_full_put_path on;
        dav_access user:rw group:r all:r;
        auth_basic "Authorized Users Only";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

修改最大允许上传得文件大小
]# vi /etc/nginx/nginx.conf
===> client_max_body_size 20G;

6.安装httpd-tools,设置用户认证
]# yum install httpd-tools
]# htpasswd -c /etc/nginx/.htpasswd us_user
]# htpasswd /etc/nginx/.htpasswd en_user

7.删除nginx包中默认配置文件
]# cd /etc/nginx/conf.d/
]# rm -f default.conf example_ssl.conf

8.启动nginx
]# systemctl start nginx
]# systemctl enable nginx

9.安装cyberduck客户端,即可开始使用。

参考文档:
1.http://www.agoragames.com/blog/2009/03/20/webdav-nginx-play-nice/
2.https://github.com/arut/nginx-dav-ext-module/
3.http://www.cnblogs.com/dtrees/archive/2013/02/07/2908833.html
4.https://opensource.ncsa.illinois.edu/confluence/display/ERGO/Creating+a+WebDAV+repository+server+with+NGINX
5.https://letitknow.wordpress.com/2012/02/29/simple-private-cloud-storage-using-webdav/
6.https://www.digitalocean.com/community/tutorials/how-to-configure-webdav-access-with-apache-on-ubuntu-12-04
7.http://moongazer.me/archives/81
8.https://www.digitalocean.com/community/tutorials/how-to-set-up-basic-http-authentication-with-nginx-on-centos-7
9.https://blog.linuxeye.com/333.html


附录: nginx-dav-ext.patch:
]# diff -uN nginx.spec.orig nginx.spec > nginx-dav-ext.patch
--- nginx.spec.orig     2016-03-08 10:52:18.343030724 +0800
+++ nginx.spec  2016-03-08 11:12:37.193607613 +0800
@@ -121,62 +121,17 @@
         --with-http_realip_module \
         --with-http_addition_module \
         --with-http_sub_module \
-        --with-http_dav_module \
-        --with-http_flv_module \
-        --with-http_mp4_module \
+        --with-http_dav_module --add-module=../nginx-dav-ext-module\
         --with-http_gunzip_module \
         --with-http_gzip_static_module \
         --with-http_random_index_module \
         --with-http_secure_link_module \
         --with-http_stub_status_module \
         --with-http_auth_request_module \
-        --with-mail \
-        --with-mail_ssl_module \
         --with-file-aio \
-        --with-ipv6 \
-        --with-debug \
         %{?with_spdy:--with-http_spdy_module} \
         --with-cc-opt="%{optflags} $(pcre-config --cflags)" \
         $*
-make %{?_smp_mflags}
-%{__mv} %{_builddir}/%{name}-%{version}/objs/nginx \
-        %{_builddir}/%{name}-%{version}/objs/nginx.debug
-./configure \
-        --prefix=%{_sysconfdir}/nginx \
-        --sbin-path=%{_sbindir}/nginx \
-        --conf-path=%{_sysconfdir}/nginx/nginx.conf \
-        --error-log-path=%{_localstatedir}/log/nginx/error.log \
-        --http-log-path=%{_localstatedir}/log/nginx/access.log \
-        --pid-path=%{_localstatedir}/run/nginx.pid \
-        --lock-path=%{_localstatedir}/run/nginx.lock \
-        --http-client-body-temp-path=%{_localstatedir}/cache/nginx/client_temp \
-        --http-proxy-temp-path=%{_localstatedir}/cache/nginx/proxy_temp \
-        --http-fastcgi-temp-path=%{_localstatedir}/cache/nginx/fastcgi_temp \
-        --http-uwsgi-temp-path=%{_localstatedir}/cache/nginx/uwsgi_temp \
-        --http-scgi-temp-path=%{_localstatedir}/cache/nginx/scgi_temp \
-        --user=%{nginx_user} \
-        --group=%{nginx_group} \
-        --with-http_ssl_module \
-        --with-http_realip_module \
-        --with-http_addition_module \
-        --with-http_sub_module \
-        --with-http_dav_module \
-        --with-http_flv_module \
-        --with-http_mp4_module \
-        --with-http_gunzip_module \
-        --with-http_gzip_static_module \
-        --with-http_random_index_module \
-        --with-http_secure_link_module \
-        --with-http_stub_status_module \
-        --with-http_auth_request_module \
-        --with-mail \
-        --with-mail_ssl_module \
-        --with-file-aio \
-        --with-ipv6 \
-        %{?with_spdy:--with-http_spdy_module} \
-        --with-cc-opt="%{optflags} $(pcre-config --cflags)" \
-        $*
-make %{?_smp_mflags}
 
 %install
 %{__rm} -rf $RPM_BUILD_ROOT
@@ -235,9 +190,6 @@
    $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/nginx
 %endif
 
-%{__install} -m644 %{_builddir}/%{name}-%{version}/objs/nginx.debug \
-   $RPM_BUILD_ROOT%{_sbindir}/nginx.debug
-
 %clean
 %{__rm} -rf $RPM_BUILD_ROOT
 
@@ -277,12 +229,10 @@
 %attr(0755,root,root) %dir %{_localstatedir}/cache/nginx
 %attr(0755,root,root) %dir %{_localstatedir}/log/nginx
 
-%files debug
-%attr(0755,root,root) %{_sbindir}/nginx.debug
-
 %pre
 # Add the "nginx" user
 getent group %{nginx_group} >/dev/null || groupadd -r %{nginx_group}
+getent group %{nginx_loggroup} >/dev/null || groupadd -r %{nginx_loggroup}
 getent passwd %{nginx_user} >/dev/null || \
     useradd -r -g %{nginx_group} -s /sbin/nologin \
     -d %{nginx_home} -c "nginx user"  %{nginx_user}

 

squid流水账

$
0
0
1、安装quid和squid-helpers

]# vi /etc/yum.repos.d/squid.repo
[squid]
name=Squid repo for CentOS Linux - $basearch
#IL mirror
baseurl=http://www1.ngtech.co.il/repo/centos/$releasever/$basearch/
failovermethod=priority
enabled=1
gpgcheck=0

]# yum install squid squid-helpers

2、配置squid

#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
# =======================

# 禁止缓存任何内容,纯代理
cache deny all

# 禁用access_log
access_log none

# 禁用cache_store log
cache_store_log none

# 禁用cache log
cache_log /dev/null 

# 使用basic_nsca_auth认证
auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid/passwd
auth_param basic children 5
auth_param basic credentialsttl 2 hours
auth_param basic realm Do you have rights to access?
auth_param basic casesensitive off
acl ncsa_users proxy_auth REQUIRED
http_access allow localnet
http_access allow localhost
http_access allow ncsa_users

以上配置部分要在http_access deny all语句之前。
这里使用了nsca认证,密码长度不能超过8位。
如果需要更复杂的认证,可以使用HTTP Digest方法,见参考文档10。

# 这部分可以放在配置文件末尾,避免被网站检测到你在使用proxy
# =======================
via off
forwarded_for off
request_header_access Allow allow all 
request_header_access Authorization allow all 
request_header_access WWW-Authenticate allow all 
request_header_access Proxy-Authorization allow all 
request_header_access Proxy-Authenticate allow all 
request_header_access Cache-Control allow all 
request_header_access Content-Encoding allow all 
request_header_access Content-Length allow all 
request_header_access Content-Type allow all 
request_header_access Date allow all 
request_header_access Expires allow all 
request_header_access Host allow all 
request_header_access If-Modified-Since allow all 
request_header_access Last-Modified allow all 
request_header_access Location allow all 
request_header_access Pragma allow all 
request_header_access Accept allow all 
request_header_access Accept-Charset allow all 
request_header_access Accept-Encoding allow all 
request_header_access Accept-Language allow all 
request_header_access Content-Language allow all 
request_header_access Mime-Version allow all 
request_header_access Retry-After allow all 
request_header_access Title allow all 
request_header_access Connection allow all 
request_header_access Proxy-Connection allow all 
request_header_access User-Agent allow all 
request_header_access Cookie allow all 
request_header_access All deny all
# =======================

参考文档:
1.https://www.digitalocean.com/community/tutorials/how-to-install-squid-proxy-on-centos-6
2.http://wiki.squid-cache.org/SquidFaq/BinaryPackages
3.http://wiki.squid-cache.org/SquidFaq/SquidLogs
4.http://wiki.squid-cache.org/Features/Authentication
5.http://idroot.net/tutorials/install-configure-squid-3-centos/
6.http://www.cyberciti.biz/tips/linux-unix-squid-proxy-server-authentication.html
7.http://serverfault.com/questions/358754/how-to-make-squid-work-like-proxy-only-without-caching-anything
8.http://blog.faq-book.com/?p=5334
9.http://www.squid-cache.org/mail-archive/squid-users/200603/0418.html
10.https://blog.mafr.de/2013/06/16/setting-up-a-web-proxy-with-squid/
11.http://www.techrepublic.com/article/run-configure/

 

如何使用snmp计算接口带宽

$
0
0
作为运维的日常工作,看监控是我们每天都做的事情。突然有点好奇,zabbix,cacti这类软件通过snmp监控流量计算的方法依据是什么呢?于是google一番,得到一些结论。

首先 ,交换机提供的snmp信息中并没有直接的端口带宽,而只有端口上的流量数据。

以下是Cisco在其IF-MIB文件中对于端口接收(IN)流量的定义:
ifHCInOctets OBJECT-TYPE
    SYNTAX      Counter64
    MAX-ACCESS  read-only
    STATUS      current
    DESCRIPTION
            "The total number of octets received on the interface,
            including framing characters.  This object is a 64-bit
            version of ifInOctets.

            Discontinuities in the value of this counter can occur at
            re-initialization of the management system, and at other
            times as indicated by the value of
            ifCounterDiscontinuityTime."
    ::= { ifXEntry 6 }

可以看到,这个ifHCInOctets取到的值是"端口收到的octets的总数"。

那么RFC中的Octet这量词是什么呢?继续google,发现octet是指8个二进制位(bit),那么8bits不就是一个Byte吗?为啥不用Byte这个大众化量词呢?

区别是:
Octet 总是表达8个bit,就像他名字所说的。
Byte 通常情况下也表示8个bit,但是准确的说,Byte表示的是CPU可以独立的寻址的最小内存单位(不过通过移位和逻辑运算,CPU也可以寻址到某一个单独的bit)。
曾几何时,有些电脑的寻址单位不是8Bit的。当然,当前来说大多数情况下byte可以和octet等价,但是如果你想要强调准确的8bit时,你就应该使用octet而不是byte。

另外,还有一个量词 nibble用来形容一个字节的一半 (通常是4bit)不过这个词基本上没人用就是了。

然后又google到了Cisco的一篇文档,说明了如何通过端口的流量来计算带宽。
具体可以看参考文档,直接说结论,对于全双工网络建议将IN和OUT流量分开计算,公式如下:

也就是前后取两次接口的流量值相减,然后乘以8,再除以两次取值的时间间隔(以秒为单位),这样得到的是bits/s
可以再除1024的平方,变为Mbits/s。

但是上边的公式并不完善,为什么呢?因为ifHCInOctets是个64bits的计数器,尽管这个数字已经很大了,但在长期使用以后还是会达到最大值,接着计数器会回环,直接清零。那么问题来了,如果刚好前一次和后一次取值的中间,遇到了计数器清零了呢?后一次减一次的值就会是负数。那么这种问题应该怎么处理呢?请参考如下代码:


以上就是如何通过snmp计算接口带宽的方法啦,打完收工。

参考文档:
1.http://www.cisco.com/c/en/us/support/docs/ip/simple-network-management-protocol-snmp/8141-calculate-bandwidth-snmp.html
2.http://blog.chasefox.net/calculating-ifinoctets/
3.ftp://ftp.cisco.com/pub/mibs/v2/IF-MIB.my
4.http://blog.icybear.net/2009/09/octets-vs-bytes.html
5.http://www.cisco.com/c/en/us/support/docs/ip/simple-network-management-protocol-snmp/26007-faq-snmpcounter.html

 

使用snmp监控haproxy的性能指标

$
0
0
haproxy本身提供了很好的stats页面来展示其性能指标,不过我们需要将这些集中到zabbix监控中去。
可以通过自定义key的方式,也可以通过snmp的方式。
就部署来说,还是使用snmp方式更容易一些。

haproxy的官方源码中包含了社区贡献者Krzysztof Piotr Oledzki提供的haproxy.pl脚本。
这个脚本可以通过读取haproxy socket的方式,将对应指标转换成snmp的OID节点。
脚本支持GET命令(snmpget)和GETNEXT命令(snmpwalk),正式监控推荐使用GET命令。
因为GET非常快,而GETNEXT则比较慢。

下面流水账一下安装步骤:

1. 安装snmp相关软件
]# yum install net-snmp net-snmp-perl net-snmp-utils

2. 安装haproxy.pl脚本
]# wget http://www.haproxy.org/download/contrib/netsnmp-perl/haproxy.pl
]# cp haproxy.pl /etc/snmp/
]# grep -q "disablePerl false" /etc/snmp/snmpd.conf || echo "disablePerl false" >> /etc/snmp/snmpd.conf
]# echo "perl do '/etc/snmp/haproxy.pl';" >> /etc/snmp/snmpd.conf
]# vi /etc/snmp/snmpd.conf
加入如下一行:
===> view    systemview    included   .1.3.6.1.4.1.29385.106
]# /etc/init.d/snmpd start

3. haproxy.pl给出的snmp信息
haproxy可以输出很多统计信息,例如info,stats,errors,sess等,我们比较关注如下两种:
一种是stats,包含了frontend,backend,server相关的各种指标计数器值,例如某个frontend上的连接数等。
一种是info,包含了haproxy进程本身的一些相关信息,例如haproxy版本号,PID,当前连接数, session rates等。
haproxy.pl支持种OID,分别对应stats和info。
具体每个指标的含义,请参考官方文档的9.1章节。

- 1.3.6.1.4.1.29385.106.1: 从stats中获取一个变量
   使用方法: 1.3.6.1.4.1.29385.106.1.$type.$field.$iid.$sid

    - type为下列值之一:
      0) frontend
      1) backend
      2) server

    - field为下列值之一:
      0..32) CSV格式变量
      10001) index
      10002) unique name

    - iid 是proxy id 

    - sid 是service id (sid): 0是frontends和backends, >= 1对应的是server

- 1.3.6.1.4.1.29385.106.2: 从info中获取一个变量
   使用方法: 1.3.6.1.4.1.29385.106.2.$req.$varnr

     - req为下列值之一:
       0) 获取变量名
       1) 获取变量值

  
应用举例:

- 获取fontend列表(type: 0),并带有状态信息 (field: 17):
$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.1.0.17
SNMPv2-SMI::enterprises.29385.106.1.0.17.1.0 = STRING: "OPEN"
SNMPv2-SMI::enterprises.29385.106.1.0.17.47.0 = STRING: "OPEN"

- 获取backend列表(type: 1),并带有index (field: 10001):
$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.1.1.10001
SNMPv2-SMI::enterprises.29385.106.1.1.10001.1.0 = STRING: "1.0"
SNMPv2-SMI::enterprises.29385.106.1.1.10001.1100.0 = STRING: "1100.0"
SNMPv2-SMI::enterprises.29385.106.1.1.10001.1101.0 = STRING: "1101.0"
SNMPv2-SMI::enterprises.29385.106.1.1.10001.1200.0 = STRING: "1200.0"
SNMPv2-SMI::enterprises.29385.106.1.1.10001.1201.0 = STRING: "1201.0"
SNMPv2-SMI::enterprises.29385.106.1.1.10001.1300.0 = STRING: "1300.0"
SNMPv2-SMI::enterprises.29385.106.1.1.10001.1400.0 = STRING: "1400.0"
SNMPv2-SMI::enterprises.29385.106.1.1.10001.1401.0 = STRING: "1401.0"
SNMPv2-SMI::enterprises.29385.106.1.1.10001.1500.0 = STRING: "1500.0"
(...)

- 获取所有的backend server列表(type: 2),并带有unique name (field: 10002):
$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.1.2.10002
SNMPv2-SMI::enterprises.29385.106.1.2.10002.1100.1001 = STRING: "backend1/s2"
SNMPv2-SMI::enterprises.29385.106.1.2.10002.1100.1002 = STRING: "backend1/s5"
SNMPv2-SMI::enterprises.29385.106.1.2.10002.1100.1003 = STRING: "backend1/s6"
SNMPv2-SMI::enterprises.29385.106.1.2.10002.1101.1001 = STRING: "backend2/s9"
SNMPv2-SMI::enterprises.29385.106.1.2.10002.1101.1002 = STRING: "backend2/s10"
SNMPv2-SMI::enterprises.29385.106.1.2.10002.1200.1001 = STRING: "backend3/s8"
(...)

- 获取proxy 4300的backend server列表(type: 2),并带有weight值 (field: 18):
$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.1.2.18.4300
SNMPv2-SMI::enterprises.29385.106.1.2.18.4300.1001 = STRING: "40"
SNMPv2-SMI::enterprises.29385.106.1.2.18.4300.1002 = STRING: "25"
SNMPv2-SMI::enterprises.29385.106.1.2.18.4300.1003 = STRING: "40"
SNMPv2-SMI::enterprises.29385.106.1.2.18.4300.1012 = STRING: "80"

- 获取所有索引为47的proxy[sid.iid: 47.0 (proxy #47)]的fronted(type: 1)的session的计数器(field: 7), :
snmpget -c public -v2c 192.168.0.1 enterprises.29385.106.1.0.7.47.0
SNMPv2-SMI::enterprises.29385.106.1.0.7.47.0 = STRING: "1014019"

- 获取所有可用变量列表(req: 0):
$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.2.0
SNMPv2-SMI::enterprises.29385.106.2.0.0 = STRING: "Name"
SNMPv2-SMI::enterprises.29385.106.2.0.1 = STRING: "Version"
SNMPv2-SMI::enterprises.29385.106.2.0.2 = STRING: "Release_date"
SNMPv2-SMI::enterprises.29385.106.2.0.3 = STRING: "Nbproc"
SNMPv2-SMI::enterprises.29385.106.2.0.4 = STRING: "Process_num"
SNMPv2-SMI::enterprises.29385.106.2.0.5 = STRING: "Pid"
SNMPv2-SMI::enterprises.29385.106.2.0.6 = STRING: "Uptime"
SNMPv2-SMI::enterprises.29385.106.2.0.7 = STRING: "Uptime_sec"
SNMPv2-SMI::enterprises.29385.106.2.0.8 = STRING: "Memmax_MB"
SNMPv2-SMI::enterprises.29385.106.2.0.9 = STRING: "Ulimit-n"
SNMPv2-SMI::enterprises.29385.106.2.0.10 = STRING: "Maxsock"
SNMPv2-SMI::enterprises.29385.106.2.0.11 = STRING: "Maxconn"
SNMPv2-SMI::enterprises.29385.106.2.0.12 = STRING: "CurrConns"

- 获取变量Uptime_sec的值(varnr: 7)的值(req: 1):
$ snmpget -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.2.1.7
SNMPv2-SMI::enterprises.29385.106.2.1.7 = STRING: "18761"   
 
参考文档:
https://fossies.org/linux/haproxy/contrib/netsnmp-perl/README
http://blog.chinaunix.net/uid-28718792-id-3598804.html
http://makandracards.com/makandra/36727-get-haproxy-stats-informations-via-socat
http://haproxy.tech-notes.net/9-2-unix-socket-commands/
http://www.liquidweb.com/kb/how-to-install-and-configure-snmp-on-centos/

 

使用nfsen+sflow分析网络流量

$
0
0
    netflow和sflow可以帮助我们分析网络中的流量构成,抓取实施带宽使用情况,帮我们找出"hog"。作为collector的软件一般都是收费的,例如solarwinds NTA,sflowTrend-Pro,ntopng商业版等。不过也有开源的解决方案,例如ntopng社区版和nfsen。
    下边来说说nfsen,首先流水账一下安装过程。

一、安装nfsen

1.1 安装nginx+php-fpm,细节不说,需要的可以看我之前的blog文章,网上其他人的好文章也大把。

1.2 安装nfdump,nfsen其实是nfdump的前端展示,真正收集数据的是nfdump。
]# yum install nfdump

1.3 安装nfsen
]# tar -zxvf nfsen-1.3.7.tar.gz
]# cd nfsen-1.3.7
]# cp etc/nfsen-dist.conf etc/nfsen.conf
]# vi /etc/nfsen.conf
===> $BASEDIR= "/data/nfsen";
===> $HTMLDIR = "/var/www/nfsen";
===> $WWWUSER  = "nginx";
===> $WWWGROUP = "nginx";
===> $PREFIX  = '/usr/bin';
===> %sources = (
===> 'H3C' => { 'port' => '8603', 'col' => '#0000ff', 'type' => 'sflow' },
===> );
]# mkdir -p /data/nfsen
]# useradd netflow -g nginx
]# yum install perl perl-Sys-Syslog rrdtool-perl perl-Data-Dumper perl-MailTools perl-Socket6
]# vi libexec/NfProfile.pm
===> no strict 'refs';
]# ./install.pl etc/nfsen.conf
]# cd /data/nfsen/bin
]# ./nfsen start

1.4 访问http://your_ip_address/nfsen/nfsen.php

二、nfsen界面介绍  [ 这部分转自台湾网友的文章,原文见参考文档9 ]

进入nfsen,在首页Home里,会显示flow/packet/流量三种类型的流量缩图,并分别以日/周/月/年四个层级来显示。


在Graphs里其实跟Home显示的功能是相同的,主要的差别在于三种类型的流量图挂在各自的页面里。
也因为如此,流量图能以正常尺寸显示,而不是Home里的缩小显示。


Details是主要的操作页面。
在Details的页面上方,将流量区分为TCP/UDP/ICMP与other四种protocol类型。
点了任何一种protocl,都会显示对应的流量于其下主要的大图里。
而flow/packet/流量三种类型的流量则可透过右侧的小流量图来进行切换。
下方Display旁的浏览列功能按钮,提供用户以鼠标拖拉时间轴外的另一种操作选择。
最下方的统计数据则显示了时间范围内三种类型流量的总和或速率。


Alerts页面可以设定告警机制。

但Nfsen提供的smtp组态设定相当有限,在配置文件nfsen.conf中只提供了以下两个参数,并没有其它细项设定。
$MAIL_FROM = 'your@from.example.net';
$SMTP_SERVER = 'localhost';

所以环境中的mail server需对Nfsen的IP进行open relay放行,才能让Nfsen成功发信;
不然就要在Nfsen上再起个MTA服务来实现发信机制。

Stats页面可对数据保留时与间大小等进行设定。流量图中的颜色调整也是在这个页面中进行喔~


plugins页面显示了Nfsen的插件,若有装PortTracker的话,将于此页面中显示。

在PortTracker中,可以看到它是针对TCP/UDP两种类型的flow/packet/流量三种方式绘制流量图,
可以在这一大五小的图中自行切换要查看的Protocol与流量。
并且于下方统计数据中显示各种Protocol的Port使用排名。

不过比较可惜的是它没有提供像Details页面的时间轴工具,
无法自定时间范围来查询该范围内的Port使用排名。
只能以现在的时间往前推12小时、一天、两天、四天、一周与两周等既定的时间范围来做查询,
这是比较可惜的地方...

三、nfsen抓"hog"实战示例 [ 这部分转自台湾网友的文章,原文见参考文档9 ]
登入nfsen网页,由Home或Graphs的Traffic查看有无问题流量。


找到要查询的流量后,选择“Time Windows”来设定要查询的流量区间。
选择“Time Windows”后,X轴的光标尺会分裂为左右两边。


先拉右半边游标尺到观察区间的右边。


再拉左半边光标尺到观察区间的左边,完成区间设定。


调整显示方式为“sum”,可在Traffic部分发现问题流量来自tcp协议,
足足有1.2G远远大于其它protocol



 

然后在Netflow Processing区块中,将Options改以“Stat TopN”的方式,做前几名大户排序。


发现第一名主机IP192.168.10.208



找到大户后,接下来要确认的是该用户是以发送端还是接收端在占用流量?
tcp protocolsrc ip 192.168.10.208的条件查询其流量



改以tcp protocol及dst ip 192.168.10.208的方式确认其流量。

 

proto tcp and dst ip 192.168.10.208

经过比对,做为发送端的流量只有9.2M,做为接收端的流量则有1.2G
很明显的192.168.10.208是做为接收端在下载流量。



改以List Flow来查看来源与目的间的关系。



发现192.168.10.208主要以80及443 port两种方式在下载数据,确认究竟是哪一port所造成的流量。

 

proto tcp and dst ip 192.168.10.208 and src port 80



proto tcp and dst ip 192.168.10.208 and src port 443


由流量来看,192.168.10.208是透过80 port在下载数据。但有很多80 port在下载,究竟是哪个来源IP呢?
把Flows打开到最大10000,查询所有联机。


发现是140.138.144.170这个来源IP。



原来是作者本人透过网页在Ubuntu网站下载iso档所造成,一切都是误会~但也成功透过Nfsen查询到占用带宽的凶手!Nfsen的基本操作示范到此,更深入的应用留给有兴趣的您继续研究啰~

四、PortTracker插件的安装流水账

]# cd nfsen-1.3.7
]# vi contrib/PortTracker/PortTracker.pm
===> my $PORTSDBDIR = "/data/ports-db";
]# cp PortTracker.pm /data/nfsen/plugins/
]# cp PortTracker.php /usr/share/nginx/html/nfsen/plugins/
]# vi etc/nfsen.conf 
===>    @plugins = (
===>        [ 'live',   'PortTracker'],
===>     ); 

每次对nfsen.conf做过改动以后,都需要重新执行一次install.pl
]# ./install.pl etc/nfsen.conf 

创建nftrack data目录,并初始化portracker database
]# mkdir -p /data/ports-db
]# nftrack -I -d ports-db/

修改nftrack data目录属主和权限,必须确认netflow用户和nginx用户能访问到。
]# chown -R netflow:nginx ports-db/
]# chmod -R 775 ports-db/

重新载入一下nfsen
]# cd /data/nfsen/bin
]# ./nfsen reload

在日志中确认PortTracker成功初始化
]# grep -i 'porttracker.*success' /var/log/syslog
Nov 27 02:46:13 noc nfsen[17312]: Loading plugin 'PortTracker': Success
Nov 27 02:46:13 noc nfsen[17312]: Initializing plugin 'PortTracker': Success

等待几分钟后,在nfsen的WEB GUI上访问plugins标签查看结果。
==================
参考文档:
1.http://tekyhost.com/building-centos-7-netflows-monitoring-station-with-nfsen-and-nfdump/
2.http://terraltech.com/installation-and-configuration-of-nfdump-and-nfsen-on-ubuntu/
3.https://support.cumulusnetworks.com/hc/en-us/articles/201787866-Configuring-and-Using-sFlow-Visualization-Tools
4.http://blog.sina.com.cn/s/blog_71261a2d0100zew1.html
5.https://code.google.com/p/renisac/wiki/NFSen_Introduction
6.https://www.forwardingplane.net/2014/01/install-nfsen-and-nfdump-on-centos-6-5-for-netflow-and-or-sflow-collection/
7.http://meetings.ripe.net/ripe-50/presentations/ripe50-plenary-tue-nfsen-nfdump.pdf
8.http://blog.gmane.org/gmane.network.nfsen.general/month=20100901
9.http://www.shunze.info/forum/thread.php?threadid=1953&boardid=3&sid=aadc298e695d7f799db0b872563884b3&sid=aadc298e695d7f799db0b872563884b3
10.http://perlmaven.com/symbolic-reference-in-perl

 

futex的bug导致MySQL故障

$
0
0
    公司有一批HP服务器,采用的是Intel XEON E5 v3的CPU,并且配置了FusionIO的SSD卡,在运行MySQL的时候经常出现MySQL hang住的情况。
    找了HP厂家,驱动,硬件换了一个溜够,故障依然存在。近日DBA部门经理去参加一个交流会议时从其他公司同行处得知,此乃一个内核的BUG导致的,尤其是在Intel haswells平台CPU上特别明显,具体情况见参考文档。这个BUG影响的范围如下:

RHEL 6 (and CentOS 6, and SL 6): 6.0-6.5 are good. 6.6 is BAD. 6.6.z is good.
RHEL 7 (and CentOS 7, and SL 7): 7.1 is BADAs of yesterday. there does not yet appear to be a 7.x fix.  [May 13, 2015]
RHEL 5 (and CentOS 5, and SL 5): All versions are good (including 5.11).

   经过确认,CentOS 6.7版本已经解决了这个问题,可以直接升级到6.7版即可。
可以查看CentOS 6.7内核源码,linux-2.6.32-573.el6/kernel/futex.c,224~230行。

    212 static void get_futex_key_refs(union futex_key *key)
    213 {
    214         if (!key->both.ptr)
    215                 return;
    216 
    217         switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) {
    218         case FUT_OFF_INODE:
    219                 atomic_inc(&key->shared.inode->i_count);
    220                 break;
    221         case FUT_OFF_MMSHARED:
    222                 futex_get_mm(key);
    223                 break;
    224         default:
    225                 --
    226                 -- * Private futexes do not hold reference on an inode or
    227                 -- * mm, therefore the only purpose of calling get_futex_key_refs
    228                 -- * is because we need the barrier for the lockless waiter check.
    229                 -- */
    230                 smp_mb();
    231         }
    232 }

参考文档:    
https://groups.google.com/forum/#!msg/mechanical-sympathy/QbmpZxp6C64/0M4_EbzSLj4J
https://www.infoq.com/news/2015/05/redhat-futex
https://github.com/torvalds/linux/commit/76835b0ebf8a7fe85beb03c75121419a7dec52f0


 

使用clumsy模拟"坏网络"

$
0
0
之前写过一篇使用netem模拟WAN延时,丢包等的博文,参见http://blog.sina.com.cn/s/blog_704836f40102vsuw.html。

但那个要求netem设备放在网络通路中间,对于QA来说,有时不好或不能实现。于是又继续研究,发现了一个更易用的模拟工具,clumsy。

clumsy运行在windows平台上, 其原理是利用WinDivert library将本机的数据包截住,经过一定的修改以后再发送出去,例如可以增加延时,模拟丢包,模拟TCP乱序等等。

clumsy有32位和64位两个版本,用法非常简单,下面简单说一下。

1.下载 clumsy    官网: https://jagt.github.io/clumsy/
2.运行clumsy
3.设置要捕获的数据包,语法与tcpdump类似
4.根据需要增加延时,丢包等设置即可。

延迟(Lag),把数据包缓存一段时间后再发出,这样能够模拟网络延迟的状况。
掉包(Drop),随机丢弃一些数据。
节流(Throttle),把一小段时间内的数据拦截下来后再在之后的同一时间一同发出去。
重发(Duplicate),随机复制一些数据并与其本身一同发送。
乱序(Out of order),打乱数据包发送的顺序。
篡改(Tamper),随机修改小部分的包裹内容。

下边这个图是GIF,官网提供的,点击可以看到动画。


参考文档:
http://xenthusiast.com/high-latency-network-connection-simulation-of-bad-ux/
http://www.oschina.net/p/clumsy

 

pptp的流水账

$
0
0
一、远端服务器操作

1.安装pptp服务

]# rpm -i http://poptop.sourceforge.net/yum/stable/rhel6/pptp-release-current.noarch.rpm
]# yum -y install pptpd


2.配置pptp服务

]# vi /etc/pptpd.conf
===> option /etc/ppp/options.pptpd
===> logwtmp
===> localip 远端IP
===> remoteip 172.16.111.101-201

]# vi /etc/ppp/options.pptpd
===> name pptpd
===> refuse-pap
===> refuse-chap
===> refuse-mschap
===> require-mschap-v2
===> require-mppe-128
===> proxyarp
===> lock
===> nobsdcomp
===> novj
===> novjccomp
===> nologfd
===> ms-dns 8.8.8.8
===> ms-dns 8.8.4.4
===> #debug
===> #dump

3.设定用户名和密码

]# vi etc/ppp/chap-secrets
===> VPNUSER pptpd VPNPASS *

4.开启NAT

]# vi /etc/sysctl.conf
===> net.ipv4.ip_forward = 1
]# sysctl -p
]# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

5.开启pptpd服务

]# chkconfig pptpd on
]# /etc/init.d/pptpd start

二、本地服务器操作

1.载入nf_nat_pptp和ip_gre模块

]# vi /etc/sysconfig/modules/nf_nat_pptp.modules 

===> #!/bin/sh
===> exec /sbin/modprobe ip_gre >/dev/null 2>&1
===> exec /sbin/modprobe nf_nat_pptp >/dev/null 2>&1

2.将TCP 1723和GRE包转到远端服务器

]# vi /etc/sysctl.conf
===> net.ipv4.ip_forward = 1
]# sysctl -p

]# iptables -A INPUT -p tcp -m tcp --dport 1723 -j ACCEPT 
]# iptables -A INPUT -p gre -j ACCEPT 

]# iptables -A PREROUTING -d 本地IP/32 -p tcp -m tcp --dport 1723 -j DNAT --to-destination 远端IP 
]# iptables -A POSTROUTING -d 远端IP/32 -p tcp -m tcp --dport 1723 -j MASQUERADE 

]# iptables -A PREROUTING -d 本地IP/32 -p gre -j DNAT --to-destination 远端IP 
]# iptables -A POSTROUTING -d 远端IP/32 -p gre -j MASQUERADE 

OK了,打完收工。

 

使用nginx-rtmp-module做推流服务器

$
0
0
公司视频部门的需求,要一个接收推流的服务器,据他们说之前用red5,好多需求不能实现,不知真假。既然需求到我这里了,就搜索研究了一下,发现nginx有rmtp模块,大概看了看,写个文档记录一下。

1. 用nginx官方srpm,重新编译nginx,加入nginx-rtmp-module,不用多说。
]# wget http://nginx.org/packages/centos/7/SRPMS/nginx-1.10.2-1.el7.ngx.src.rpm
]# rpm -ivh nginx-1.10.2-1.el7.ngx.src.rpm
]# cd rpmbuild/SOURCES
]# wget https://codeload.github.com/sergey-dryabzhinsky/nginx-rtmp-module/zip/master
]# unzip nginx-rtmp-module-master.zip
]# mv nginx-rtmp-module-master nginx-rtmp-module
]# tar zcvf nginx-rtmp-module.tar.gz nginx-rtmp-module

]# vi rpmbuild/SPECS/nginx.spec
==========......(略)
Source12: COPYRIGHT
增加====> Source13: nginx-rtmp-module.tar.gz
......(略)
%prep
%setup -q
增加====> %setup -T -D -b 13
......(略)
%build
......(略)
增加"\" ==>  --with-debug \
增加====>  --add-module=%{_builddir}/nginx-rtmp-module
make %{?_smp_mflags}
......(略)
增加"\" ==>  --with-debug \
增加====>  --add-module=%{_builddir}/nginx-rtmp-module
==========......(略)

]# rpmbuild -bb rpmbuild/SPECS/nginx.spec
]# rpm -ivh nginx-1.10.2-1.el7.centos.ngx.x86_64.rpm
]# systemctl enable nginx
]# systemctl start nginx

2. rtmp的配置

rtmp的URL基础格式 rtmp://nginx_host[:nginx_port]/application_name/stream_name

让我们来看看一个最简化的rtmp配置:

rtmp {
    server {
        listen 1935;
chunk_size 4096;
        application my-first-show {
            live on;
            record off;
        }
    }
}

rtmp,server,listen这几个指令的含义一目了然。
application定义了一个名为my-first-show的rtmp流
live on 打开了直播模式
record off 关闭录制模式,不将stream存储为flv文件

以上配置实现了一个最基本的可以接收stream的服务器,不做任何事,只是将接受到的stream转发出去。

让我们来做一个测试,使用obs。

打开OBS,创建一个profile,修改Broadcast Settings:

Streaming Service: Custom
Server: rtmp:///my-first-show
Play Path/Stream Key: test

也许你和我一样会疑惑"test"那里来的呢?呵呵,是我们刚刚创建的,在OBS中创建的,而不是在nginx配置中。
我们着一段影片,然后点击"Start Streaming",如果OBS没有报错,那么stream已经推到了我们的nginx服务器上了。

然后我们在其他PC上使用VLC 2.1.0以后版本的播放器,打开一个Network stream,输入rtmp URL:
rtmp:///my-first-show/test

点击play,如果出现了我们推上去的stream画面,就OK了。

3.rtmp统计信息查看

为了解当前服务器发送了或接收了多少数据,或当前运行了多少路stream,rtmp模块提供了一个stat页面。
不同于rtmp模块,这个stat功能需要放到http区段中定义。

]# cp nginx-rtmp-module/stat.xsl /path/to/stat/xsl/file/
]# vi /etc/nginx/nginx.conf

http {
    server {
        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }
        location /stat.xsl {
            root /path/to/stat/xsl/file;
        }
    }
}

打开浏览器,输入 http://your_host_ip/stat即可查看rtmp相关统计信息。

4.使用nginx-rtmp录制推送上来的流

application live1 {
live on;
record all;
record_append on;
record_path /path/to/storage;
    }

record all 指明audio和video都要录制
record_append  录制暂停后,继续推流会追加到之前的文件,中间不会有间隔
application live3 {
live on;
meta copy;
recorder audio {
record audio manual;
record_suffix .audio.flv;
record_path D:\Capture\audio;
}

recorder chunked {
record all;
record_interval 15s;
record_suffix -%Y-%m-%d-%H%M.flv;
record_path /path/to/storage;
}
}

live3这个例子是分开录制音频和每隔15秒生成一个新文件。

更多关于认证,安全连接什么的,我没再细研究了,诸位看官可以自行看ngixn-rtmp-module的文档即可。
参考文档:
https://github.com/sergey-dryabzhinsky/nginx-rtmp-module/blob/master/doc/directives.md
https://www.tekovic.com/adding-custom-modules-to-nginx-rpm
https://helping-squad.com/nginx-rtmp-how-to-use-secure-links/
https://helping-squad.com/nginx-rtmp-setup-rtmp-statistics/
https://helping-squad.com/setup-a-recorder-or-recordings-with-nginx/
https://obsproject.com/forum/resources/how-to-set-up-your-own-private-rtmp-server-using-nginx.50/
https://www.leaseweb.com/labs/2013/11/streaming-video-demand-nginx-rtmp-module/
https://docs.peer5.com/guides/setting-up-hls-live-streaming-server-using-nginx/
https://www.vultr.com/docs/setup-nginx-rtmp-on-ubuntu-14-04


 

使用tinc构建full mesh结构的VPN

$
0
0
    以前做VPN都是用openvpn来做,当点数多于两个时,需要做星型结构才能让所有点互通。这会产生一个问题,例如同一省的两个城市分公司之间通信,需要绕道核心点,例如北京的VPN服务器,无端增加了非常大的延时。
    为了解决这种需求,发现TINC这个VPN软件可以做到。

TINC是开源软件,官网https://www.tinc-vpn.org.

下边举个例子,说明一下full mesh结构的tinc VPN如何配置。

三个点,北京,柏林,拉斯维加斯,后边分别带本地的一个局域网络,要求3个点组成Full mesh结构。 

下边开始流水账:

TINC在Centos的EPEL源中有,yum安装即可。

1.[北京]
Beijing]# yum install tinc
Beijing]# mkdir -p /etc/tinc/netname/hosts
Beijing]# vi /etc/tinc/netname/tinc.conf

==> Name = Beijing
==> AddressFamily = ipv4
==> Interface = tun0
==> ConnectTo = Lasvegas
==> ConnectTo = Berlin

Beijing]# vi /etc/tinc/netname/hosts/Beijing
==> Address = Beijing_public_IP
==> Subnet = 10.0.0.1/32
==> Subnet = 172.16.3.0/24

Beijing]# tincd -n netname -K4096

Beijing]# vi /etc/tinc/netname/tinc-up
==> #!/bin/sh
==> ip link set $INTERFACE up
==> ip addr add 10.0.0.1/24 dev $INTERFACE
==> ip route add 192.168.133.0/24 dev $INTERFACE
==> ip route add 192.168.184.0/24 dev $INTERFACE

Beijing]# vi /etc/tinc/netname/tinc-down
==> #!/bin/sh
==> ip link set $INTERFACE down

Beijing]# chmod 755 /etc/tinc/netname/tinc-*


2.[拉斯维加斯]
Lasvegas]# yum install tinc
Lasvegas]# mkdir -p /etc/tinc/netname/hosts
Lasvegas]# vi /etc/tinc/netname/tinc.conf

==> Name = Lasvegas
==> AddressFamily = ipv4
==> Interface = tun0
==> ConnectTo = Beijing
==> ConnectTo = Berlin

Lasvegas]# vi /etc/tinc/netname/hosts/Lasvegas
==> Address = Lasvegas_public_IP
==> Subnet = 10.0.0.2/32
==> Subnet = 192.168.184.0/24

Lasvegas]# tincd -n netname -K4096

Lasvegas]# vi /etc/tinc/netname/tinc-up
==> #!/bin/sh
==> ip link set $INTERFACE up
==> ip addr add 10.0.0.2/24 dev $INTERFACE
==> ip route add 172.16.3.0/24 dev $INTERFACE
==> ip route add 192.168.184.0/24 dev $INTERFACE

Lasvegas]# vi /etc/tinc/netname/tinc-down
==> #!/bin/sh
==> ip link set $INTERFACE down

Lasvegas]# chmod 755 /etc/tinc/netname/tinc-*

3.[柏林]
Berlin]# yum install tinc
Berlin]# mkdir -p /etc/tinc/netname/hosts
Berlin]# vi /etc/tinc/netname/tinc.conf

==> Name = Berlin
==> AddressFamily = ipv4
==> Interface = tun0
==> ConnectTo = Beijing
==> ConnectTo = Lasvegas

Berlin]# vi /etc/tinc/netname/hosts/Berlin
==> Address = Berlin_public_IP
==> Subnet = 10.0.0.3/32
==> Subnet = 192.168.133.0/24

Berlin]# tincd -n netname -K4096

Berlin]# vi /etc/tinc/netname/tinc-up
==> #!/bin/sh
==> ip link set $INTERFACE up
==> ip addr add 10.0.0.3/24 dev $INTERFACE
==> ip route add 172.16.3.0/24 dev $INTERFACE
==> ip route add 192.168.133.0/24 dev $INTERFACE

Berlin]# vi /etc/tinc/netname/tinc-down
==> #!/bin/sh
==> ip link set $INTERFACE down

Berlin]# chmod 755 /etc/tinc/netname/tinc-*

4.复制hosts文件到各台服务器

不管你用什么方法,scp也好,sftp也好,最终每台机器的/etc/tinc/netname/hosts目录下都要有所有vpn节点的配置文件,如下所示:
--/etc
    --tinc
 --netname
   --hosts
            --Beijing
            --Berlin
            --Lasvegas
     
5.在每个vpn节点启动tinc
]# tincd -n netname -D -d3

6.设为开机自动启动
]# systemctl enable tinc@$VPN_NAME
]# systemctl start tinc@$VPN_NAME

备注1: 更改监听的端口
如果网络防火墙有低位端口限制,可以让tinc监听在任意你指定的端口上,只需要在hosts文件里Address位置写上指定的端口号即可。
Address = address [port]

参考文档:
https://www.digitalocean.com/community/tutorials/how-to-install-tinc-and-set-up-a-basic-vpn-on-ubuntu-14-04
http://ostolc.org/site-to-site-vpn-with-tinc.html
https://2kswiki.wordpress.com/2016/02/05/simple-vpn-network-mesh-with-tinc/
http://blog.hackathon.de/using-tinc-with-iproute2.html
http://www.jianshu.com/p/e030dabafd61
https://florianjacob.de/tinc-vpn-with-ipv6-and-iproute2.html
http://www.rendoumi.com/ling-wai-yi-chong-vpnfang-shi-tinc/
https://silvenga.com/deploy-a-tinc-mesh-vpn-running-tap/
https://wiki.archlinux.org/index.php/Tinc


 

haproxy与ssl应用中的一些点

$
0
0
    haproxy自1.5版开始引入了原生SSL支持,因此可以用来做SSL Termination或者SSL Offload。但在做https负载的时候,有一些点需要注意一下,以下文字可能前后逻辑性不太强,是因为我本人对加密算法这些东西也是一头雾水,只是将我看到的一些文档转述一下,尽我所能写明白一些吧。

一、HTTPS慢在哪里?  

使用https会明显感觉网站响应变慢了,为什么?慢在哪里了呢?

第一个因素,是初始化SSL连接消耗了时间。

在开始web页面内容下载之前,对于一个SSL session,用户的浏览器会与web server间有至少4个RTT用来协商加密算法,交换公钥。
而根据Yslow的前段优化建议,大的站点可能有多个域名用来分散图片,css,js等文件,一边浏览器并行下载这些文件,使用了https以后,则针对每一个域名,都需要进行上边所说的4个RTT协商!
能不慢吗?这个部分是有一些方法可以进行优化的,后边细说。

第二个因素,是OCSP和CRL

网站SSL证书一般是由一些证书机构(CA)签发的,这些证书由于某些原因,可能丢失了或者被盗了,因此需要对这些有问题的证书进行吊销。证书机构会有Certificate Revocation List (CRL)用于保存哪些证书被吊销了,当用户浏览一个使用https的网站时,浏览器会去CA的站点下载CRL用于比对或者使用Online Certificate Status Protocol (OCSP)去检查。
问题在于这些CA的站点往往没有针对全球用户进行地域优化,所以导致用户访问CA的站点是非常慢的。例如最大的CA,Verisign的平均OCSP响应时间在300ms,这也就意味着,如果你使用Verisign签发的证书,则用户访问你的站点,需要多花300ms!这个数字已经很成问题了。
这个部分,作为用户的我们是没有办法的,只能在买证书之前,尽可能的找OCSP响应快的CA,例如 Globalsign

二、如何加速SSL

1. 选用带有SSL加速指令的CPU和针对这些CPU优化的openssl版本

Intel新的xeon CPU中集成了一些指令,专门针对ssl加密进行了优化。所有选择硬件的时候,尽可能选择这样的CPU。另外,openssl有很多版本,其中1.0.2这个分支,专门针对Intel这些指令做了优化,所以要使用这个分支,才能最大化CPU的功能,两者相辅相成。

2. 简化cipher suite

openssl支持很多种加密算法,但其中一些是过时的,另外一些是不安全的,还有一些是非常耗CPU的。因此,我们要简化一下cipher suite,这个部分我不懂,所以找了mozilla的一个在线工具,可以生成只支持比较新的浏览器的cipher suite,至于坚守Windows XP,使用IE6的用户,就算了吧。

工具地址: https://mozilla.github.io/server-side-tls/ssl-config-generator/

该工具也可以生成haproxy,apache,nginx,Lighttpd ,AWS ELB的配置文件。

下边是使用该工具生成的haproxy的配置文件,


-----------------------------------------------------
使用openssl查看一下具体支持的算法:


3. 开启KeepAlive

前边说了每个SSL session要进行至少4个RTT来初始化,所以我们不想与同一server所建立的多个connection都走一遍这个过程,因此开启KeepAlive非常必要。

4.  提供中间证书

在SSL初始化握手的时候,需要校验证书是被受信CA所签发的。而大的CA一般不会直接用自己的根证书给用户签名,而是生成下一级证书或者更多级证书用于给用户签名。例如会给每个授权经销商一个证书,经销商可以用自己的证书给用户签名。这就是所谓SSL证书链,经销商使用的证书被称为"中间证书"。
如果浏览器不能识别“中间证书”,就会沿着证书链一级级去验证,直到验证成功,这个过程中,SSL握手会一直处于block状态。所以必须提供完整的证书链文件,以避免这种情况。这个可以找你的证书经销商要。

例子:
    bind 10.20.30.40:443 ssl crt /etc/haproxy/pem/server.pem ca-file /etc/haproxy/pem/client-chain.pem verify optional crt-ignore-err all crl-file /etc/haproxy/crl/haproxy.pem

5. 缓存静态文件

使用了https以后,默认浏览器不会对进过加密的任何内容作缓存,这也就是说所有静态文件会每次都要重新请求一遍,这个必须避免。方法是使用HTTP头,"Cache-Control: public"

对于nginx来说,配置如下:


注: firefox貌似不支持这个。

参考文档:
https://software.intel.com/zh-cn/articles/accelerating-ssl-load-balancers-with-intel-xeon-v3-processors
http://blog.haproxy.com/2011/09/16/benchmarking_ssl_performance/
https://serversforhackers.com/using-ssl-certificates-with-haproxy
http://blog.onefellow.com/post/82478335338/haproxy-mapping-process-to-cpu-core-for-maximum
https://www.haproxy.com/doc/hapee/1.5/system/tunning.html
http://op.baidu.com/2015/04/https-s01a03/
http://blog.haproxy.com/2015/06/09/haproxy-and-http-strict-transport-security-hsts-header-in-http-redirects/
https://s3-eu-west-1.amazonaws.com/ssl247/1415979645_Webinar-SSL_EN.pdf
https://www.digitalocean.com/community/tutorials/how-to-implement-ssl-termination-with-haproxy-on-ubuntu-14-04
https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-with-http-2-support-on-ubuntu-16-04
http://arstechnica.com/information-technology/2015/05/web-served-how-to-make-your-site-all-https-all-the-time-for-everyone/
https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/
https://raymii.org/s/snippets/haproxy_ssl_backends.html
http://virtuallyhyper.com/2013/05/configure-haproxy-to-load-balance-sites-with-ssl/
https://bjornjohansen.no/optimizing-https-nginx
http://arstechnica.com/information-technology/2015/05/web-served-how-to-make-your-site-all-https-all-the-time-for-everyone/
https://raymii.org/s/snippets/haproxy_set_specific_ssl_ciphers.html
https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
http://www.systutorials.com/5450/improving-sshscp-performance-by-choosing-ciphers/
https://blog.cloudflare.com/how-cloudflare-is-making-ssl-fast/
https://mozilla.github.io/server-side-tls/ssl-config-generator/
https://www.troyhunt.com/understanding-http-strict-transport/
https://www.ietf.org/proceedings/92/slides/slides-92-saag-2.pdf
https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices
https://raymii.org/s/tutorials/HTTP_Strict_Transport_Security_for_Apache_NGINX_and_Lighttpd.html
https://blog.qualys.com/securitylabs/2016/03/28/the-importance-of-a-proper-http-strict-transport-security-implementation-on-your-web-server
https://gold.xitu.io/entry/58252932da2f60005d2b8ac8
https://androidtest.org/gohttps/
http://www.jianshu.com/p/e3aa175e232c

 
Viewing all 55 articles
Browse latest View live