Access an HTTPS service by IP address

更新时间:
复制 MD 格式

Remote services in a VPC only support access through a domain name. Requests made directly with an IP address fail. This topic describes how to access an HTTPS service by using its IP address by adding the domain name to the request's Host header. This method is useful for remote access scenarios, like Spark or User-Defined Function (UDF) tasks.

HTTPS access failure by IP address

Error message

SSL: no alternative certificate subject name matches target host name '47.116.XX.XX'
More details here: https://curl.haxx.se/docs/sslcerts.html 
curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. 
To learn more about this situation and how to fix it, please visit the web page mentioned above.

Problem description

When a Spark or UDF task uses an IP address to access a remote service like KMS or OSS over HTTPS in a VPC, the request fails with the error shown above.

Solution

To fix the SSL certificate validation failure that occurs when using an IP address to access an HTTPS service, add the service's domain name to the request's Host header.

1. Obtain the IP address of the remote service.

Use ping

From a Windows or Linux console, run the following command to get the IP address of the remote service.

ping service.cn-shanghai-vpc.maxcompute.aliyun-inc.com
  • Windows result:

    PS C:\Users\xxx> ping service.cn-shanghai-vpc.maxcompute.aliyun-inc.com
    Ping service.cn-shanghai-vpc.maxcompute.aliyun-inc.com [100.103.104.45] xxx
  • Linux result:

    [root@iZbxxx ~]# ping service.cn-shanghai-vpc.maxcompute.aliyun-inc.com
    PING service.cn-shanghai-vpc.maxcompute.aliyun-inc.com (100.103.104.45) 56(84) bytes of data.

Use dig

  1. Install bind-utils in your Windows or Linux environment.

    • Windows

      Download BIND9.17.12.x64.zip, extract it to a directory such as D:\install\BIND9.17.12.x64, and then add this path to the Path environment variable in Windows.

    • Linux (CentOS)

      Run sudo yum install bind-utils to install the package.

  2. Run the following command in your console:

    dig service.cn-shanghai-vpc.maxcompute.aliyun-inc.com

    Windows

    PS C:\Users\xxx> dig service.cn-shanghai-vpc.maxcompute.aliyun-inc.com
    ; <<>> DiG 9.17.12 <<>> service.cn-shanghai-vpc.maxcompute.aliyun-inc.com
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49974
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
    ;; OPT PSEUDOSECTION:
    ; EDNS: version: 0, flags:; udp: 4000
    ;; QUESTION SECTION:
    ;service.cn-shanghai-vpc.maxcompute.aliyun-inc.com. IN A
    ;; ANSWER SECTION:
    service.cn-shanghai-vpc.maxcompute.aliyun-inc.com. 1 IN A 100.103.104.45
    ;; Query time: 4 msec
    ;; SERVER: 10.61.150.xxx
    ;; WHEN: Wed Jan 08 14xxx

    Linux

    [root@iZbxxx ~]# dig service.cn-shanghai-vpc.maxcompute.aliyun-inc.com
    ; &lt;&lt;&gt;> DiG 9.11.4-P2-RedHat-9.11.4 &lt;&lt;&gt;> service.cn-shanghai-vpc.maxcompute.aliyun-inc.com
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36725
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
    ;; OPT PSEUDOSECTION:
    ; EDNS: version: 0, flags:; udp: 4096
    ;; QUESTION SECTION:
    ;service.cn-shanghai-vpc.maxcompute.aliyun-inc.com. IN A
    ;; ANSWER SECTION:
    service.cn-shanghai-vpc.maxcompute.aliyun-inc.com. 1 IN A 100.103.104.45
    ;; Query time: 2 msec
    ;; SERVER: 100.100.2.xxx
    ;; WHEN: Wed Jan 08 14xxx

2. Configure the HTTP client.

The following code samples show how to create a custom HTTP adapter for different Python versions. This adapter sends the IP address in the request URL but uses the original domain name in the Host header for SSL validation. Before publishing your task, test remote access from the correct network environment.

  • Python 2

    # _*_ coding: utf-8 _*_
    # only for python2
    import requests
    from urlparse import urlparse
    class HostHeaderSSLAdapter(requests.adapters.HTTPAdapter):
        def __init__(self, resolved_ip):
            super(HostHeaderSSLAdapter,self).__init__()
            self.resolved_ip = resolved_ip
        def send(self, request, **kwargs):
            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['assert_hostname'] = result.hostname
                request.headers['Host'] = result.hostname
            else:
                connection_pool_kwargs.pop('assert_hostname', None)
            return super(HostHeaderSSLAdapter, self).send(request, **kwargs)
    def access_url(url, resolved_ip):
        session = requests.Session()
        # Get the hostname from the URL.
        parsed_url = urlparse(url)
        hostname = parsed_url.hostname
        session.mount('https://'+hostname, HostHeaderSSLAdapter(resolved_ip))
        try:
            r = session.get(url)
        except Exception as e:
            print("Error: "+ str(e))
        else:
            if r.status_code != 200:
                print("Request failed with non-200 status. Response: "+ r.text)
            else:
                print("Success. Response: "+ r.text)
    if __name__ == "__main__":
        # Obtain the IP address by using 'dig' for the domain name within the VPC environment.
        # Test a VPC address.
        #access_url("https://service.cn-shanghai-vpc.maxcompute.aliyun-inc.com", "100.103.104.45")
        # Test a public network address.
        access_url("https://service.cn-shanghai.maxcompute.aliyun.com", "47.116.XX.XX")
  • Python 3

    # _*_ coding: utf-8 _*_
    import requests
    from urllib.parse import urlparse 
    class HostHeaderSSLAdapter(requests.adapters.HTTPAdapter):
        def __init__(self, resolved_ip):
            super().__init__()                                        
            self.resolved_ip = resolved_ip
        def send(self, request, **kwargs):
            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  
                # Overwrite the Host header.
                request.headers['Host'] = result.hostname
            else:
                # Clear headers that might be left from a previous request.
                connection_pool_kwargs.pop('server_hostname', None)           
            return super().send(request, **kwargs)
    def access_url(url, resolved_ip):
        session = requests.Session()
        # Get the hostname from the URL.
        parsed_url = urlparse(url)
        hostname = parsed_url.hostname
        session.mount('https://'+hostname, HostHeaderSSLAdapter(resolved_ip))
        try:
            r = session.get(url)
        except Exception as e:
            print("Error: "+ str(e))
        else:
            if r.status_code != 200:
                print("Request failed with non-200 status. Response: "+ r.text)
            else:
                print("Success. Response: "+ r.text)
    if __name__ == "__main__":
        # Obtain the IP address by using 'dig' for the domain name within the VPC environment.
        # Test a VPC address.
        #access_url("https://service.cn-shanghai-vpc.maxcompute.aliyun-inc.com", "100.103.104.45")
        # Test a public network address.
        access_url("https://service.cn-shanghai.maxcompute.aliyun.com", "47.116.XX.XX")

Test results

Note

Run the test from the same network environment as your task. To access a service in a VPC, set up the Python environment inside that VPC and use the service URL and IP address resolved from within it.

  • Access the MaxCompute service from a local machine over the public network.

    if __name__ == "__main__":
        access_url( url="https://service.cn-shanghai.maxcompute.aliyun.com", resolved_ip="47.116.XX.XX")
    "D:\Program Files\Python311\python.exe" D:\ProgramData\PycharmProjects\pythontest1\text.py
    Success. Response: &lt;!DOCTYPE html&gt;
    <html>
    <head>
    <title>Welcome to tengine!</title>
    <style>
        body {
                width: 35em;
                margin: 0 auto;
                font-family: Tahoma, Verdana, Arial, sans-serif;
        }
    </style>
    </head>
    <body>
    <h1>Welcome to tengine!</h1>
    <p>If you see this page, the tengine web server is successfully installed and
    working. Further configuration is required.</p>
    <p>For online documentation and support please refer to
    <a href="http://tengine.taobao.org/">tengine.taobao.org</a>.</p>
    <p><em>Thank you for using tengine.</em></p>
    </body>
    </html>
  • Access the MaxCompute service from a Linux ECS instance over the public network.

    [root@iZbp1ehm6ky76ig8n1jd8dZ opt]# python3 text.py
    Success. Response: &lt;!DOCTYPE html&gt;
    <html>
    <head>
    <title>Welcome to tengine!</title>
    <style>
        body {
            width: 35em;
            margin: 0 auto;
            font-family: Tahoma, Verdana, Arial, sans-serif;
        }
    </style>
    </head>
    <body>
    <h1>Welcome to tengine!</h1>
    <p>If you see this page, the tengine web server is successfully installed and
    working. Further configuration is required.</p>
    <p>For online documentation and support please refer to
    <a href="http://tengine.taobao.org/">tengine.taobao.org</a>.</p>
    <p><em>Thank you for using tengine.</em></p>
    </body>
    </html>