Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Cannot use VPC Endpoint for Redshift Serverless

オープン
#246 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
35/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
停滞
技術スタック
aws, python

調査の方向性

まず、報告された redshift_connector.connect 呼び出しを iam=True と VPC の endpoint_url で再現し、次に stack trace に示されている IAM 認証情報リクエストを追跡します。VPC エンドポイント経由で Redshift Serverless への接続が、SSL ホスト名検証エラーなしに成功すれば完了です。

索引モデルが issue の本文から書いたものです。

説明

Driver version

2.1.4

Redshift version

PostgreSQL 8.0.2 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3), Redshift 1.0.79237

Client Operating System

Ubuntu 22.04.4 LTS

Python version

3.11.9

Problem description

  1. Expected behaviour: Create connection to Redshift Serverless with VPC Endpoint
  2. Actual behaviour: SSL validation failed
  3. Error message/stack trace:
    SSLError: SSL validation failed for https://vpce-06c30eb85ac87559f-vu0ekkrw.redshift-serverless.ap-southeast-1.vpce.amazonaws.com/ hostname 'vpce-06c30eb85ac87559f-vu0ekkrw.redshift-serverless.ap-southeast-1.vpce.amazonaws.com' doesn't match 'redshift-serverless.ap-southeast-1.amazonaws.com'

Python Driver trace logs

---------------------------------------------------------------------------
CertificateError                          Traceback (most recent call last)
File /opt/conda/lib/python3.11/site-packages/urllib3/connectionpool.py:715, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    714 # Make the request on the httplib connection object.
--> 715 httplib_response = self._make_request(
    716     conn,
    717     method,
    718     url,
    719     timeout=timeout_obj,
    720     body=body,
    721     headers=headers,
    722     chunked=chunked,
    723 )
    725 # If we're going to release the connection in ``finally:``, then
    726 # the response doesn't need to know about the connection. Otherwise
    727 # it will also try to release it and we'll have a double-release
    728 # mess.

File /opt/conda/lib/python3.11/site-packages/urllib3/connectionpool.py:404, in HTTPConnectionPool._make_request(self, conn, method, url, timeout, chunked, **httplib_request_kw)
    403 try:
--> 404     self._validate_conn(conn)
    405 except (SocketTimeout, BaseSSLError) as e:
    406     # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout.

File /opt/conda/lib/python3.11/site-packages/urllib3/connectionpool.py:1060, in HTTPSConnectionPool._validate_conn(self, conn)
   1059 if not getattr(conn, "sock", None):  # AppEngine might not have  `.sock`
-> 1060     conn.connect()
   1062 if not conn.is_verified:

File /opt/conda/lib/python3.11/site-packages/urllib3/connection.py:472, in HTTPSConnection.connect(self)
    463         warnings.warn(
    464             (
    465                 "Certificate for {0} has no `subjectAltName`, falling back to check for a "
   (...)
    470             SubjectAltNameWarning,
    471         )
--> 472     _match_hostname(cert, self.assert_hostname or server_hostname)
    474 self.is_verified = (
    475     context.verify_mode == ssl.CERT_REQUIRED
    476     or self.assert_fingerprint is not None
    477 )

File /opt/conda/lib/python3.11/site-packages/urllib3/connection.py:545, in _match_hostname(cert, asserted_hostname)
    544 try:
--> 545     match_hostname(cert, asserted_hostname)
    546 except CertificateError as e:

File /opt/conda/lib/python3.11/site-packages/urllib3/util/ssl_match_hostname.py:155, in match_hostname(cert, hostname)
    154 elif len(dnsnames) == 1:
--> 155     raise CertificateError("hostname %r doesn't match %r" % (hostname, dnsnames[0]))
    156 else:

CertificateError: hostname 'vpce-06c30eb85ac87559f-vu0ekkrw.redshift-serverless.ap-southeast-1.vpce.amazonaws.com' doesn't match 'redshift-serverless.ap-southeast-1.amazonaws.com'

During handling of the above exception, another exception occurred:

SSLError                                  Traceback (most recent call last)
File /opt/conda/lib/python3.11/site-packages/botocore/httpsession.py:464, in URLLib3Session.send(self, request)
    463 request_target = self._get_request_target(request.url, proxy_url)
--> 464 urllib_response = conn.urlopen(
    465     method=request.method,
    466     url=request_target,
    467     body=request.body,
    468     headers=request.headers,
    469     retries=Retry(False),
    470     assert_same_host=False,
    471     preload_content=False,
    472     decode_content=False,
    473     chunked=self._chunked(request.headers),
    474 )
    476 http_response = botocore.awsrequest.AWSResponse(
    477     request.url,
    478     urllib_response.status,
    479     urllib_response.headers,
    480     urllib_response,
    481 )

File /opt/conda/lib/python3.11/site-packages/urllib3/connectionpool.py:801, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    799     e = ProtocolError("Connection aborted.", e)
--> 801 retries = retries.increment(
    802     method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
    803 )
    804 retries.sleep()

File /opt/conda/lib/python3.11/site-packages/urllib3/util/retry.py:527, in Retry.increment(self, method, url, response, error, _pool, _stacktrace)
    525 if self.total is False and error:
    526     # Disabled, indicate to re-raise the error.
--> 527     raise six.reraise(type(error), error, _stacktrace)
    529 total = self.total

File /opt/conda/lib/python3.11/site-packages/urllib3/packages/six.py:769, in reraise(tp, value, tb)
    768 if value.__traceback__ is not tb:
--> 769     raise value.with_traceback(tb)
    770 raise value

File /opt/conda/lib/python3.11/site-packages/urllib3/connectionpool.py:715, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    714 # Make the request on the httplib connection object.
--> 715 httplib_response = self._make_request(
    716     conn,
    717     method,
    718     url,
    719     timeout=timeout_obj,
    720     body=body,
    721     headers=headers,
    722     chunked=chunked,
    723 )
    725 # If we're going to release the connection in ``finally:``, then
    726 # the response doesn't need to know about the connection. Otherwise
    727 # it will also try to release it and we'll have a double-release
    728 # mess.

File /opt/conda/lib/python3.11/site-packages/urllib3/connectionpool.py:404, in HTTPConnectionPool._make_request(self, conn, method, url, timeout, chunked, **httplib_request_kw)
    403 try:
--> 404     self._validate_conn(conn)
    405 except (SocketTimeout, BaseSSLError) as e:
    406     # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout.

File /opt/conda/lib/python3.11/site-packages/urllib3/connectionpool.py:1060, in HTTPSConnectionPool._validate_conn(self, conn)
   1059 if not getattr(conn, "sock", None):  # AppEngine might not have  `.sock`
-> 1060     conn.connect()
   1062 if not conn.is_verified:

File /opt/conda/lib/python3.11/site-packages/urllib3/connection.py:472, in HTTPSConnection.connect(self)
    463         warnings.warn(
    464             (
    465                 "Certificate for {0} has no `subjectAltName`, falling back to check for a "
   (...)
    470             SubjectAltNameWarning,
    471         )
--> 472     _match_hostname(cert, self.assert_hostname or server_hostname)
    474 self.is_verified = (
    475     context.verify_mode == ssl.CERT_REQUIRED
    476     or self.assert_fingerprint is not None
    477 )

File /opt/conda/lib/python3.11/site-packages/urllib3/connection.py:545, in _match_hostname(cert, asserted_hostname)
    544 try:
--> 545     match_hostname(cert, asserted_hostname)
    546 except CertificateError as e:

File /opt/conda/lib/python3.11/site-packages/urllib3/util/ssl_match_hostname.py:155, in match_hostname(cert, hostname)
    154 elif len(dnsnames) == 1:
--> 155     raise CertificateError("hostname %r doesn't match %r" % (hostname, dnsnames[0]))
    156 else:

SSLError: hostname 'vpce-06c30eb85ac87559f-vu0ekkrw.redshift-serverless.ap-southeast-1.vpce.amazonaws.com' doesn't match 'redshift-serverless.ap-southeast-1.amazonaws.com'

During handling of the above exception, another exception occurred:

SSLError                                  Traceback (most recent call last)
Cell In[5], line 1
----> 1 conn = redshift_connector.connect(
      2      host=host,
      3      port=5439,
      4      database=db_name,
      5      iam=True,
      6      endpoint_url='https://vpce-06c30eb85ac87559f-vu0ekkrw.redshift-serverless.ap-southeast-1.vpce.amazonaws.com'
      7   )
      9 conn.autocommit = True
     10 # conn.run("VACUUM")

File /opt/conda/lib/python3.11/site-packages/redshift_connector/__init__.py:386, in connect(user, database, password, port, host, source_address, unix_sock, ssl, sslmode, timeout, max_prepared_statements, tcp_keepalive, application_name, replication, idp_host, db_user, app_id, app_name, preferred_role, principal_arn, access_key_id, secret_access_key, session_token, profile, credentials_provider, region, cluster_identifier, iam, client_id, idp_tenant, client_secret, partner_sp_id, idp_response_timeout, listen_port, login_to_rp, login_url, auto_create, db_groups, force_lowercase, allow_db_user_override, client_protocol_version, database_metadata_current_db_only, ssl_insecure, web_identity_token, role_session_name, role_arn, iam_disable_cache, auth_profile, endpoint_url, provider_name, scope, numeric_to_float, is_serverless, serverless_acct_id, serverless_work_group, group_federation, identity_namespace, idc_client_display_name, idc_region, issuer_url, token, token_type)
    383         _logger.debug("redshift_native_auth enabled")
    385 if not redshift_native_auth:
--> 386     IamHelper.set_iam_properties(info)
    388 _logger.debug(make_divider_block())
    389 _logger.debug("Connection arguments following validation and IAM auth (if applicable)")

File /opt/conda/lib/python3.11/site-packages/redshift_connector/iam_helper.py:149, in IamHelper.set_iam_properties(info)
    145     if info.cluster_identifier is None and not info._is_serverless and not info.is_cname:
    146         raise InterfaceError(
    147             "Invalid connection property setting. cluster_identifier must be provided when IAM is enabled"
    148         )
--> 149     IamHelper.set_iam_credentials(info)
    150 # Check for Browser based OAuth Native authentication
    151 NativeAuthPluginHelper.set_native_auth_plugin_properties(info)

File /opt/conda/lib/python3.11/site-packages/redshift_connector/iam_helper.py:230, in IamHelper.set_iam_credentials(info)
    227     IamHelper.set_cluster_identifier(provider, info)
    229 # Redshift database credentials  will be determined using the redshift client from boto3 API
--> 230 IamHelper.set_cluster_credentials(provider, info)
    232 # Redshift instance host and port must be retrieved
    233 IamHelper.set_cluster_host_and_port(provider, info)

File /opt/conda/lib/python3.11/site-packages/redshift_connector/iam_helper.py:460, in IamHelper.set_cluster_credentials(cred_provider, info)
    456     get_cred_args["customDomainName"] = info.host
    457 _logger.debug("Calling get_credentials with parameters %s", get_cred_args)
    458 cred = typing.cast(
    459     typing.Dict[str, typing.Union[str, datetime.datetime]],
--> 460     client.get_credentials(**get_cred_args),
    461 )
    462 # re-map expiration for compatibility with redshift credential response
    463 cred["Expiration"] = cred["expiration"]

File /opt/conda/lib/python3.11/site-packages/botocore/client.py:565, in ClientCreator._create_api_method.<locals>._api_call(self, *args, **kwargs)
    561     raise TypeError(
    562         f"{py_operation_name}() only accepts keyword arguments."
    563     )
    564 # The "self" in this scope is referring to the BaseClient.
--> 565 return self._make_api_call(operation_name, kwargs)

File /opt/conda/lib/python3.11/site-packages/botocore/client.py:1001, in BaseClient._make_api_call(self, operation_name, api_params)
    997     maybe_compress_request(
    998         self.meta.config, request_dict, operation_model
    999     )
   1000     apply_request_checksum(request_dict)
-> 1001     http, parsed_response = self._make_request(
   1002         operation_model, request_dict, request_context
   1003     )
   1005 self.meta.events.emit(
   1006     'after-call.{service_id}.{operation_name}'.format(
   1007         service_id=service_id, operation_name=operation_name
   (...)
   1012     context=request_context,
   1013 )
   1015 if http.status_code >= 300:

File /opt/conda/lib/python3.11/site-packages/botocore/client.py:1027, in BaseClient._make_request(self, operation_model, request_dict, request_context)
   1025 def _make_request(self, operation_model, request_dict, request_context):
   1026     try:
-> 1027         return self._endpoint.make_request(operation_model, request_dict)
   1028     except Exception as e:
   1029         self.meta.events.emit(
   1030             'after-call-error.{service_id}.{operation_name}'.format(
   1031                 service_id=self._service_model.service_id.hyphenize(),
   (...)
   1035             context=request_context,
   1036         )

File /opt/conda/lib/python3.11/site-packages/botocore/endpoint.py:119, in Endpoint.make_request(self, operation_model, request_dict)
    113 def make_request(self, operation_model, request_dict):
    114     logger.debug(
    115         "Making request for %s with params: %s",
    116         operation_model,
    117         request_dict,
    118     )
--> 119     return self._send_request(request_dict, operation_model)

File /opt/conda/lib/python3.11/site-packages/botocore/endpoint.py:202, in Endpoint._send_request(self, request_dict, operation_model)
    198 request = self.create_request(request_dict, operation_model)
    199 success_response, exception = self._get_response(
    200     request, operation_model, context
    201 )
--> 202 while self._needs_retry(
    203     attempts,
    204     operation_model,
    205     request_dict,
    206     success_response,
    207     exception,
    208 ):
    209     attempts += 1
    210     self._update_retries_context(context, attempts, success_response)

File /opt/conda/lib/python3.11/site-packages/botocore/endpoint.py:354, in Endpoint._needs_retry(self, attempts, operation_model, request_dict, response, caught_exception)
    352 service_id = operation_model.service_model.service_id.hyphenize()
    353 event_name = f"needs-retry.{service_id}.{operation_model.name}"
--> 354 responses = self._event_emitter.emit(
    355     event_name,
    356     response=response,
    357     endpoint=self,
    358     operation=operation_model,
    359     attempts=attempts,
    360     caught_exception=caught_exception,
    361     request_dict=request_dict,
    362 )
    363 handler_response = first_non_none_response(responses)
    364 if handler_response is None:

File /opt/conda/lib/python3.11/site-packages/botocore/hooks.py:412, in EventAliaser.emit(self, event_name, **kwargs)
    410 def emit(self, event_name, **kwargs):
    411     aliased_event_name = self._alias_event_name(event_name)
--> 412     return self._emitter.emit(aliased_event_name, **kwargs)

File /opt/conda/lib/python3.11/site-packages/botocore/hooks.py:256, in HierarchicalEmitter.emit(self, event_name, **kwargs)
    245 def emit(self, event_name, **kwargs):
    246     """
    247     Emit an event by name with arguments passed as keyword args.
    248 
   (...)
    254              handlers.
    255     """
--> 256     return self._emit(event_name, kwargs)

File /opt/conda/lib/python3.11/site-packages/botocore/hooks.py:239, in HierarchicalEmitter._emit(self, event_name, kwargs, stop_on_response)
    237 for handler in handlers_to_call:
    238     logger.debug('Event %s: calling handler %s', event_name, handler)
--> 239     response = handler(**kwargs)
    240     responses.append((handler, response))
    241     if stop_on_response and response is not None:

File /opt/conda/lib/python3.11/site-packages/botocore/retryhandler.py:207, in RetryHandler.__call__(self, attempts, response, caught_exception, **kwargs)
    204     retries_context = kwargs['request_dict']['context'].get('retries')
    205     checker_kwargs.update({'retries_context': retries_context})
--> 207 if self._checker(**checker_kwargs):
    208     result = self._action(attempts=attempts)
    209     logger.debug("Retry needed, action of: %s", result)

File /opt/conda/lib/python3.11/site-packages/botocore/retryhandler.py:284, in MaxAttemptsDecorator.__call__(self, attempt_number, response, caught_exception, retries_context)
    279 if retries_context:
    280     retries_context['max'] = max(
    281         retries_context.get('max', 0), self._max_attempts
    282     )
--> 284 should_retry = self._should_retry(
    285     attempt_number, response, caught_exception
    286 )
    287 if should_retry:
    288     if attempt_number >= self._max_attempts:
    289         # explicitly set MaxAttemptsReached

File /opt/conda/lib/python3.11/site-packages/botocore/retryhandler.py:320, in MaxAttemptsDecorator._should_retry(self, attempt_number, response, caught_exception)
    316         return True
    317 else:
    318     # If we've exceeded the max attempts we just let the exception
    319     # propagate if one has occurred.
--> 320     return self._checker(attempt_number, response, caught_exception)

File /opt/conda/lib/python3.11/site-packages/botocore/retryhandler.py:363, in MultiChecker.__call__(self, attempt_number, response, caught_exception)
    361 def __call__(self, attempt_number, response, caught_exception):
    362     for checker in self._checkers:
--> 363         checker_response = checker(
    364             attempt_number, response, caught_exception
    365         )
    366         if checker_response:
    367             return checker_response

File /opt/conda/lib/python3.11/site-packages/botocore/retryhandler.py:247, in BaseChecker.__call__(self, attempt_number, response, caught_exception)
    245     return self._check_response(attempt_number, response)
    246 elif caught_exception is not None:
--> 247     return self._check_caught_exception(
    248         attempt_number, caught_exception
    249     )
    250 else:
    251     raise ValueError("Both response and caught_exception are None.")

File /opt/conda/lib/python3.11/site-packages/botocore/retryhandler.py:416, in ExceptionRaiser._check_caught_exception(self, attempt_number, caught_exception)
    408 def _check_caught_exception(self, attempt_number, caught_exception):
    409     # This is implementation specific, but this class is useful by
    410     # coordinating with the MaxAttemptsDecorator.
   (...)
    414     # the MaxAttemptsDecorator is not interested in retrying the exception
    415     # then this exception just propagates out past the retry code.
--> 416     raise caught_exception

File /opt/conda/lib/python3.11/site-packages/botocore/endpoint.py:281, in Endpoint._do_get_response(self, request, operation_model, context)
    279     http_response = first_non_none_response(responses)
    280     if http_response is None:
--> 281         http_response = self._send(request)
    282 except HTTPClientError as e:
    283     return (None, e)

File /opt/conda/lib/python3.11/site-packages/botocore/endpoint.py:377, in Endpoint._send(self, request)
    376 def _send(self, request):
--> 377     return self.http_session.send(request)

File /opt/conda/lib/python3.11/site-packages/botocore/httpsession.py:491, in URLLib3Session.send(self, request)
    489     return http_response
    490 except URLLib3SSLError as e:
--> 491     raise SSLError(endpoint_url=request.url, error=e)
    492 except (NewConnectionError, socket.gaierror) as e:
    493     raise EndpointConnectionError(endpoint_url=request.url, error=e)

SSLError: SSL validation failed for https://vpce-06c30eb85ac87559f-vu0ekkrw.redshift-serverless.ap-southeast-1.vpce.amazonaws.com/ hostname 'vpce-06c30eb85ac87559f-vu0ekkrw.redshift-serverless.ap-southeast-1.vpce.amazonaws.com' doesn't match 'redshift-serverless.ap-southeast-1.amazonaws.com'

Reproduction code

conn = redshift_connector.connect(
     host=host,
     port=5439,
     database=db_name,
     iam=True,
     endpoint_url='https://vpce-06c30eb85ac87559f-vu0ekkrw.redshift-serverless.ap-southeast-1.vpce.amazonaws.com'
  )
主要言語
Python
スター
220
フォーク
86
PR マージ指標
30日以内にマージされた PR はありません

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

aws/amazon-redshift-python-driver のほかの issue

aws/amazon-redshift-python-driver の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。