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

posted @ 2022-12-22 21:45  zscbest  阅读(1527)  评论(0编辑  收藏  举报