Получение SSL: Certificate_verify_failed каждый раз, когда я пытаюсь войти в систему с помощью моего бота Discord на VPS

Я перемещаю своего бота со своего старого сломанного ноутбука на полноценный VPS. Я использую более старую асинхронную версию Discord.py (0.16.0), потому что я начал работать над этим задолго до того, как переписывание было чем-то; и у меня нет большого опыта работы с Linux, поэтому переход на Windows Server казался разумным. Я установил все те же пакеты (насколько мне известно), но я постоянно получаю сообщение об ошибке при запуске в строке bot.run():

ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)

Ошибка возникает во время рукопожатия, насколько я могу судить по трассировке стека, не то чтобы я знал, каковы последствия этого.

Эта установка работала несколько лет на двух разных ноутбуках (хотя и под управлением Windows 10) без каких-либо проблем, и сервер discord.py, похоже, не сталкивался с такими проблемами на машине Windows (это происходит на OS X, но из-за некоторая идиосинкразия Python 3.6, и это также происходило в Python 3.5. Тоже неправильная ОС.) Я попытался изменить установки Python и установить некоторые автоматические обновления Windows, а также выполнить некоторые команды, которые я нашел здесь и там: pip install certifi и pip install incremental.

После нескольких часов бездействия из любопытства: и разочарования, и из-за того, что у меня есть тестовый бот на одном сервере, который я не возражаю, чтобы его выбросили, я попробовал что-то, что я видел в обсуждении stackoverflow.

bot = commands.Bot(
    command_prefix='/',
    connector=aiohttp.TCPConnector(verify_ssl=False)
        ) 

(добавленная строка - это, в частности, весь connector kwarg, которого раньше не было.)

Странно то, что это ИЗМЕНИЛО трассировку стека, но на самом деле не изменило ошибку, как показано ниже:

Перед добавлением verify_ssl = False я получал ...

Traceback (most recent call last):
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 601, in _create_direct_connection
    local_addr=self._local_addr)
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 802, in create_connection
    sock, protocol_factory, ssl, server_hostname)
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 828, in _create_connection_transport
    yield from waiter
  File "C:\Program Files\Python36\lib\asyncio\sslproto.py", line 503, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "C:\Program Files\Python36\lib\asyncio\sslproto.py", line 201, in feed_ssldata
    self._sslobj.do_handshake()
  File "C:\Program Files\Python36\lib\ssl.py", line 683, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 304, in connect
    yield from self._create_connection(req)
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 578, in _create_connection
    transport, proto = yield from self._create_direct_connection(req)
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 624, in _create_direct_connection
    (req.host, req.port, exc.strerror)) from exc
aiohttp.errors.ClientOSError: [Errno 1] Can not connect to discordapp.com:443 [[SSL: CERTIFICATE_VERIFY_FAILED] certific
ate verify failed (_ssl.c:749)]

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "harubotFE98.py", line 43748, in <module>    # trust me, I know
    bot.run('SNIP')
  File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 519, in run
    self.loop.run_until_complete(self.start(*args, **kwargs))
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 466, in run_until_complete
    return future.result()
  File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 490, in start
    yield from self.login(*args, **kwargs)
  File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 416, in login
    yield from getattr(self, '_login_' + str(n))(*args, **kwargs)
  File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 346, in _login_1
    data = yield from self.http.static_login(token, bot=is_bot)
  File "C:\Program Files\Python36\lib\site-packages\discord\http.py", line 195, in static_login
    data = yield from self.get(self.ME)
  File "C:\Program Files\Python36\lib\site-packages\discord\http.py", line 105, in request
    r = yield from self.session.request(method, url, **kwargs)
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\client.py", line 555, in __iter__
    resp = yield from self._coro
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\client.py", line 198, in _request
    conn = yield from self._connector.connect(req)
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 314, in connect
    .format(key, exc.strerror)) from exc
aiohttp.errors.ClientOSError: [Errno 1] Cannot connect to host discordapp.com:443 ssl:True [Can not connect to discordap
p.com:443 [[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)]]
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x00000224005401D0>

но потом его сократили (?) до ...

Traceback (most recent call last):
  File "harubotFE98.py", line 43748, in <module>
    bot.run('NOPE')
  File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 519, in run
    self.loop.run_until_complete(self.start(*args, **kwargs))
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 466, in run_until_complete
    return future.result()
  File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 491, in start
    yield from self.connect()
  File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 444, in connect
    self.ws = yield from DiscordWebSocket.from_client(self)
  File "C:\Program Files\Python36\lib\site-packages\discord\gateway.py", line 175, in from_client
    ws = yield from websockets.connect(gateway, loop=client.loop, klass=cls)
  File "C:\Program Files\Python36\lib\site-packages\websockets\py35\client.py", line 19, in __await__
    return (yield from self.client)
  File "C:\Program Files\Python36\lib\site-packages\websockets\client.py", line 150, in connect
    factory, wsuri.host, wsuri.port, **kwds)
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 802, in create_connection
    sock, protocol_factory, ssl, server_hostname)
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 828, in _create_connection_transport
    yield from waiter
  File "C:\Program Files\Python36\lib\asyncio\sslproto.py", line 503, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "C:\Program Files\Python36\lib\asyncio\sslproto.py", line 201, in feed_ssldata
    self._sslobj.do_handshake()
  File "C:\Program Files\Python36\lib\ssl.py", line 683, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x0000021A50E0E358>

Я также пробовал возиться с чем-то, что называется OpenSSL, но практически любое обсуждение, которое я могу найти на нем, достаточно плотное, чтобы я не мог полностью осмыслить его.

Изменить: после некоторого возни я написал что-то вроде минимального скрипта, чтобы вызвать ошибку:

import aiohttp
import asyncio
import ssl

async def main():
    async with aiohttp.ClientSession() as cs:
        async with cs.get("https://www.discordapp.com") as r:
            res = await r.text()
            print(res)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

который возвращает:

Traceback (most recent call last):
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 924, in _wrap_create_connection
    await self._loop.create_connection(*args, **kwargs))
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 802, in create_connection
    sock, protocol_factory, ssl, server_hostname)
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 828, in _create_connection_transport
    yield from waiter
  File "C:\Program Files\Python36\lib\asyncio\sslproto.py", line 503, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "C:\Program Files\Python36\lib\asyncio\sslproto.py", line 201, in feed_ssldata
    self._sslobj.do_handshake()
  File "C:\Program Files\Python36\lib\ssl.py", line 683, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test2.py", line 12, in <module>
    loop.run_until_complete(main())
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 466, in run_until_complete
    return future.result()
  File "test2.py", line 7, in main
    async with cs.get("https://www.discordapp.com") as r:
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\client.py", line 1005, in __aenter__
    self._resp = await self._coro
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\client.py", line 476, in _request
    timeout=real_timeout
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 522, in connect
    proto = await self._create_connection(req, traces, timeout)
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 854, in _create_connection
    req, traces, timeout)
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 992, in _create_direct_connection
    raise last_exc
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 974, in _create_direct_connection
    req=req, client_error=client_error)
  File "C:\Program Files\Python36\lib\site-packages\aiohttp\connector.py", line 929, in _wrap_create_connection
    raise ClientConnectorSSLError(req.connection_key, exc) from exc
aiohttp.client_exceptions.ClientConnectorSSLError: Cannot connect to host www.discordapp.com:443 ssl:None [[SSL: CERTIFI
CATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)]

Я также пробовал несколько других решений, таких как использование веб-клиента для посещения discordapp.com:443 (который работал, но не исправил это) и обновление python aiohttp до самой последней сборки (которая не исправила это) .


person Harudoku    schedule 17.08.2019    source источник


Ответы (3)


если вы используете Windows Server 2018/2019 (я пробовал его на личном сервере Datacenter Server), вы можете ввести команду ниже, чтобы разрешить вашему боту подключиться. Мне пришлось запустить его из административного терминала, чтобы заставить его работать, но эта команда устранила все мои проблемы.

certutil -generateSSTFromWU roots.sst && certutil -addstore -f root roots.sst && del roots.sst

Удачи тебе!

person Jerhyn Huey    schedule 17.05.2020
comment
Просто подумал, что дам вам знать, мой бот недавно начал падать, и после того, как я попробовал множество вещей, которые на самом деле были предназначены для пользователей iOS, это было то, что на самом деле решило мою проблему! - person Tshsmith; 30.05.2020
comment
Я рад, что смог помочь, я только что приобрел Windows Server Data Center 2019 edition, и у меня тоже были проблемы. Я искал исправление около двух часов и наконец наткнулся на это. Я не пытался редактировать свой реестр из-за ошибки сертификата, поэтому это произошло. В любом случае, счастлив, что смог помочь! - person Jerhyn Huey; 31.05.2020

Переносимым решением этой проблемы было бы использование ssl_context с сертификатом cacert.pem

import asyncio
import ssl

import aiohttp
import certifi


async def main():
    ssl_context = ssl.create_default_context(cafile=certifi.where())
    
    async with aiohttp.ClientSession() as cs:
        async with cs.get("https://www.discordapp.com", ssl_context=ssl_context) as r:
            res = await r.text()
            print(res)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
person jackotonye    schedule 28.03.2021

Хорошо. Итак, этот оказался довольно странным - я до сих пор не знаю точно, что пошло не так, но суть в том, что, несмотря на то, что я мог нормально загружать https через браузер, сертификат SSL просто не был загружен должным образом. для Python (или других вещей, например OpenSSL). Я понял это при попытке подключиться к discordapp.com вне командной строки OpenSSL (всегда возвращалось error 20 -- unable to get local issuer certificate).

Я загрузил другой сертификат (файл .pem) (на который мне указал друг, который работает в сети), и с помощью флага -CAfile я смог указать на него OpenSSL, что позволило ему работать должным образом.

Теперь, на этом этапе это был просто вопрос, чтобы Discord.py увидел этот новый сертификат и использовал его для своего контекста SSL, но, поскольку я использую 0.16.12 (старая, устаревшая ветка), это оказалось проще сказать чем сделано - в строке bot = commands.Bot() я добавил kwarg, который выглядел так:

sslc = ssl.create_default_context(
    cafile = "C:\Program Files\Common Files\SSL\cert.pem"  #point this at your pem file
        )
con = aiohttp.TCPConnector(ssl_context=sslc)
bot = commands.Bot(
    command_prefix='/',
    connector=con
        ) 

Это ... на самом деле не устранило проблему, но, как я узнал от Дэнни (ведущего разработчика discord.py), похоже, что это изменило трассировку стека, даже если ошибка осталась прежней. Как оказалось, при этом изменяется контекст ssl для коннектора, но websocket использует свой собственный, а discord.py - или, по крайней мере, 0.16.12 - не совместим с этим. . Итак, если вы перейдете в папку \Python36\Lib\site-packages\discord, вам нужно будет найти файл с подключением к веб-сокету - это будет gateway.py. Откройте его, перейдите к строке 66 (по крайней мере, в 0.16.12 - в противном случае найдите строку, содержащую yield from websockets.connect(...). Как только вы найдете свое соединение с веб-сокетом, все, что вам нужно сделать, это добавить ssl kwarg с правильным контекстом, который должен выглядеть примерно так:

    ws = yield from websockets.connect(gateway, loop=loop, klass=klass,
        ssl = ssl.create_default_context(
            cafile = "C:\Program Files\Common Files\SSL\cert.pem"  #point this at your pem file
            )
        )

Если по какой-то богом забытой причине ваша установка проклята, так что ваш питон не может видеть правильные сертификаты SSL, вы используете старую устаревшую асинхронную версию Discord.py, и вы не можете исправить ее другим способом, зная больше о это, чем я, это должно решить вашу проблему. У меня это сработало. Удачи!

person Harudoku    schedule 18.08.2019