Запросы Python - как использовать системные CA-сертификаты (debian / ubuntu)?

Я установил самозаверяющий корневой сертификат CA в /usr/share/ca-certificates/local debian и установил их с sudo dpkg-reconfigure ca-certificates. На данный момент true | gnutls-cli mysite.local доволен, а true | openssl s_client -connect mysite.local:443 доволен, но модуль запросов python2 и python3 настаивает, что он недоволен сертификатом.

python2:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 70, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 56, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 488, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 609, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/adapters.py", line 497, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",)

python3

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/bin/python3.5/site-packages/requests/api.py", line 70, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/api.py", line 56, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/sessions.py", line 488, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/sessions.py", line 609, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/adapters.py", line 497, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",)

Почему Python игнорирует системный пакет CA-сертификатов и как его интегрировать?


person ThorSummoner    schedule 23.03.2017    source источник


Ответы (4)


Из https://stackoverflow.com/a/33717517/1695680

Чтобы запросы python использовали системный пакет CA-сертификатов, ему необходимо указать, чтобы он использовал его вместе с собственным встроенным пакетом.

export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt

Запросы встраивают свои пакеты сюда, для справки:

/usr/local/lib/python2.7/site-packages/requests/cacert.pem
/usr/lib/python3/dist-packages/requests/cacert.pem

Или в более новых версиях используйте дополнительный пакет для получения сертификатов от: https://github.com/certifi/python-certifi

Чтобы проверить, из каких файлов загружаются сертификаты, вы можете попробовать:

Python 3.8.5 (default, Jul 28 2020, 12:59:40) 
>>> import certifi
>>> certifi.where()
'/etc/ssl/certs/ca-certificates.crt'
person Community    schedule 23.03.2017
comment
После того, как я удалю файл cacert.pem по умолчанию, связанный с запросами, запросы, похоже, забирают пакет системных сертификатов CA без установки переменной среды. - person Rajasi Kulkarni; 08.08.2017
comment
Невозможно описать словами, насколько этот ответ помог мне решить проблему, которую я искал в течение нескольких недель. - person rwarner; 20.11.2019
comment
что сказал @rwarner! - person Smedegaard; 12.04.2020
comment
Что сказал @Smedegaard! - person PierreF; 08.02.2021
comment
Установка переменной окружения REQUESTS_CA_BUNDLE работает. Однако это не меняет путь к crt в модуле certifi. Ответ подразумевает, что это так, но мой тест на Python 3.7 и 3.8 показывает обратное. Вместо этого я рекомендую использовать os.getenv для проверки пути. - person nafooesi; 27.04.2021

Недавно я боролся с этим около недели. Наконец-то я нашел способ проверить самозаверяющий или подписанный частным образом сертификат в Python. Вам необходимо создать свой собственный файл пакета сертификатов. Нет необходимости обновлять непонятные пакеты сертификатов каждый раз, когда вы обновляете библиотеку или добавляете что-либо в системное хранилище сертификатов.

Начните с выполнения команды openssl, которую вы запускали ранее, но добавьте -showcerts. openssl s_client -connect mysite.local:443 -showcerts Это даст вам длинный результат, а вверху вы увидите всю цепочку сертификатов. Обычно это означает три сертификата, сертификат веб-сайта, промежуточный сертификат и корневой сертификат в указанном порядке. Нам нужно поместить только корневой и промежуточный сертификаты в следующий файл в обратном порядке.

Скопируйте последний сертификат, корневой сертификат, в новый текстовый файл. Возьмите только то, что находится между и в том числе:

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

Скопируйте средний сертификат (также известный как промежуточный сертификат) в новый текстовый файл под корневым сертификатом. Опять же, возьмите строки Begin и End Certificate и все, что между ними.

Сохраните этот текстовый файл в каталоге, в котором находится ваш скрипт Python. Я рекомендую называть это CertBundle.pem. (Если вы дадите ему другое имя или поместите его в другое место в структуре папок, убедитесь, что это отражает строка проверки.) Обновите свой скрипт, чтобы он ссылался на новый пакет сертификатов:

response = requests.post("https://www.example.com/", headers=headerContents, json=bodyContents, verify="CertBundle.pem")

Вот и все. Если у вас есть только корневой или только промежуточный сертификат, Python не сможет проверить всю цепочку сертификатов. Но если вы включите оба сертификата в созданный вами комплект сертификатов, то Python может проверить, что промежуточный продукт был подписан корнем, а затем, когда он обращается к веб-сайту, он может подтвердить, что сертификат веб-сайта был подписан промежуточным сертификатом. .

edit: Исправлено расширение файла для пакета сертификатов. Также исправлена ​​пара грамматических ошибок.

person fryad    schedule 06.02.2018
comment
Я не уверен, что .p7b - правильное семантическое расширение для указанного пакета. (Хотя я не совсем эксперт) просто привык видеть .pem и .crt, используемые для пакетов CA Bundles. Я знаю, что пакет debian ca-сертификатов разборчив в том, что сертификаты являются расширениями .crt, которые нужно добавить в хранилище сертификатов, предоставляемое системой. - person ThorSummoner; 06.02.2018
comment
Я чуть не сказал что-то об этом в своем посте. Я выбрал p7b, потому что считаю, что это правильное расширение, но для этой цели это не имеет значения. Это может быть .txt или вообще без расширения. Важно то, что ваш скрипт Python может найти файл. - person fryad; 09.02.2018
comment
@fryad правильный; этот файл должен иметь расширение .pem, и некоторые инструменты будут неправильно его обрабатывать, потому что он имеет неправильное расширение. .pem является де-факто стандартным расширением для этого формата сертификата в кодировке base64, а двоичная версия того же формата - .der, а .p7b - это другой формат с кодировкой base64. Удобный справочник о том, как конвертировать их с помощью openssl инструментов CLI: knowledge.digicert.com/solution/ SO26449.html - person Dan Lenski; 17.07.2018

Мои два цента:

Благодаря этому другому ответу, который заставил меня проверить Фактический код запросов, я понял, что вам не обязательно использовать переменную env, но вы можете просто установить параметр "verify" в своем запросе:

requests.get("https://whatever", verify="/my/path/to/cacert.crt", ...)

Он также задокументирован, хотя мне удалось найти только документация после открытия (и проект pypi указывает на мертвую ссылку для документа): D

person Riccardo Manfrin    schedule 23.10.2019

requests использует certifi в качестве пакета корневых сертификатов по умолчанию, который встроен во множество хороших центров сертификации, но не может быть изменен.

Сопровождающие Debian (и Ubuntu) изменили поведение certifi, отличное от поведения по умолчанию:

def where():
    return "/etc/ssl/certs/ca-certificates.crt"

Так что, если вы используете requests и certifi с установленной apt, проблем нет.

Но pip3 установленный certifi внутри виртуального env использует встроенные центры сертификации. Поэтому невозможно использовать механизм update-ca-certificates. Помимо ручного указания корневого сертификата в коде приложения (что может быть невозможно, если request вызывается косвенно через сторонние интерфейсы), он также может переопределить переменную окружения REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt для имитации поведения Debianized.

person adoal    schedule 19.05.2021