Visitar URL original
ssl.SSLError: [SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1131) · Issue #2653 · urllib3/urllib3 · GitHub
Skip to content

ssl.SSLError: [SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1131) #2653

Closed

Description

@sebfournier95

Subject

Error on SSL Connection

Environment

OS Windows-10-10.0.19044-SP0
Python 3.8.12
urllib3 1.26.8

Steps to Reproduce

import os
import shutil
import datetime
import urllib3

# Different tools to download NOAA datas

class NOAA_Tools:
    def __init__(self, race_name='TEST', north_coord=52, west_coord=-75, south_coord=10, east_coord=0):
        self.race_name = race_name

        # Convention: [N(positif), W(negatif), S(negatif), E(positif)]

        self.north_coord = north_coord
        self.west_coord = west_coord
        self.east_coord = east_coord
        self.south_coord = south_coord

        self.area = [north_coord, west_coord, south_coord, east_coord]

    # Download forecast wave moment from today 000 hour to +384 hour (+16 jours), 6 days before are available
    def WW3_Wave_Moment(self, day, month, year, hour, archive_path='F:\\GRIBS\\NOAA\\WAVE_MOMENT\\'):
        if os.path.isdir(archive_path) is False:
            os.mkdir(archive_path)

        now = datetime.datetime.now()
        now_date = now.strftime('%d/%m/%Y')

        if month < 10:
            month = str(month)
            month = month.zfill(2)

        if day < 10:
            day = str(day)
            day = day.zfill(2)

        forecast_date = str(day) + '/' + str(month) + '/' + str(year)
        forecast_date_hour = str(day) + '/' + str(month) + '/' + str(year) + ' - ' + str(hour) + ':00:00'

        if forecast_date == now_date:
            now_date_hour = now_date + ' - ' + '00:00:00'
            now_date_hour_timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(now_date_hour, "%d/%m/%Y - %H:%M:%S"))

            forecast_date_hour_timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(forecast_date_hour, "%d/%m/%Y - %H:%M:%S"))

            forecast_hour = forecast_date_hour_timestamp - now_date_hour_timestamp
        else:
            now_date_hour = now_date + ' - ' + '00:00:00'
            now_date_obj = datetime.datetime.strptime(now_date_hour, "%d/%m/%Y - %H:%M:%S")

            forecast_date_obj = datetime.datetime.strptime(forecast_date_hour, "%d/%m/%Y - %H:%M:%S")

            if forecast_date_obj > now_date_obj:
                forecast_date_hour_timestamp = datetime.datetime.timestamp(forecast_date_obj)
                now_date_hour_timestamp = datetime.datetime.timestamp(now_date_obj)

                forecast_hour = forecast_date_hour_timestamp - now_date_hour_timestamp

                day = now_date_obj.day
                if day < 10:
                    day = str(day)
                    day = day.zfill(2)

            elif forecast_date_obj < now_date_obj:
                start_date_hour = forecast_date+ ' - ' + '00:00:00'
                start_date_hour_timestamp = datetime.datetime.timestamp(
                    datetime.datetime.strptime(start_date_hour, "%d/%m/%Y - %H:%M:%S"))

                forecast_date_hour_timestamp = datetime.datetime.timestamp(
                    datetime.datetime.strptime(forecast_date_hour, "%d/%m/%Y - %H:%M:%S"))

                forecast_hour = forecast_date_hour_timestamp - start_date_hour_timestamp

        timer_tmp = int(forecast_hour / 3600)

        if timer_tmp < 10:
            timer = str(timer_tmp)
            timer = timer.zfill(3)
        elif timer_tmp > 10 & timer_tmp < 100:
            timer = str(timer_tmp)
            timer = timer.zfill(3)

        url_base = 'https://nomads.ncep.noaa.gov/cgi-bin/filter_gfswave.pl?file=gfswave.t00z.global.0p16.'
        url_time = 'f' + str(timer) + '.grib2&all_lev=on&all_var=on'
        url_sub = '&subregion=&leftlon=' + str(self.west_coord) + '&rightlon=' + str(
            self.east_coord) + '&toplat=' + str(self.north_coord) + '&bottomlat=' + str(self.south_coord)

        url_end = '&dir=%2Fgfs.' + str(year) + str(month) + str(day) + '%2F00%2Fwave%2Fgridded'

        url = url_base + url_time + url_sub + url_end

        print('URL : ', url)

        #filename = url.split('=')[1].split('&')[0]
        outfile = url.split('=')[1].split('&')[0].split('.grib2')[0] + '_' + str(day) + str(month) + str(year) + '_' + str(hour) + '.grib2'

        c = urllib3.PoolManager()

        with c.request('GET', url, preload_content=False) as resp, open(outfile, 'wb') as out_file:
            shutil.copyfileobj(resp, out_file)

        resp.release_conn()

TEST = NOAA_Tools(race_name='WW3_Forecast', north_coord=52, west_coord=-75, south_coord=10, east_coord=0)
TEST.WW3_Wave_Moment(22, 6, 2022, 12, archive_path='./TMP/') # Download wave spectrum for 14 June 2022 at 12:00:00

Expected Behavior

Actual Behavior

Traceback (most recent call last):
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\connectionpool.py", line 703, in urlopen
    httplib_response = self._make_request(
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\connectionpool.py", line 386, in _make_request
    self._validate_conn(conn)
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\connectionpool.py", line 1040, in _validate_conn
    conn.connect()
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\connection.py", line 416, in connect
    self.sock = ssl_wrap_socket(
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\util\ssl_.py", line 449, in ssl_wrap_socket
    ssl_sock = _ssl_wrap_socket_impl(
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\util\ssl_.py", line 493, in _ssl_wrap_socket_impl
    return ssl_context.wrap_socket(sock, server_hostname=server_hostname)
  File "C:\Users\Seb\anaconda3\envs\routing\lib\ssl.py", line 500, in wrap_socket
    return self.sslsocket_class._create(
  File "C:\Users\Seb\anaconda3\envs\routing\lib\ssl.py", line 1040, in _create
    self.do_handshake()
  File "C:\Users\Seb\anaconda3\envs\routing\lib\ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1131)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:/Users/Seb/PycharmProjects/RTools/tmp.py", line 110, in <module>
    TEST.WW3_Wave_Moment(22, 6, 2022, 12, archive_path='./TMP/') # Download wave spectrum for 14 June 2022 at 12:00:00
  File "C:/Users/Seb/PycharmProjects/RTools/tmp.py", line 104, in WW3_Wave_Moment
    with c.request('GET', url, preload_content=False) as resp, open(outfile, 'wb') as out_file:
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\request.py", line 74, in request
    return self.request_encode_url(
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\request.py", line 96, in request_encode_url
    return self.urlopen(method, url, **extra_kw)
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\poolmanager.py", line 375, in urlopen
    response = conn.urlopen(method, u.request_uri, **kw)
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\connectionpool.py", line 813, in urlopen
    return self.urlopen(
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\connectionpool.py", line 813, in urlopen
    return self.urlopen(
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\connectionpool.py", line 813, in urlopen
    return self.urlopen(
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\connectionpool.py", line 785, in urlopen
    retries = retries.increment(
  File "C:\Users\Seb\anaconda3\envs\routing\lib\site-packages\urllib3\util\retry.py", line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='nomads.ncep.noaa.gov', port=443): Max retries exceeded with url: /cgi-bin/filter_gfswave.pl?file=gfswave.t00z.global.0p16.f012.grib2&all_lev=on&all_var=on&subregion=&leftlon=-75&rightlon=0&toplat=52&bottomlat=10&dir=%2Fgfs.20220622%2F00%2Fwave%2Fgridded (Caused by SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1131)')))

Activity

  1. graingert commented on Jun 23, 2022

    @graingert
    Contributor
  2. pquentin commented on Jun 24, 2022

    @pquentin
    Member

    What is the output of python -c 'import ssl; print(ssl.OPENSSL_VERSION)'? https://stackoverflow.com/a/71646353 suggests that this happens with OpenSSL 3.0 when connecting to legacy US government websites that disable renegotiation without signalling it correctly.

    To solve the issue, you need a custom SSL context (again the StackOverflow answers explains how to do it). Thanks to @graingert's in Python 3.12 you will be able to switch from 0x4 to ssl.OP_LEGACY_SERVER_CONNECT.

  3. sebfournier95 commented on Jun 24, 2022

    @sebfournier95
    Author

    Thank you

    python -c 'import ssl; print(ssl.OPENSSL_VERSION) = OpenSSL 3.0.3 3 May 2022

    Do I need to pass in Python 3.12, I am under Python 3.9 ?

  4. pquentin commented on Jun 24, 2022

    @pquentin
    Member

    Python 3.12 will be released in October 2023, I just mentioned it for future readers. You can downgrade to OpenSSL 1.1.1 or use the following code:

    import urllib3
    from urllib3.util.ssl_ import create_urllib3_context
    
    ctx = create_urllib3_context()
    ctx.load_default_certs()
    ctx.options |= 0x4  # ssl.OP_LEGACY_SERVER_CONNECT
    
    with urllib3.PoolManager(ssl_context=ctx) as http:
        r = http.request("GET", "https://nomads.ncep.noaa.gov/")
        print(r.status)
  5. sebfournier95 commented on Jun 24, 2022

    @sebfournier95
    Author

    Thank you that works well.

  6. added a commit that references this issue on Jul 17, 2023
  7. Dhruv97Sharma commented on Sep 25, 2023

    @Dhruv97Sharma

    Python 3.12 will be released in October 2023, I just mentioned it for future readers. You can downgrade to OpenSSL 1.1.1 or use the following code:

    import urllib3
    from urllib3.util.ssl_ import create_urllib3_context
    
    ctx = create_urllib3_context()
    ctx.load_default_certs()
    ctx.options |= 0x4  # ssl.OP_LEGACY_SERVER_CONNECT
    
    with urllib3.PoolManager(ssl_context=ctx) as http:
        r = http.request("GET", "https://nomads.ncep.noaa.gov/")
        print(r.status)

    Really worked wonders for my case where I modified the given approach to cater to my case where I needed this with the requests library:

    import urllib3, requests
    from urllib3.util.ssl_ import create_urllib3_context
    from requests.adapters import HTTPAdapter
    
    your_url = "https://nomads.ncep.noaa.gov/"
    
    class ExampleCustomSslContextHttpAdapter(HTTPAdapter):
            """"Transport adapter" that allows us to use a custom ssl context object with the requests."""
            def init_poolmanager(self, connections, maxsize, block=False):
                ctx = create_urllib3_context()
                ctx.load_default_certs()
                ctx.options |= 0x4  # ssl.OP_LEGACY_SERVER_CONNECT
                self.poolmanager = urllib3.PoolManager(ssl_context=ctx)
    
    
    
    session = requests.Session()
    session.mount(your_url, CustomSslContextHttpAdapter())
    response = session.get(your_url)

    This gave me required results! Thanks! Hope this helps someone!

  8. AndreikaTallinn commented on Oct 29, 2024

    @AndreikaTallinn

    Thanks all! Both solutions (with pure urllib3 and with requests) work correctly with straight connection.
    Unfortunately it does not work with proxied connection. None of the solutions work:

    The non-working solution with pure urllib3:
    ` ctx = create_urllib3_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    ctx.options |= ssl.OP_LEGACY_SERVER_CONNECT

        proxy = urllib3.ProxyManager(
            proxy_url=http_proxy,
            proxy_ssl_context=ctx,
            use_forwarding_for_https=True,
            cert_reqs='CERT_NONE',
            proxy_assert_hostname=True
        )
        
        server_response = proxy.urlopen(
                method='POST',
                url=argocd_url,
                body=get_token_request_body,
                timeout=Timeout(connect=3.0, read=10.0),
                context=ctx
         )
    

    `

    The non-working solution with requests:

    `def get_legacy_ssl_session(self):
    if self.ssl_session is None:
    ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
    ctx.options |= ssl.OP_LEGACY_SERVER_CONNECT
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    session = requests.session()
    session.mount('http://', CustomHttpAdapter(ctx))
    session.mount('https://', CustomHttpAdapter(ctx))
    self.ssl_session = session
    self.ssl_session.proxies.update(self.proxies)
    return self.ssl_session

    class CustomHttpAdapter (requests.adapters.HTTPAdapter):
        # "Transport adapter" that allows us to use custom ssl_context.
    
        def __init__(self, ssl_context=None, **kwargs):
            self.ssl_context = ssl_context
            super().__init__(**kwargs)
    
        def init_poolmanager(self, connections, maxsize, block=False):
            self.poolmanager = urllib3.poolmanager.PoolManager(
                num_pools=connections, maxsize=maxsize,
                block=block, ssl_context=self.ssl_context,
                proxy_ssl_context=self.ssl_context
            )`
    

    both variants return SSL error: SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1000)')

    If somebody has a solution, that is working with proxy, I will be very grateful!
    Thanks in advance!

  9. AndreikaTallinn commented on Oct 29, 2024

    @AndreikaTallinn

    sorry for the formatting. do not know why Github consider half of the code fragment as a plain text

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions