在Heroku Redis上SSL认证验证失败

11
我正在使用Redis高级版计划在Heroku上部署一个flask应用程序,但是遇到了“SSL证书验证失败”的错误。尝试的解决方法有:
  • 降级到Redis 5。
  • redis-py中将ssl_cert_reqs = None传递给Redis构造函数。
解决此问题的方法可能是:
  1. 说明如何在Heroku Redis高级版计划上禁用TLS证书验证。
  2. 说明如何在Heroku Redis高级版计划上使TLS证书验证正常工作。
从Heroku的文档中,这可能是一个提示:“为了连接到Redis 6数据库,必须在Redis客户端的配置中启用TLS”。我不明白这是什么意思。
8个回答

9

我通过在我的Heroku配置文件的REDIS_URL末尾添加?ssl_cert_reqs=CERT_NONE解决了我的问题。


1
这是方法。 - Rafael Santos
4
当我将其明确设置为?ssl_cert_reqs=none时,它对我起作用了(CERT_NONE引发了“无效标志”错误)。 - jtschoonhoven
2
如果您正在使用Heroku Redis数据或其他插件,则此设置不会保留,因为该环境变量受Heroku管理。 - drweird
@drweird 是正确的。我为此应用的解决方法是保持 Heroku 环境变量不变,并在 Flask 的 config.py 文件中将 ?ssl_cert_reqs=none 添加到 Redis URL 中。 - Rob

5

您可以通过降级到Redis 5并将ssl_cert_reqs=None传递给Redis构造函数,在Heroku上禁用TLS认证。

$ heroku addons:create heroku-redis:premium-0 --version 5

from redis import ConnectionPool, Redis
import os
connection_pool = ConnectionPool.from_url(os.environ.get('REDIS_URL'))
app.redis = Redis(connection_pool=connection_pool, ssl_cert_reqs=None)

我的错误是没有同时完成两件事。

理想的解决方案将解释如何为Redis 6配置TLS认证。


5

文档实际上是错误的,您必须将SSL设置为verify_none,因为TLS会自动发生。

来自Heroku支持:

“我们的数据基础设施使用自签名证书,因此证书可以定期循环...您需要将verify_mode配置变量设置为OpenSSL :: SSL :: VERIFY_NONE”

我通过将ssl_params设置为verify_none解决了这个问题:

ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }

对我来说,这是我配置Redis的地方(在Sidekiq初始化程序中):

# config/initializers/sidekiq.rb
Sidekiq.configure_client do |config|
  config.redis = { url: ENV['REDIS_URL'], size: 1, network_timeout: 5, 
    ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }
end

Sidekiq.configure_server do |config|
  config.redis = { url: ENV['REDIS_URL'], size: 7, network_timeout: 5, 
    ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }
end

请问您能否解释一下什么是Sidekiq初始化器以及它与Flask应用程序的关系? - Dillon Bowen
Sidekiq是一个后台处理的宝石,因为它连接到redis,所以我在那里设置了SSL配置来修复你遇到的相同错误。你可能可以在配置redis的任何地方做同样的事情(验证无SSL)。 - Joe's Ideas
@Joe'sIdeas,你能提供一下Heroku支持报价的链接吗?我在任何地方都找不到它 :/(我有一个相关问题:https://dev59.com/I1EG5IYBdhLWcg3wcuD2) - ogirginc
1
@ogirginc 没有链接,那是与支持团队的对话中得出的结论。看起来 verify none 对你起作用了,不错。 - Joe's Ideas
@Joe的想法没错!谢谢 :) - ogirginc

2

我在这个问题上遇到了困难,但最终解决了...

关于这个主题的很多文档和帖子都不够清晰。我已请求Heroku更新他们的文档https://devcenter.heroku.com/articles/connecting-heroku-redis#connecting-in-python以包括对Procfile的特定更改。

虽然上面提到了这点,但是它指的是“Heroku config”而不是Procfile

在您的Procfile中添加?ssl_cert_reqs=none$REDIS_URL

e.g.:

web: gunicorn webapp:app
worker: rq worker -u $REDIS_URL?ssl_cert_reqs=none queue

不要直接更新REDIS_URL,因为Heroku会定期循环更新。

我按照原始文档更新了Redis设置:

url = urlparse(os.environ.get("REDIS_URL"))
r = redis.Redis(host=url.hostname, port=url.port, password=url.password, ssl=True, ssl_cert_reqs=None)

但是对Procfile的更改最终使得Redis v6.2.11与TLS正确地工作。


你真是个天赐之人,谢谢你。 - jacob_g

2
在Heroku上(假设使用了Heroku Redis插件),redis TLS路由已经处理好了ssl_cert_reqs参数。一个常见的疏忽是在Heroku上使用REDIS_URL而非REDIS_TLS_URL,这可能导致错误。
解决方案: redis_url = os.environ.get('REDIS_TLS_URL')

2
这仅适用于“Mini Heroku Data for Redis”,而不适用于“Premium”版本。 - Miroslav Valcicak

2
如果使用django-rq包装器并尝试处理此问题,请确保不要使用URL参数和SSL_CERTS_REQS。有一个未解决的问题描述了这一切,但基本上您需要指定每个连接参数而不是使用URL。

谢谢,我在django-rq中遇到了同样的问题,你的解决方案帮了我大忙! - PythonSherpa

1
这个解决方案适用于 Heroku 上的 Redis 6 和 Python。
import os, redis

redis_url = os.getenv('REDIS_URL')
redis_store = redis.from_url(redis_url, ssl_cert_reqs=None)

在我的本地开发环境中,我不使用rediss协议的redis,因此我使用这样一个函数来允许在两种情况下工作:
def get_redis_store():
    '''
    Get a connection pool to redis based on the url configured
    on env variable REDIS_URL

    Returns
    -------
    redis.ConnectionPool
    '''
    redis_url = os.getenv('REDIS_URL')
    if redis_url.startswith('rediss://'):
        redis_store = redis.from_url(
            redis_url, ssl_cert_reqs=None)
    else:
        redis_store = redis.from_url(redis_url)
    return redis_store

嘿@GonzaloOdiard,非常感谢!你刚刚节省了我很多时间。我一直在想为什么它不起作用,它与RedisInsight连接时TLS是正确的,但无法与Python集成。我还尝试安装OpenSSL证书。 - Saqib

0

该解决方案适用于nodejs16和redis客户端4.6.x

redisClient = redis.createClient({
            url: process.env.REDIS_URL,
            socket: {
                tls: true,
                rejectUnauthorized: false
            })

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接