Я работаю над реализацией аутентификации OAuth2 в приложении FastAPI, используя ForgeRock в качестве поставщика удостоверений. Эта установка находится во внутренней среде моей компании, и у меня есть необходимые сертификаты. Однако я сталкиваюсь с ошибкой проверки сертификата SSL при попытке аутентификации с помощью ForgeRock (или вызова конечной точки /auth/login):
[SSL: CERTIFICATE_VERIFY_FAILED] проверка сертификата не удалась: самостоятельно подписанный сертификат в цепочке сертификатов (_ssl.c:997)
FastAPI работает в контейнере, вот мой Dockerfile:
FROM xxx.com/xxx/python3.10.9-slim:1.0
WORKDIR /app
COPY ./ /app
COPY ./certs /etc/ssl/certs/ca-certificates
RUN chmod 644 /etc/ssl/certs/ca-certificates/*
RUN apt-get update && apt-get install -y ca-certificates
RUN update-ca-certificates
...
Сертификаты правильно перемещены в /etc/ssl/certs/ca-certificates.crt.
Вот мой упрощенный код FastAPI:
CA_BUNDLE_PATH = '/etc/ssl/certs/ca-certificates.crt'
os.environ['REQUESTS_CA_BUNDLE'] = CA_BUNDLE_PATH
ssl_context = ssl.create_default_context(cafile=CA_BUNDLE_PATH)
logging.debug(f"SSL context CA certificates: {ssl_context.get_ca_certs()}")
class CustomHttpxClient(httpx.AsyncClient):
def __init__(self, *args, **kwargs):
kwargs['verify'] = ssl_context
super().__init__(*args, **kwargs)
oauth = OAuth()
oauth.register(
name='forgerock',
client_id='client-id',
client_secret='secret',
server_metadata_url='https://forgerock-login',
client_kwargs = {
'scope': 'openid profile email',
'httpx_client': CustomHttpxClient()
}
)
async def debug_session_state(request: Request):
session = request.session
state = session.get('state')
logging.debug(f"Session state: {state}")
@app.get('/auth/login')
async def login(request: Request):
try:
redirect_uri = request.url_for('auth_callback')
response = await oauth.forgerock.authorize_redirect(request, redirect_uri)
request.session['state'] = response.state
logging.debug(f"Stored state in session: {request.session['state']}")
return response
except Exception as e:
logging.error(f"Error during authorization redirect: {e}")
raise HTTPException(status_code=500, detail = "Internal Server Error")
@app.get('/auth/callback')
async def auth_callback(request: Request):
await debug_session_state(request)
try:
state_in_session = request.session.get('state')
state_in_request = request.query_params.get('state')
logging.debug(f"State in session: {state_in_session}, State in request: {state_in_request}")
if state_in_session != state_in_request:
raise HTTPException(status_code=400, detail = "CSRF Warning! State not equal in request and response.")
token = await oauth.forgerock.authorize_access_token(request)
user = token.get('userinfo')
return {'user': user}
except Exception as e:
logging.error(f"Callback Error: {e}")
raise HTTPException(status_code=400, detail = "Authentication Failed")
Поэтому я загрузил сертификаты CA в собственный контекст SSL и использовал его с httpx.AsyncClient. Во всяком случае, я вижу это в журналах отладки:
ОТЛАДКА:httpx:load_verify_locations cafile='/usr/local/lib/python3.10/site-packages/certif/cacert.pem'
Кажется, контекст SSL отличается от указанного мной. Я пробовал разные методы передачи контекста SSL, но безрезультатно. Почему я не могу настроить контекст SSL?


В сообщении об ошибке упоминается httpx, а не requests.
httpx имеет собственный набор переменных Env, в данном случае SSL_CERT_FILE.
Будем надеяться, что переменная env var переопределит жестко запрограммированный CACERT из certify, что является серьезной проблемой при попытке использовать Python в корпоративной IS.