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
-
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-utilsto install the package.
-
-
Run the following command in your console:
dig service.cn-shanghai-vpc.maxcompute.aliyun-inc.comWindows
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 14xxxLinux
[root@iZbxxx ~]# dig service.cn-shanghai-vpc.maxcompute.aliyun-inc.com ; <<>> DiG 9.11.4-P2-RedHat-9.11.4 <<>> 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
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: <!DOCTYPE html> <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: <!DOCTYPE html> <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>