Python requests库指定IP请求,并指定域名证书验证
背景
运维同学通过网关平台下发nginx配置文件,下发的过程是先灰度发布,发灰度机器,然后需要验证域名的请求是否正常,然后在下发其他生产机器,但是验证过程是人为操作,这就不可避免的会有些同学不进行验证,点击“继续”发布,如果出现问题将是灾难性的(别问为什么是灾难性的)。
为了避免这个问题的出现,发布平台添加自动校验的过程,也就是在灰度机器发完,点击继续时,网关平台会将重点域名进行准确性校验,但是只验证连通性和证书,不会进行具体数据的验证。
过程
一般人工校验都是本地配置DNS解析(/etc/hosts),在通过域名请求,使域名打到目标机器上。如果是机器自动校验还是要配置DNS解析就难免有些不够便利
所以还是想通过参数传入目标IP的形式,使重点域名可以达到目标机器上
代码
按照以下代码可以实现流量打到目标机器
response = requests.request("GET", "https://10.12.13.14/healthcheck", headers={'Host':'www.nginxtest.com'}, data={}, verify = False)
但是如果是https,必须得指定verify=false,禁用证书校验。但是却违背了我们的意愿
因为在requests校验证书不会从headers里取,只会从url中取,所以会报无法匹配10.12.13.14的证书
有两种方式去解决
本环境是Python2的环境,与Python3稍微有些差别,已经标出。
方法1
使用requests的底层 urllib3
import urllib3
pool = urllib3.HTTPSConnectionPool("123.456.23.123",assert_hostname="nginxtest.com")
# pool = urllib3.HTTPSConnectionPool("123.456.23.123",assert_hostname="nginxtest.com", server_hostname="nginxtest.com" )
# 注释掉的是Python3的写法,Python2 如果这么写会报 TypeError: __init__() got an unexpected keyword argument 'server_hostname'
ss = pool.urlopen("GET","/healthcheck",headers={"Host": "nginxtest.com"},assert_same_host=False)
print(ss.status)
方法2
用urllib3 封装 HTTPAdapter
# 封装函数 resolved_ip是目标IP
import requests
class HostHeaderSSLAdapter(requests.adapters.HTTPAdapter):
def __init__(self, resolved_ip):
super(HostHeaderSSLAdapter,self).__init__() #python2
#super().__init__() #python3
self.resolved_ip = resolved_ip
def send(self, request, **kwargs):
from urlparse import urlparse #python2
#from urllib.parse import urlparse #python3
connection_pool_kwargs = self.poolmanager.connection_pool_kw
result = urlparse(request.url)
if result.scheme == 'https' and self.resolved_ip:
request.url = request.url.replace(
'https://' + result.hostname,
'https://' + self.resolved_ip,
)
#connection_pool_kwargs['server_hostname'] = result.hostname # SNI python2 需要屏蔽掉 不然会报 非预期的字段 key_server_hostname
connection_pool_kwargs['assert_hostname'] = result.hostname
# overwrite the host header
request.headers['Host'] = result.hostname
else:
# theses headers from a previous request may have been left
#connection_pool_kwargs.pop('server_hostname', None) #python2 需要屏蔽掉
connection_pool_kwargs.pop('assert_hostname', None)
return super(HostHeaderSSLAdapter, self).send(request, **kwargs)
#使用-------------------------------------------------------------------------->
url = "https://nginxtest.com/healthcheck"
session = requests.Session()
session.mount('https://', HostHeaderSSLAdapter("123.456.23.123"))
try:
r = session.get(url)
except Exception as e:
return "err : "+ str(e)
if r.status_code != 200:
return "not 200 " + "resp : "+ r.text
以上就是整个解决过程
参考引用:
https://www.zhihu.com/question/266700601/answer/2263452766
https://stackoverflow.com/questions/22609385/python-requests-library-define-specific-dns