ESTABLISHED,TIME-WAIT与网络流量的关系

之前有同事看了有2个监控图,然后就问我为什么tcp的连接数基本没有变化,但是网卡的数据流量增加了不少?
tcp_conn_time
packets_bond0

这个其实比较好分析,应用系统和前端的LB之间实际是keepalive的持久连接。当单个应用服务器上请求量不是太的时候,应用服务器上看到的连接数和用户的请求量其实不是一个线性的关系,维持的连接数多少,主要看前端LB设置的保留多少个空闲连接。但是后端应用服务器(apache)上每个持久连接默认配置只能处理100个请求,所以当业务量增加的时候可以看到TIME_WAIT的数量增加了。同时网卡流量也对应增加的。

发表在 net | 留下评论

DNS放大攻击

近来比较火的一个问题就是DNS放大攻击,详细的介绍可以参考这里。cloudflare被攻击的流量超过20G。这个攻击思路其实和smurf攻击类似。smurf攻击是对一个子网的广播地址发起一个伪造IP源的ICMP包,这样这个子网的所有机器都会icmp-reply到这个伪造的IP地址上。造成只需要少量的肉鸡就能引起很大的攻击流量。而DNS放大攻击是伪造一个DNS查询的报文,源地址改成想要攻击的IP。单个查询的包64字节,如果是ANY类型查询(或者DNSSEC记录),那么回复报文一般会大几十倍。当然,如果攻击者自己制造一个很大的TXT记录,那么可能返回的更大的报文,攻击的强度就会更大。这样2M带宽的肉鸡,能制造的攻击流量就能到几百兆了,几十个G攻击流量很容易制造。

发表在 System | 留下评论

openwrt上安装tcpdump抓包

其实在DB120之类的路由器上装tcpdump什么的非常简单,有16M的ROM基本随便装自己想玩的东西。但是对于只有4M ROM的tp-link 740来说就比较麻烦了。一方面是ROM比较小,另外一方面是要想保存数据的话可以用nc之类的同步传输数据。我是直接选中tcpdump和nfs客户端。这样可以直接远程挂载,把抓包的文件直接放远程服务器上,方便后期的分析。
1.下载openwrt代码
git clone git://git.openwrt.org/12.09/openwrt.git
cd openwrt && ./scripts/feeds update -a && ./scripts/feeds install -a
2.配置openwrt
基本上就是选上ar71xx的cpu,然后模版选上TP-LINK 740ND就行了,注意选上tcpdump,nfs-utils,kmod-fs-nfs,wpad

3.编译openwrt
make -j 10 V=99
直接等编译好把openwrt-ar71xx-generic-tl-wr740n-v1-squashfs-sysupgrade刷到路由器就OK了。

然后就可以直接在路由器上挂载上远程的NFS,放抓包的文件。

发表在 OpenWrt | 留下评论

nginx的dns ttl问题

今天遇到了一个和dns ttl相关的问题。线上一个nginx服务器代理了一些外部的资源,把外部的http的资源变成https的供我们自己的https页面上用。
但是今天看到了有很多错误日志,显示的是连upstream的机器失败了。我看了一下配置文件,直接在nginx服务器上访问配置的url是正常访问的。再在nginx服务器上解析了一下对应的IP,发现和错误日志里显示的不一样了。看样子是外部的dns切换了IP,nginx一直是在访问老的失效的IP。

网上看了一下nginx的WIKI,也问了一下tengine的开发同学。nginx wiki上说是会遵循DNS的ttl设置,但是结果确实不是这样。自己简单测试了一下。
测试环境:
1. 1台linux服务器,装上nginx-1.2.8即可。
2. 1台linux服务器跑dnsmasq,设置好ttl并开启日志,也在上面装了wireshark方便抓包。
配置文件如下
[text]
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8888;
server_name localhost;
charset utf-8;

location / {
root /home/admin/soft;
index index.html index.htm;
autoindex on;
}
}
server {
listen 9001;
server_name localhost;
charset utf-8;
location / {
proxy_pass http://dnstest;
}
}
upstream dnstest {
server nginx.test.org:8888;

}
}

[/text]

发现启动的时候会做4次dns查询,但是后面无论多久是不会重新进行nginx.test.org的查询的,而wireshark显示TTL确实是被置为了10s。
[text]
Domain Name System (response)
[Request In: 7]
[Time: 0.000074000 seconds]
Transaction ID: 0x925f
Flags: 0x8580 (Standard query response, No error)
1… …. …. …. = Response: Message is a response
.000 0… …. …. = Opcode: Standard query (0)
…. .1.. …. …. = Authoritative: Server is an authority for domain
…. ..0. …. …. = Truncated: Message is not truncated
…. …1 …. …. = Recursion desired: Do query recursively
…. …. 1… …. = Recursion available: Server can do recursive queries
…. …. .0.. …. = Z: reserved (0)
…. …. ..0. …. = Answer authenticated: Answer/authority portion was not authenticated by the server
…. …. …. 0000 = Reply code: No error (0)
Questions: 1
Answer RRs: 1
Authority RRs: 0
Additional RRs: 0
Queries
nginx.test.org: type A, class IN
Name: nginx.test.org
Type: A (Host address)
Class: IN (0x0001)
Answers
nginx.test.org: type A, class IN, addr 220.xx.xx.xx
Name: nginx.test.org
Type: A (Host address)
Class: IN (0x0001)
Time to live: 10 seconds
Data length: 4
Addr: 220.xx.xx.xx

[/text]
另外设置了resolver 220.xxx.xxx.xx valid=10s;发现还是不会在指定的时间内更新。咨询文景,得知只能采用proxy_pass http://$host这种做正向代理才能每次动态查询dns。

自己测试了一下其实只有另外加上resolver才能使得nginx遵循ttl时间的设置。

[text]
resolver 220.xx.xx.xx valid=15s;
….
server {
listen 9002;
server_name localhost;
charset utf-8;
location / {
proxy_pass http://$http_host:8888;
}
}

[/text]

[text]
Apr 8 14:22:45 dnsmasq[6970]: query[A] nginx.test.org from 220.xx.xx.xx
Apr 8 14:22:45 dnsmasq[6970]: /home/admin/dnsmasq/dnsmasq.hosts nginx.test.org is 220.xx.xx.xx
Apr 8 14:23:01 dnsmasq[6970]: query[A] nginx.test.org from 220.xx.xx.xx
Apr 8 14:23:01 dnsmasq[6970]: /home/admin/dnsmasq/dnsmasq.hosts nginx.test.org is 220.xx.xx.xx
Apr 8 14:23:17 dnsmasq[6970]: query[A] nginx.test.org from 220.xx.xx.xx
Apr 8 14:23:17 dnsmasq[6970]: /home/admin/dnsmasq/dnsmasq.hosts nginx.test.org is 220.xx.xx.xx
Apr 8 14:23:33 dnsmasq[6970]: query[A] nginx.test.org from 220.xx.xx.xx
Apr 8 14:23:33 dnsmasq[6970]: /home/admin/dnsmasq/dnsmasq.hosts nginx.test.org is 220.xx.xx.xx
[/text]

最终才能得到前面的这种效果,每个15s重新查询一次。

发表在 nginx | 留下评论

也说说LVS模式的选择

前几天看论坛上有人问LVS几种模式的选择问题,简单地回复了一下。觉得很多做运维的新人都对这个选择存在疑惑,就单独写写。

LVS主要是DR,TUN,NAT和淘宝的FULLNAT模式,对于绝大部分人而言只能选择原版内核支持的前三种。

1.DR模式
DR模式是效率最高的一种,对于每个请求LVS把目的mac改成从RS中选择的机器的mac,再将修改后的数据帧在与服务器组的局域网上发送。但是局限性是LVS机器需要和RS至少能有一个网卡同在一个VLAN下面,这样限制了DR模式只能在比较单一的网络拓扑下使用。

2.TUN模式
TUN模式其实性能与DR模式相比差别不大的,TUN模式下会动态地从RS列表选择一台服务器,将请求报文封装在另一个IP报文中,再将封装后的IP报文转发给选出的服务器;RS服务器收到报文后,先将报文解封获得原来目标地址为VIP的报文,服务器发现VIP地址被配置在本地的IP隧道设备上,所以就处理这个请求,然后根据路由表将响应报文直接返回给客户。TUN模式可以解决DR模式下不能垮网段的问题,甚至可以垮公网进行。但是需要RS能支持ipip模块。

3.NAT模式
NAT模式对RS没有其他要求,唯一的要求是得把RS的网关设置为LVS机器。因为进出的流量都要通过LVS机器,所以性能相对会差很多,而且部署的规模很难做大。

以上DR模式和TUN模式在部署的时候都需要在本机绑定VIP,非常麻烦,比如我们之前有的老应用因为一些历史问题单个应用的VIP有40来个,如果用LVS做负载均衡基本就崩溃了,每次新增/删除一个VIP,估计得线下测试好多次ip addr add/del的用法。NAT模式在部署的时候也是太麻烦了。而且还有一个很关键很关键的是,使用了LVS后万一被人ddos怎么办?syn-cookie在抵挡攻击的时候效果一般不是太好,这样攻击透过lvs直击后端应用就杯具了。所以在很多大公司都不敢直接把lvs放公网,前面得加个防火墙啥的。所以淘宝单独搞了一个fullnat模式,一方面可以解决部署绑VIP、或者把RS的网关设置为LVS机器IP带来的部署复杂问题,另外一方面是加了一个syn-proxy等等,可以抵挡下一般的网络层攻击。但是使用FULLNAT模式后确实有个麻烦的是后端机器看不到用户的IP了,所以RS上还得用装上打过补丁的内核,对取IP的操作就劫持才能获取到用户IP。

对于大规模网站,其实无聊单独使用哪种LVS都是不能替换商业设备的,所以还是得配合nginx or haproxy做负载均衡。这个时候最简单的就是lvs(fullnat)+nginx/haproxy(nginx官方版本现在没有4层代理功能,haproxy对后端又不支持keepalive),当然使用DR模式或者TUN模式也还可以的。总之基本都得用2层才能搞得定,满足大部分应用上的需求。其实对于很多小公司,我觉得直接用nginx/haproxy就OK了。搞的那么复杂维护成本会非常高的,自动化运维没有跟上的时候只会把自己给玩死。

在使用LVS之前,建议大家一定仔细看看文档,没有好好看文档就别瞎折腾了。
1、http://zh.linuxvirtualserver.org/handbooks
2、http://www.linuxvirtualserver.org/Documents.html#manuals

发表在 lvs | 留下评论

blog设置缓存后遇到的问题

上周把blog开启了cache php结果后,主要是为了解决假想的一种短时间内请求过大php-fpm性能跟不上的问题。但是今天发现了一个比较奇怪的问题,就是打开的blog首页的时候页面是空白的。然后想起这个可能是和我针对dnspod的监控有特殊配置的原因。因为dnspod的访问比较频繁,所以我设置了直接返回200的特殊配置,避免无谓消耗机器的性能。
[text]
42 location / {
43
44 # First attempt to serve request as file, then
45 # as directory, then fall back to displaying a 404.
46 try_files $uri $uri/ /index.php;
47 # Uncomment to enable naxsi on this location
48 # include /etc/nginx/naxsi.rules
49 if ($http_user_agent ~ monitor ) {
50 return 200;
51 access_log off;
52 }
53 fastcgi_cache blog;
54 }

[/text]

自己当时配置的时候只是去连续刷新几次,看是否成功cache住了页面。但是实际平时都是dnspod的监控在访问,直接返回了200,这样如果这样的页面被cache,自己打开的时候就啥东西都看不到了。解决的方式就是修改一下dnspod的监控页面,比如监控的URL改成/favicon.ico之类的。

但是这样修改也会有个漏洞,比如我手动设置user-agent为monitor,使劲访问/,则首页缓存住的一直是一个空页面,所以想了个办法,把user-agent单独map到一个变量上,然后cache_key里把这个变量加上
[text]
25 map $http_user_agent $agent {
26 default ‘normal’;
27 ~monitor ‘dnspod’;
28 }
[/text]
fastcgi_cache_key修改为:
fastcgi_cache_key “$scheme$host$agent$request_uri$server_protocol$request_method”;
简单测试一下:
[text]
# curl -I -A "monitor" http://blog.gnuers.org
HTTP/1.1 200 OK
Server: nginx/1.2.7
Date: Wed, 03 Apr 2013 12:11:55 GMT
Content-Type: application/octet-stream
Content-Length: 0
Connection: keep-alive

# curl -I http://blog.gnuers.org
HTTP/1.1 200 OK
Server: nginx/1.2.7
Date: Wed, 03 Apr 2013 12:12:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.3.22-1~dotdeb.0
X-Pingback: http://blog.gnuers.org/xmlrpc.php
Expires: Wed, 03 Apr 2013 13:12:00 GMT
Cache-Control: max-age=3600
hit: cache
[/text]
需要注意的时候cache_key一定要写全。我之前写的时候没有加$request_method。
fastcgi_cache_key “$scheme$host$agent$request_uri$server_protocol”;
结果发现还是出现了页面不能访问的问题,把日志打开发现是有这样的记录,QQ浏览器默认会发出HEAD请求,这样会使得这个key存的东西是空的。
[text]

– – ::ffff:101.226.68.137:35145 – – [03/Apr/2013:13:46:19 +0000] blog.gnuers.org "HEAD / HTTP/1.1" 200 0 "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; QQDownload 713; .NET CLR 2.0.50727; InfoPath.2)" normal "-" "unix:/var/run/php5-fpm.sock" "- -" 0.214 0.214
[/text]
顺便再修改了一下日志格式,可以直接看到请求是否是从cache读取的。
[text]
log_format main ‘$http_orig_client_ip – $remote_addr:$remote_port – $remote_user [$time_local] $host "$request" $status $body_bytes_sent "$http_referer" ‘
‘"$http_user_agent – $agent – $hitstatus" "$http_x_forwarded_for" "$upstream_addr" "$ssl_protocol $ssl_cipher" $request_time $upstream_response_time’;
[/text]
虽然说原本不需要过度优化,但是能简单地优化一下也未尝不可。

发表在 nginx | 留下评论

使用Graphviz做有向图

Graphviz是一个比较简单的画图程序,之前看别人用这个自动画图生成上百个系统的依赖关系觉得挺玄乎的。今天照着http://www.ibm.com/developerworks/cn/aix/library/au-aix-graphviz/ 简单学习了一下。
[text]
digraph G{
A->B;
E [shape=box,color=red]
J [shape=box, fillcolor="#ABACBA", style=filled]
B-> { E;F } -> G ->J;
{ A;G } -> J;
{A;F}->K;
}
[/text]
test

参考文档:

http://www.ibm.com/developerworks/cn/aix/library/au-aix-graphviz/
http://www.graphviz.org/content/attrs
http://blog.163.com/prevBlogPerma.do?host=lockriver&srl=487232242010101761749383&mode=prev

发表在 linux shell | 留下评论

VPS iptables配置

使用VPS的时候肯定也会考虑到安全上的问题,简单配置一下iptables。直接贴一下我自己写的2个设置脚本。
首先得写一个清理规则、重置默认策略的脚本放crontab里面,以免自己设置错了策略造成自己不能登录。
[bash]
cleanrules.sh
#!/bin/bash
source /etc/profile
DIR=$(dirname $0)
LOG=$DIR/clean.log
iptables -F
iptables -X
iptables -Z #reset counter
iptables -P INPUT ACCEPT #set default action for INPUT packages
date >$LOG
echo "clean the iptables rule" >>$LOG
[/bash]
规则设置脚本
[bash]
iptables.sh
#!/bin/bash
#for my vps
iptables -F #remove all rules
iptables -X #remove all chains defined by myself
iptables -Z #reset counter
iptables -P INPUT DROP #set default action for INPUT packages
iptables -P OUTPUT ACCEPT #set default action for OUTPUT packages
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT # allow all traffice thron lo
iptables -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT # allow all established and related INPUT packages
for port in 22 80 443
do
iptables -A INPUT -p tcp –dport $port -m state –state NEW -j ACCEPT #
done
iptables -A INPUT -p icmp -m limit –limit 20/m -j ACCEPT
[/bash]
默认策略就是所有主动入站的DROP掉,然后出去的都允许,使用-m state –state ESTABLISHED,RELATED -j ACCEPT可以把自己的配置规则处理的非常简洁。如果不使用state模块的话,为了VPS自己能打开80,443端口还得单独写规则,允许sport是80、443的进入,但是这样实际就是有安全上的漏洞了。
另外就是需要注意每个端口的顺序,自己可以先跑一段时间后运行一下iptables -L -n -v看看,尽量让匹配得多的规则拍在前面,这样可以提高效率。
[bash]
# iptables -L -n -v
Chain INPUT (policy DROP 551 packets, 28376 bytes)
pkts bytes target prot opt in out source destination
6 300 ACCEPT all — lo * 0.0.0.0/0 0.0.0.0/0
64949 8740K ACCEPT all — * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
34 1884 ACCEPT tcp — * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 state NEW
6564 367K ACCEPT tcp — * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 state NEW
72 3952 ACCEPT tcp — * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 state NEW
[/bash]

发表在 net | 留下评论

nginx map的使用

在前面的一篇blog里说了现在对页面做cache,但是我想能直接在浏览器里看到是否是命中缓存。这个可以比较简单地通过map来说解决。
在http段添加
[text]
map $upstream_addr $hitstatus {
default ‘cache’ ;
~unix ‘nocache’;
}
[/text]
之所以是匹配unix是因为nginx是使用uninx sock连接后端的php-fpm,fastcgi_pass unix:/var/run/php5-fpm.sock。所以当$upstream_addr匹配unix的时候说明是没有命中缓存,其他情况则是命中了缓存的。
在server段添加好 add_header hit $hitstatus就能在http头里添加上对应的这个header了。

发表在 nginx | 留下评论

limit_req引发的访问速度慢

之前是考虑到安全上的问题,所以简单地设置了一下limit_req,但是设置的太死了,限制了每秒3个请求。今天突然意识到这个可能是之前blog速度慢的元凶。
直接看来一下一个页面了需要请求的元素大概有接近20个。赶紧把原来的
[text]
limit_req_zone $binary_remote_addr zone=gnuers:10m rate=3r/s;
[/text]
修改了一下,放宽这个限制后感觉速度是有明显的变化。其实要是用淘宝的tengine的话有个比较好解决方式,就是可以限制每个IP访问每个URL的频率。这样就可以兼顾安全和实际的使用体验了。

发表在 nginx | 留下评论