VPC 内のリモートサービスは、ドメイン名を使用したアクセスのみをサポートしています。IP アドレスを直接使用したリクエストは失敗します。このトピックでは、リクエストの Host ヘッダーにドメイン名を追加することで、IP アドレスを使用して HTTPS サービスにアクセスする方法について説明します。この方法は、Spark やユーザー定義関数 (UDF) タスクなどのリモートアクセスシナリオで役立ちます。
IP アドレスによる HTTPS アクセスの失敗
エラーメッセージ
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.
問題の説明
Spark または UDF タスクが VPC 内で IP アドレスを使用して KMS や OSS などのリモートサービスに HTTPS 経由でアクセスすると、上記のエラーが発生してリクエストが失敗します。
ソリューション
IP アドレスを使用して HTTPS サービスにアクセスする際に発生する SSL 証明書の検証エラーを解決するには、リクエストの Host ヘッダーにサービスのドメイン名を追加します。
1. リモートサービスの IP アドレスを取得します。
PING を使用
Windows または Linux のコンソールから次のコマンドを実行して、リモートサービスの IP アドレスを取得します。
ping service.cn-shanghai-vpc.maxcompute.aliyun-inc.com
-
Windows の結果:
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 の結果:
[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.
dig を使用
-
Windows または Linux 環境に bind-utils をインストールします。
-
Windows
BIND9.17.12.x64.zip をダウンロードし、
D:\install\BIND9.17.12.x64などのディレクトリに解凍してから、このパスを Windows の Path 環境変数に追加します。 -
Linux (CentOS)
sudo yum install bind-utilsを実行してパッケージをインストールします。
-
-
コンソールで次のコマンドを実行します。
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. HTTP クライアントを設定します。
次のコードサンプルは、異なる Python バージョン向けにカスタム HTTP アダプターを作成する方法を示しています。このアダプターは、リクエスト URL では IP アドレスを送信しますが、SSL 証明書の検証には Host ヘッダーで元のドメイン名を使用します。タスクを公開する前に、正しいネットワーク環境からリモートアクセスをテストしてください。
-
Python 2
# _*_ coding: utf-8 _*_ # Python 2 専用 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() # 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__": # VPC 環境内のドメイン名に対して 'dig' を使用して IP アドレスを取得します。 # VPC アドレスをテストします。 #access_url("https://service.cn-shanghai-vpc.maxcompute.aliyun-inc.com", "100.103.104.45") # パブリックネットワークアドレスをテストします。 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 # Host ヘッダーを上書きします。 request.headers['Host'] = result.hostname else: # 前回のリクエストから残っている可能性のあるヘッダーをクリアします。 connection_pool_kwargs.pop('server_hostname', None) return super().send(request, **kwargs) def access_url(url, resolved_ip): session = requests.Session() # 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__": # VPC 環境内のドメイン名に対して 'dig' を使用して IP アドレスを取得します。 # VPC アドレスをテストします。 #access_url("https://service.cn-shanghai-vpc.maxcompute.aliyun-inc.com", "100.103.104.45") # パブリックネットワークアドレスをテストします。 access_url("https://service.cn-shanghai.maxcompute.aliyun.com", "47.116.XX.XX")
テスト結果
タスクと同じネットワーク環境からテストを実行してください。VPC 内のサービスにアクセスするには、その VPC 内に Python 環境を構築し、VPC 内から解決されたサービス URL と IP アドレスを使用してください。
-
ローカルマシンからパブリックネットワーク経由で MaxCompute サービスにアクセスします。
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> -
Linux ECS インスタンスからパブリックネットワーク経由で MaxCompute サービスにアクセスします。
[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>