linux lvs 配置

lvs 需要配置下内核参数,否则会发生抢VIP断流之类的异常情况,本文档主要介绍DR模式下
首先是LVS 前端,需要设置两个参数arp_ignore和arp_announce


       echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore

       echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce

       echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore

       echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce

而后端的节点,也需要设置对应的参数


       echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore

       echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce

       echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore

       echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce

这里解释下这两个参数的含义:

arp_ignore - INTEGER
	Define different modes for sending replies in response to
	received ARP requests that resolve local target IP addresses:
	0 - (default): reply for any local target IP address, configured
	on any interface
	1 - reply only if the target IP address is local address
	configured on the incoming interface
	2 - reply only if the target IP address is local address
	configured on the incoming interface and both with the
	sender's IP address are part from same subnet on this interface
	3 - do not reply for local addresses configured with scope host,
	only resolutions for global and link addresses are replied
	4-7 - reserved
	8 - do not reply for all local addresses

	The max value from conf/{all,interface}/arp_ignore is used
	when ARP request is received on the {interface}

简单翻译下:
arp_ignore:定义对目标地址为本地IP的ARP询问不同的应答模式0

0 – (默认值): 回应任何网络接口上对任何本地IP地址的arp查询请求

1 – 只回答目标IP地址是来访网络接口本地地址的ARP查询请求

2 -只回答目标IP地址是来访网络接口本地地址的ARP查询请求,且来访IP必须在该网络接口的子网段内

3 – 不回应该网络界面的arp请求,而只对设置的唯一和连接地址做出回应

4-7 – 保留未使用

8 -不回应所有(本地地址)的arp查询


arp_announce - INTEGER
	Define different restriction levels for announcing the local
	source IP address from IP packets in ARP requests sent on
	interface:
	0 - (default) Use any local address, configured on any interface
	1 - Try to avoid local addresses that are not in the target's
	subnet for this interface. This mode is useful when target
	hosts reachable via this interface require the source IP
	address in ARP requests to be part of their logical network
	configured on the receiving interface. When we generate the
	request we will check all our subnets that include the
	target IP and will preserve the source address if it is from
	such subnet. If there is no such subnet we select source
	address according to the rules for level 2.
	2 - Always use the best local address for this target.
	In this mode we ignore the source address in the IP packet
	and try to select local address that we prefer for talks with
	the target host. Such local address is selected by looking
	for primary IP addresses on all our subnets on the outgoing
	interface that include the target IP address. If no suitable
	local address is found we select the first local address
	we have on the outgoing interface or on all other interfaces,
	with the hope we will receive reply for our request and
	even sometimes no matter the source IP address we announce.

	The max value from conf/{all,interface}/arp_announce is used.

	Increasing the restriction level gives more chance for
	receiving answer from the resolved target while decreasing
	the level announces more valid sender's information.

arp_announce:对网络接口上,本地IP地址的发出的,ARP回应,作出相应级别的限制: 确定不同程度的限制,宣布对来自本地源IP地址发出Arp请求的接口

0 – (默认) 在任意网络接口(eth0,eth1,lo)上的任何本地地址

1 -尽量避免不在该网络接口子网段的本地地址做出arp回应. 当发起ARP请求的源IP地址是被设置应该经由路由达到此网络接口的时候很有用.此时会检查来访IP是否为所有接口上的子网段内ip之一.如果改来访IP不属于各个网络接口上的子网段内,那么将采用级别2的方式来进行处理.

2 – 对查询目标使用最适当的本地地址.在此模式下将忽略这个IP数据包的源地址并尝试选择与能与该地址通信的本地地址.首要是选择所有的网络接口的子网中外出访问子网中包含该目标IP地址的本地地址. 如果没有合适的地址被发现,将选择当前的发送网络接口或其他的有可能接受到该ARP回应的网络接口来进行发送.

这里有个附加的例子解释

Assume that a linux box X has three interfaces - eth0, eth1 and eth2. Each interface has an IP address IP0,

IP1 and IP2. When a local application tries to send an IP packet with IP0 through the eth2. Unfortunately,

the target node’s mac address is not resolved. Thelinux box X will send the ARP request to know

the mac address of the target(or the gateway). In this case what is the IP source address of the

“ARP request message”? The IP0- the IP source address of the transmitting IP or IP2 - the outgoing

interface? Until now(actually just 3 hours before) ARP request uses the IP address assigned to

the outgoing interface(IP2 in the above example) However the linux’s behavior is a little bit

different. Actually the selection of source address in ARP request is totally configurable

bythe proc variable “arp_announce”

If we want to use the IP2 not the IP0 in the ARP request, we should change the value to 1 or 2.

The default value is 0 - allow IP0 is used for ARP request.

其实就是路由器的问题,因为路由器一般是动态学习ARP包的(一般动态配置DHCP的话),当内网的机器要发送一个到外部的ip包,那么它就会请求 路由器的Mac地址,发送一个arp请求,这个arp请求里面包括了自己的ip地址和Mac地址,而linux默认是使用ip的源ip地址作为arp里面 的源ip地址,而不是使用发送设备上面的 ,这样在lvs这样的架构下,所有发送包都是同一个VIP地址,那么arp请求就会包括VIP地址和设备 Mac,而路由器收到这个arp请求就会更新自己的arp缓存,这样就会造成ip欺骗了,VIP被抢夺,所以就会有问题。

arp缓存为什么会更新了,什么时候会更新呢,为了减少arp请求的次数,当主机接收到询问自己的arp请求的时候,就会把源ip和源Mac放入自 己的arp表里面,方便接下来的通讯。如果收到不是询问自己的包(arp是广播的,所有人都收到),就会丢掉,这样不会造成arp表里面无用数据太多导致 有用的记录被删除。

在设置参数的时候将arp_ignore 设置为1,意味着当别人的arp请求过来的时候,如果接收的设备上面没有这个ip,就不做出响应,默认是0,只要这台机器上面任何一个设备上面有这个ip,就响应arp请求,并发送mac地址

参考文档: http://www.cnblogs.com/lgfeng/archive/2012/10/16/2726308.html

免费https证书

免费https证书,早期有startssl,现在有 let’s Encrypt

Let’s Encrypt 是 由非盈利组织 ISRG(Internet Security Research Group) 带来的免费 自动颁发 开放认证的HTTPS证书服务

Let’s Encrypt is a free, automated, and open certificate authority brought to you by the non-profit Internet Security Research Group (ISRG).

Let’s Encrypt发展到现在,兼容性已经得到非常长足的发展,足以媲美geotrust globalsign verisign等商业证书,由于该证书还是天然三级证书链,性能方面也有明显的优势,更难能可贵的是: 它免费提供RSA/ECC两个版本的证书.

缺点? 基于安全性,只有90天的有效期,需要定期续签,另外它不类似于startssl提供WEB界面给使用者,需要使用命令行工具自行签发,有一定的门槛

本文的目的就是推广Let’s Encrypt 并降低入门门槛

1. 创建身份认证私钥

这个证书就是Let’s Encrypt 辨认识别你的私钥

openssl genrsa 4096 > account.key

2. 创建证书签名请求文件

CSR(Certificate Signing Request,证书签名请求)文件

创建RSA域名私钥

openssl genrsa 4096 > domain.key

创建ECC域名私钥

openssl ecparam -genkey -name secp384r1 | openssl ec -out ecc.key

生成CSR文件

openssl req -new -sha256 -key domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:4os.org,DNS:www.4os.org")) > domain.csr
openssl req -new -sha256 -key ecc.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:4os.org,DNS:www.4os.org")) > ecc.csr

 

3. 配置nginx 支持let’s Encrypt 自动校验

let’s Encrypt 是通过自签名程序生成一个随机文件,并访问你的域名的URL来校验你是否拥有这个域名的所有权,所以需要配置一个特定的目录和nginx location 路径,如果你有多台分布,请确认都能正确的访问到该目录文件.

创建用于存放验证文件的目录,例如:

mkdir ~/www/challenges/

然后配置一个 HTTP 服务,以 Nginx 为例:

server {
    server_name www.4os.org 4os.org;

    location ^~ /.well-known/acme-challenge/ {
        alias /home/xxx/www/challenges/;
        try_files $uri =404;
    }

...
}

4.从Let’s Encrypt获取证书

下载证书自动校验签发工具

wget https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py

校验并获取证书


python acme2_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir ~/www/challenges/  > ./signed.crt
python acme2_tiny.py --account-key ./account.key --csr ./ecc.csr --acme-dir ~/www/challenges/  > ./ecc.crt

现在获取下来的叫做站点证书, 为了浏览器能正确的识别证书的有效性,需要把中间证书一并下载下来并和站点证书合并


wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem
cat signed.crt intermediate.pem > chained.pem
cat ecc.crt intermediate.pem > ecc_chained.pem

5.nginx双证书 配置


#RSA
ssl_certificate ssl/chained.pem;
ssl_certificate_key ssl/domain.key;
#ECC
ssl_certificate ssl/ecc_chained.pem;
ssl_certificate_key ssl/ecc.key;

需要特别留意下: nginx 1.11.1版本以后才支持双证书,如果低于此版本,建议只使用RSA证书即可

本文主要参考了jerryqu的个人网站,关于https jerryqu做了大量的研究,Let’s Encrypt,免费好用的 HTTPS 证书

HTTPS 证书介绍和获取

Geotrust:
优点: 非常便宜
缺点: 品牌比较弱, 不兼容安卓4.x及以下平台,于是签发了equifax Secure CA的交叉认证导致证书多了一级
使用者: 搜狐 weixin.qq.com(微信公众号平台等)

globalsign:
优点: 性价比高, 天然三级, 兼容性好
缺点: 品牌稍弱, 不支持安卓2.3和之前的版本, XP需要更新根证书后才支持(系统自动完成的) 黑历史让人担忧 (2016.10出现过错误吊销中间证书的严重故障 )
使用者: 京东 淘宝 腾讯(xw.qq.com等 www.qq.com和news.qq.com 也在这张证书但是暂未提供https服务)

verisign:
优点: 品牌强大 兼容性好 可以直接使用三级 支持ECC加密性能更优
缺点: 贵 支持ECC双证书会更贵! 不支持安卓2.3和之前的版本
使用者: 搜狗 爱奇艺 百度 腾讯灯塔平台(beacon.qq.com,广告营销大数据分析等)

总结:
geotrust 的兼容性是通过交叉认证实现的,这导致每次请求都需要发多一张中间证书,这会带来一定的性能损失
除了黑历史, 京东 淘宝 腾讯都选择了globalsign, 除了性价比的因素,对方天然的三级证书优势也很明显
verisign 品牌悠久, 兼容性好,但是价格就不那么美好了,是个保守而稳定的选择

当然,以上都是要钱的,对于个人用户,选择免费证书是更好的选择,比如: https://letsencrypt.org/

HTTPS 部署和优化

本文主要介绍HTTPS 部署和优化, 构思中包括以下内容:

1. 证书的介绍和获取
1) 商业证书
介绍市面上主流的geotrust globalsign verisign三家证书签发机构
2) 免费证书
介绍Let’s Encrypt这家了不起的免费证书颁发机构
2. nginx start up配置
3. spdy 和 http2.0 支持
4. ciphers suite的选择
5. 为何不建议开启ocsp
6. HTTPS优化
7. ie6的支持

先挖个坑,以后慢慢填

如何迁移域名和投诉35互联

国内的一些域名商比较恶心,比如35互联.

当时我的域名是通过一个代理买的,我获得的权限就是一个DNS管理平台,可以指一下NS A CNAME等记录,但是域名资料信息什么的是没有账号修改的
刚好今年碰到域名实名,想着干脆就迁走吧,于是发信给35互联的客服咨询如何迁出.
本来做好了签名/身份证/转移书等邮寄到厦门的打算了,没想到35互联的客服压根就不想跟我谈,只是让我去找代理,但是代理也不管这事了…

这个时候就怒了啊,搜了下资料,被35互联这个大坑惹怒的人还真不少,投诉资料也挺多的,我顺便也科普下:
步骤1. 使用域名登记资料里边的邮箱, 正式发信给35互联客服,要求迁出,要求提供迁出密码
步骤2. 等待5个工作日,35互联肯定不会给你迁出密码的,要么让你找代理,要么让你邮寄资料之类的
步骤3. 上国际域名管理组织投诉去, 这里填表: https://forms.icann.org/en/resources/compliance/complaints/transfer/form
步骤4. 等上2-3天,icann.org 会邮件给你,要你确认是否迁出,并提供下原域名注册商不配合的证据(步骤1的邮件即可)
步骤5. 嗯,过几天,35互联的客服就会乖乖把迁出密码给你了

需要谨记,按照国际域名管理组织的规定, 只要你是域名拥有者,就有资格迁出域名和获取域名迁出码,不需要邮寄隐私资料,更加不需要找什么代理去谈

syslog-ng 的一些格式

1. syslog-ng 是支持模板的,可以自由定义格式,比如
destination d_access {
file(“/opt/itc/syslog-ng/logs/$PROGRAM/$YEAR$MONTH$DAY/access_log”
create_dirs(yes)
dir_owner(“nobody”)
dir_group(“nobody”)
dir_perm(0755)
owner(“nobody”)
group(“nobody”)
perm(0644)
template(“${HOUR}:${MIN}:${SEC} ${HOST} ${PROGRAM} ${MSG}\n”)
);
};

2. 这个例子又刚好会触发一个小窍门,一般加了template的会发现数据丢了第一项,这是因为:
syslog-ng 会把第一列 当做是 $PROGRAM ,之后的那些内容才是 MSG

3. 那么,我想加PROGRAM 让syslog-ng 识别,在收集日志的机器分散出来怎么办? 在source里边定义就好
source s_app {
file(“/opt/itc/nginx/logs/app_access.log”
program_override(“app.4os.org”)
flags(no-parse)
);
};

这个例子里边,就会在原始日志里边自动添加一列 app.4os.org 作为PROGRAM到日志里边去

4. 总结下,实际上,没有template的默认日志格式是怎样的?
${ISODATE} ${HOST} ${PROGRAM} ${MSG}\n

其中日期函数跟定义有关,默认为iso

gcc 与 ld 的lib顺序

事情是这样子的,有个编译出来的.so文件,死活在找一个诡异版本的libcrypto.so,而我检查过环境变量和编译参数还有ldconfig,并未包含这些东西
绕了一个大圈子,查找编译文件并未包含这些路径,最后在简化测试的时候发现了问题所在
#ld -shared -o test.so -lcrypto
极为简单的一个测试,发现在故障机器上编译出来的.so还是包含了错误的libcrypto.so
strace下发现了问题,这是因为ld有个依赖顺序:
“/usr/x86_64-redhat-linux/lib64/”,”/usr/local/lib64/”, “/lib64/”,/usr/lib64/, /usr/x86_64-redhat-linux/lib/ , /lib/ , /usr/lib/

而刚巧,这机器在/usr/local/lib64/被不知名土人放了个错误版本的libcrypto.so

另外,补充下,如果用了-L参数指定路径,那么这个路径则是最优先的

而LD_LIBRARY_PATH 则是执行时查找的动态链接库位置,跟ldconfig功能一样,不影响编译,因此以上例子查找ldconfig是不必要的

而最重要的是解决问题的思路,分割问题==>缩小范围==>定位问题!

python 替换\n

一般来说,python可以通过strip() 或者replace(‘\n’,”)的方式来替换掉\n 字符

不过改动某个功能代码的时候异常的发现两种替换方式都失败了,debug定位了下,发现数据是通过readlines()的方式读取进来的
而readlines 会自动的把字符中的\r\n 更改为 \\r\\n,从而导致替换失败

嗯,如果是readlines的数据,只能用replace(‘\\n’,”)了
当然,不要用readlines比较好

infinidb 出现导入错误:ERROR 122

ERROR 122 (HY000): PM1 : Bulkload Read (thread 0) Failed for Table cdn. Terminating this job.

#perror 122
OS error code 122: Disk quota exceeded
MySQL error code 122: Internal (unspecified) error in handler

内部错误,没有特别好处理的办法,查了下,有日本文档介绍通过取消batchinsert设置来定位问题的,不过我测试失败
调试过程中发现取消了infinidb_use_import_for_batchinsert则可以正常导入,另外通过笨办法分割导入定位到了出错数据,原来是原始数据的某一列中包含了”\r\n”的内容,而我的导入方式是以\n 作为结尾的

附上日本文档地址和方法:
mysql> SELECT @@infinidb_use_import_for_batchinsert; — これがONだとLOAD DATA INFILEを/usr/local/Calpont/bin/cpimportにマップしてくれるので無効にする。
+—————————————+
| @@infinidb_use_import_for_batchinsert |
+—————————————+
| 1 |
+—————————————+
1 row in set (0.00 sec)

mysql> SET SESSION infinidb_use_import_for_batchinsert= 0;
Query OK, 0 rows affected (0.00 sec)

mysql> LOAD DATA INFILE ‘/data/tmp/fifo’ INTO TABLE vegelog;
Query OK, 17208325 rows affected, 2076 warnings (5 min 35.09 sec)
Records: 17208325 Deleted: 0 Skipped: 0 Warnings: 2050

mysql> show warnings;
+———+——+——————————————————————————–+
| Level | Code | Message |
+———+——+——————————————————————————–+
| Warning | 1262 | Row 5397 was truncated; it contained more data than there were input columns |
| Warning | 1262 | Row 5406 was truncated; it contained more data than there were input columns |
| Warning | 1262 | Row 59575 was truncated; it contained more data than there were input columns |
..
| Warning | 1261 | Row 329176 doesn’t contain data for all columns |
| Warning | 1261 | Row 329176 doesn’t contain data for all columns |
| Warning | 1261 | Row 329176 doesn’t contain data for all columns |
+———+——+——————————————————————————–+
64 rows in set (0.00 sec)

edns 与 8.8.8.8 DNS Cache

测试bind-9.8.1-P1 的edns的时候发现,google DNS解析的结果一直在跳,有时候在电信区,有时候在us区

于是给它打了第一个patch,让query log 支持edns的client subnet显示,便于排查

筛选下日志,发现很多edns请求不在我们的ecs ACL范围内,最终落到了google DNS IP 所在的us区域,推测应该是这个造成了google DNS 的错误缓存

于是,把ecs的default区域加入拦截

view "default_ecs" {
match-clients { ecs 0.0.0.0/0; ecs ::/0;};
... ...
};

这样一来,的确是响应了edns请求,结果却是更多的出现了default 区的结果(热数据),或者在client所属区域和default区结果之间跳动(冷数据)

听包,发现google DNS发送过来的请求Scope Netmask 都是0,如果ecs ACL拦截不成功到了default_ecs区域,最终被ecs 0.0.0.0/0; ecs ::/0; 拦截成功,导致返回的Scope Netmask变成了0

Scope Netmask 变为0意味着什么? 意味着这个结果集有效并且范围最大,从而污染所有的subnet client结果集

目前来看,只能给它打个patch

让client subnet 到了default区域的结果集Scope Netmask 为起address netmask长度,控制结果集的有效范围

测试下,把某个网段故意从ecs ACL挪走,让default ecs拦截看看响应是否是我们期待的

正常网段的请求,返回我们ecs ACL的Netmask

上线测试,目前服务正常

 

附上edns 文档,非常重要:
https://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-01
http://noops.me/?p=653

以上的显示EDNS 的代码存在bug, 需要初始化

char edbuf[ISC_NETADDR_FORMATSIZE] = { 0 };

这里需要特别解释下 为什么char数组初始化使用 {0}, 这里有个解释

it’s a C-style cast. That is, it converts 0 (which is a literal of type int) to char (the \0 character). That cast could have been avoided entirely by simply using the ‘\0’ literal.

测试char = 0; 的时候打印出来直接是null, 从stackoverflow的解释看是这么回事

https://stackoverflow.com/questions/10004297/what-does-char0-mean-in-c