2017-05-09 49 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),它實際上獨立於調用它的服務器,因此它將保持所有現有連接不變。

相關問題