Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
sebfournier95 opened this issue Jun 22, 2022 · 8 comments

Comments

@sebfournier95
Copy link

sebfournier95 commented Jun 22, 2022

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)')))
@graingert
Copy link
Contributor

see also python/cpython#93927

@pquentin
Copy link
Member

pquentin commented Jun 24, 2022

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.

@sebfournier95
Copy link
Author

sebfournier95 commented Jun 24, 2022

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 ?

@pquentin
Copy link
Member

pquentin commented Jun 24, 2022

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)

@sebfournier95
Copy link
Author

Thank you that works well.

al42and added a commit to al42and/oppna-luftfartygsregistret that referenced this issue Jan 8, 2023
Ubuntu 22.04 has new OpenSSL, which is too strict.

Fix based on urllib3/urllib3#2653
al42and added a commit to al42and/oppna-atervinningsdata that referenced this issue Jan 14, 2023
Ubuntu 22.04 has new OpenSSL, which is too strict.

Fix based on urllib3/urllib3#2653
al42and added a commit to al42and/oppna-atervinningsdata that referenced this issue Jan 14, 2023
Ubuntu 22.04 has new OpenSSL, which is too strict.

Fix based on urllib3/urllib3#2653
polyrabbit added a commit to polyrabbit/hacker-news-digest that referenced this issue Jul 11, 2023
polyrabbit added a commit to polyrabbit/hacker-news-digest that referenced this issue Jul 11, 2023
polyrabbit added a commit to polyrabbit/hacker-news-digest that referenced this issue Jul 11, 2023
Feature image obeys width/height style attr
heber-augusto added a commit to heber-augusto/devops-pysus-get-files that referenced this issue Jul 17, 2023
Ajuste do código (conforme sugerido em urllib3/urllib3#2653), para evitar erro "UNSAFE_LEGACY_RENEGOTIATION_DISABLED"
@Dhruv97Sharma
Copy link

Dhruv97Sharma commented Sep 25, 2023

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!

PierreMesure pushed a commit to civictechsweden/oppna-luftfartygsregistret that referenced this issue May 1, 2024
Ubuntu 22.04 has new OpenSSL, which is too strict.

Fix based on urllib3/urllib3#2653
PierreMesure pushed a commit to civictechsweden/oppna-atervinningsdata that referenced this issue May 2, 2024
Ubuntu 22.04 has new OpenSSL, which is too strict.

Fix based on urllib3/urllib3#2653
PierreMesure pushed a commit to civictechsweden/oppna-atervinningsdata that referenced this issue May 2, 2024
Ubuntu 22.04 has new OpenSSL, which is too strict.

Fix based on urllib3/urllib3#2653
PierreMesure pushed a commit to civictechsweden/oppna-atervinningsdata that referenced this issue May 2, 2024
Ubuntu 22.04 has new OpenSSL, which is too strict.

Fix based on urllib3/urllib3#2653
@AndreikaTallinn
Copy link

AndreikaTallinn commented Oct 29, 2024

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!

@AndreikaTallinn
Copy link

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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants