2017-05-09 12 views
1

Python 3.5のasyncioライブラリで実装されたカスタムTCPサーバーがあります。私はまた、サーバーとの通信のSSL暗号化にLets Encrypt証明書を使用します。暗号化で90日間有効な証明書のみが発行され、サーバーがこれよりも長い間再起動されない可能性があります。長時間実行しているAsyncioサーバー、リロード証明書

私はこのようなSSLContextの生成しています:証明書の有効期限が切れると、既存の接続が機能し続けますが、新しい接続が(正確に)失敗し、

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

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

そして、このようなサーバー。明らかに、SSLContextまたはサーバー自体は、ディスク上のファイルを更新しても問題が解決されないため、証明書とキーファイルがメモリにロードされたままになっています。

私はPythonのドキュメントをかなり読んだことがあります。私が持っていた1つのアイデアは、定期的な間隔でssl_context.load_cert_chain()を呼び出して、これがcertとkeyを更新することを期待することです。私は明らかにC言語で記述されているので、その動作を判断するためにload_cert_chain関数のソースを見ることはできません。また、ドキュメントには、コンテキストがサーバに渡された後にこの関数を呼び出す動作が指定されていません。

サーバーを完全に停止して再起動することなく、実行時にSSLContextにロードされた証明書とキーファイルを更新する方法はありますか?

答えて

1

あなたが使用するコンテキストのコピーを保持し、証明書チェーンを再読み込みするメソッドを呼び出すと正常に動作することは確かです。少なくともCのopenssl APIレベルでは、これは妥当なことであり、Pythonがそれを破るために何もしないように見えません。

1

は、私の知る限りでは、サーバーが生きている時にSSL証明書を更新するasyncio.ServerにはAPIが存在しない、次の2つのソリューションを持っている:

をあなたは、証明書を更新するために、SNIコールバックを使用することができますSSLハンドシェークの間は、SNIをサポートするクライアントでのみ動作します。 これは、この機能をサポートするクライアントごとに、選択したコールバックが呼び出され、クライアントとの接続を確立するために使用されるSSLContextオブジェクトが返される可能性があることを意味します。このコールバックを作成して、各呼び出しで証明書/キーファイルを読み込んだり、必要に応じて選択したトリガーによってリロードしたりすることができます。セットアップするには

コールバック、次を参照してください。https://docs.python.org/3/library/ssl.html#ssl.SSLContext.set_servername_callback

を他のソリューションは、右asyncio APIの現在の状態を取得するより複雑である:それは場合asyncio変化の内部実装を壊すかもしれない、と私はしませんでしたそれがどのようにプロクター・ループで動作するかを確認してください。しかし、それはどんな種類のクライアントでも動作します。

あなたは新しいコンテキストで、新しいサーバーを作成しますが、最初のサーバーのソケットを使用することができます

# 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() 

ある時点でcreate_server中に()、asyncioは新しいと新しい接続に答えるために開始しますSSLコンテキスト。接続が開始され、コールバック(_accept_client)に渡されると、それは呼び出されたサーバーとは関係なく実際には存在するため、既存の接続は変更されません。

関連する問題