1. 根据域名拦截请求
思路:iptables只能通过ip粒度进行拦截转发丢弃,无法通过域名粒度转发。但是iptables可通过ipset进行拦截,并通过dnsmasq将域名对应的ip写入到ipset中,实现了域名粒度的拦截。
域名到ipset绑定的流程
1. 在/etc/resolv.conf 中加入 nameserver 127.0.0.1
2. yum install -y dnsmasq
3. yum install -y ipset
4. ipset create proxyipset hash:ip
5. dnsmasq --ipset=/.baidu.com/proxyipset (监听53端口)
另一中方式(麻烦):
rm -rf /etc/dnsmasq.d/domain.conf
echo 'ipset=/.sougou.com/proxyipset' >> /etc/dnsmasq.d/domain.conf
dnsmasq -C /etc/dnsmasq.conf
6. nslookup www.baidu.com (或者ping)
7. ipset list 可看到如下:
Name: proxyipset
Type: hash:ip
Revision: 1
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 232
References: 0
Members:
110.242.68.3
10.128.236.120
110.242.68.4
Members中添加www.baidu.com多个ip节点
8. 重启 (重启dnsmasq后域名才生效)
ps -ef | grep dnsmasq & kill -15 dnsmasq 删除
(systemctl 在docker中没法用,需特权运行 )
ipset flush proxyipset 清空
2. iptables通过ipset拦截出流量
-
参考 istio 拦截出站流量
# 出站请求,当TCP请求进入OUTPUT链时全部交给ISTIO_OUTPUT处理
-A OUTPUT -p tcp -j ISTIO_OUTPUT
#请求下一个服务/响应请求,即请求非本地服务同时不来自istio-proxy用户空间,流量被转发至ISTIO_REDIRECT链
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
# 将发送到此的TCP请求全部重定向至15006端口(Envoy入口流量端口)
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
-
简化并加入ipset ( 请求目标域名但是流量不来自proxy )
iptables -t nat -A OUTPUT -p tcp -m owner ! --uid-owner 1337 -m set --match-set proxyipset dst -j REDIRECT --to-ports 2047
TODO:只拦截发往80或者443端口或者用户指定的端口的流量
执行 iptables -L -v -n -t nat 查看
Chain OUTPUT (policy ACCEPT 3 packets, 180 bytes)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 ! owner UID match 1337 match-set proxyipset dst redir ports 2047
-
以proxy启动服务监听2047端口 ( 并且转发请求 www.baidu.com)
参考envoy:
useradd -u 1337 proxy (添加用户)
sudo usermod -a -G root proxy(加入到用户组,之后改成work)
grep proxy /etc/group (查看)
su proxy & ./output_test_server
./output_test_server
请求www.baidu.com/hello 返回真正的数据
[/]$ curl www.baidu.com/hello
<html>
<head>
<script>
location.replace(location.href.replace("https://","http://"));
</script>
</head>
<body>
<noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
</body>
</html>
请求 www.baidu.com/world 返回404 ,说明没转发成功
[/]$ curl www.baidu.com/hel
404 page not found
3. https流量拦截
思路:我们代理容器要解析请求百度的外网流量,首先我们得有百度的证书给客户端,其次我们得有百度的私钥解密客户端的流量,显然我们拿不到百度的私钥。因此我们自己自签百度的证书发给客户端,客户端加密之后我们用自签的私钥解密,拿到请求。但是有个问题,客户端拿到我们的自签的证书里,客户端本地的CA中心的公钥并不认可(不是他们签发的),因此我们将签发自签证书的CA根证书加到客户端本地证书库,客户端就信任了。这样,我们就可以解密https流量。(参考istio的自签证书体系)
CA中心怎么认证证书的正确性,私钥公钥加解密原理,istio安全体系可以查看参考内容。
-
新建ssl.conf文件(生成SAN证书)
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
req_extensions = req_ext
[ req_distinguished_name ]
countryName = CN
countryName_default = GB
stateOrProvinceName = Beijing
stateOrProvinceName_default = Chinese
localityName = Beijing
localityName_default = Beijing
organizationName = 44
organizationName_default = 44
organizationalUnitName = 44
organizationalUnitName_default = IT
commonName = www.baidu.com
commonName_max = 64
commonName_default = www.baidu.com
[ req_ext ]
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.baidu.com
-
创建私钥和证书
openssl genrsa -out server.key 4096
openssl req -new -sha256 \
-out server.csr \
-key server.key \
-config ssl.conf
openssl x509 -req \
-days 3650 \
-in server.csr \
-signkey server.key \
-out server.crt \
-extensions req_ext \
-extfile ssl.conf
-
golang 将证书加入到本地客户端的信任里
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
-
java 将证书加入到本地客户端的信任里
keytool -import -trustcacerts -alias proxy -file ~/server.crt -keystore /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.121-0.b13.el7_3.x86_64/jre/lib/security/cacerts -storepass changeit -keyalg RSAs -noprompt
4. 其他问题
!!!前置条件: http协议中通过Host字段可以拿到 域名&端口,www.baidu.com:442 这样,我们就知道拦截的请求跟谁新建请求,这样,不同域名不同端口的请求只需要解析到一个listen即可,listen自己拿到连接HOST
5. 相关参考
-
业务容器发起https请求,proxy作为https服务端拦截请求(需要提供https服务)
-
mosn 提供https服务,需要签发证书,例子参考TLS 安全链路 | MOSN
-
istio 证书签发过程,例子参考一文带你彻底厘清 Isito 中的证书工作机制 - 云+社区 - 腾讯云
-
但是上面这种是请求针对的是有私有证书中心的情况,如果证书是手动生成放到sdk中,就没有办法提供对应的https服务了。
下面可以看到,https出流量的证书在envoy侧也是istio 颁发的。
-
istio访问外网的例子 Istio / 访问外部服务
-
创建一个ServiceEntry允许对外部服务的访问
-
内部是http访问,但是经过egress变成https请求Istio 的高级边缘流量控制(二) - 云+社区 - 腾讯云
-
记一次 istio serviceentry 的填坑之路 - 掘金
在 TLS 上下文中定义了生成的证书和密钥,如果我们有多个域名,每个域名都有自己的证书,则需要通过 定义多个证书链。
openssl req -nodes -new -x509 -keyout server.key -out server.crt -days 365 -subj '/CN=www.baidu.com/O=lcp./C=CN'
2)openssl自签名泛域名、多域名证书
如何生成泛域名证书和多域名证书呢?
-
泛域名证书在csr文件创建的时候,在Common Name选项输入 *.example.com即可
-
添加 ServiceEntry,允许访问 httpbin 的 HTTPS 服务
-
Istio 常见问题 - 集群内无法访问外部服务 | MakeOptim
这个例子看,https请求拿不到 请求的url信息,所以istio也没解https流量
mosn/examples/cn_readme/http-sample at master · mosn/mosn · GitHub