2017-05-09 68 views
1

我有一个用Python 3.5的asyncio库实现的自定义TCP服务器。我还使用Lets加密证书进行与服务器通信的SSL加密。让加密只颁发有效期为90天的证书,并且很有可能我的服务器不会在比此更长的时间内重新启动。长时间运行Asyncio服务器,重新加载证书

我产生的SSLContext这样的:

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) 
ssl_context.load_cert_chain(certfile='path/to/cert', keyfile='path/to/key') 

而且这样的服务器:

server = asyncio.streams.start_server(self._accept_client, ip, port, loop=self.loop, ssl=ssl_context) 

证书过期后,现有连接继续起作用,但新的连接将(正确)失败。显然,SSLContext或服务器本身会将证书和密钥文件加载到内存中,因为更新磁盘上的文件不能解决问题。

我已经读了很多Python文档,发现没有提到的解决方案。我有一个想法是尝试定期拨打ssl_context.load_cert_chain(),希望这会更新证书和密钥。我不能看看load_cert_chain函数的来源,以确定它的行为,因为它显然是用C语言编写的,并且文档没有指定在上下文传递给服务器后调用此函数的行为。

有没有办法在运行时更新在SSLContext中加载的证书和密钥文件,而不必完全停止并重新启动服务器?

回答

1

我相当肯定,保持你使用的上下文的副本,并调用重新加载证书链的方法将工作正常。至少在C openssl API级别上这是一个合理的事情,它看起来并不像python做任何事情来打破这一点。

1

据我所知,目前在asyncio.Server没有API更新SSL证书时,服务器的活,你有两种解决方案:

可以使用SNI回调更新证书在SSL握手期间,但它只会与支持SNI的客户端协同工作。 这意味着对于支持此功能的每个客户端,将调用您选择的回调函数,并可能返回将用于与客户端建立连接的SSLContext对象。您可以调用此回调函数,以便在每次调用时读取证书/密钥文件,或者在需要时通过您选择的触发器重新加载。

要设置回调,请参阅:https://docs.python.org/3/library/ssl.html#ssl.SSLContext.set_servername_callback

其他解决方案更复杂与ASYNCIO API的当前状态,以获得正确的:如果内部实行ASYNCIO变化可能会破裂,而我却没有检查它如何与proactor循环一起工作。但是,它将与任何类型的客户端一起工作。

您可以创建新的上下文有新的服务器,但使用的第一台服务器的插槽:create_server()期间

# This will create a new server with the current socket, and setup what's required to use the new callback. 
new_server = await asyncio.create_server(callback, None, None, 
        sock=old_server.sockets[0], ssl=new_context) 

# Ensure that when cleaning the old_server, it doesn't try to close the socket we kept 
old_server.sockets = [] 
old_server.close() 
await old_server.wait_closed() 

在一个点上,ASYNCIO将开始回答与新新连接ssl上下文。请注意,一旦连接开始并传递到回调函数(_accept_client),它实际上独立于调用它的服务器,因此它将保持所有现有连接不变。

相关问题