2 回答

TA贡献1911条经验 获得超7个赞
有几种方法是可能的。Daniel F 的回答非常好,并展示了一种很好的通用技术来动态重新配置服务器。
这里还有一些技术更具体地适用于 Twisted 中的 TLS 支持。
DefaultOpenSSLContextFactory首先,您可以从实例中重新加载 OpenSSL“上下文”对象。当需要重新加载证书时,运行:
sslContext._context = None
sslContext.cacheContext()
该cacheContext调用将创建一个新的 OpenSSL 上下文,在此过程中重新读取证书文件。这确实有依赖私有接口 ( _context) 及其与非真正公共接口 ( cacheContext) 交互的缺点。
你也可以实现你自己的版本,DefaultOpenSSLContextFactory这样你就不必依赖这些东西了。 DefaultOpenSSLContextFactory并没有真正做太多。这是一个完全删除缓存行为的复制/粘贴/编辑:
class DefaultOpenSSLContextFactory(ContextFactory):
"""
L{DefaultOpenSSLContextFactory} is a factory for server-side SSL context
objects. These objects define certain parameters related to SSL
handshakes and the subsequent connection.
"""
_context = None
def __init__(self, privateKeyFileName, certificateFileName,
sslmethod=SSL.SSLv23_METHOD, _contextFactory=SSL.Context):
"""
@param privateKeyFileName: Name of a file containing a private key
@param certificateFileName: Name of a file containing a certificate
@param sslmethod: The SSL method to use
"""
self.privateKeyFileName = privateKeyFileName
self.certificateFileName = certificateFileName
self.sslmethod = sslmethod
self._contextFactory = _contextFactory
def getContext(self):
"""
Return an SSL context.
"""
ctx = self._contextFactory(self.sslmethod)
# Disallow SSLv2! It's insecure! SSLv3 has been around since
# 1996. It's time to move on.
ctx.set_options(SSL.OP_NO_SSLv2)
ctx.use_certificate_file(self.certificateFileName)
ctx.use_privatekey_file(self.privateKeyFileName)
当然,这会为每个可能不受欢迎的连接重新加载证书文件。您可以重新添加自己的缓存逻辑,并使用适合您的证书刷新系统的控制界面。这也有一个缺点,那DefaultOpenSSLContextFactory就是一开始就不是一个非常好的 SSL 上下文工厂。它不遵循 TLS 配置的当前最佳实践。
所以你可能真的想twisted.internet.ssl.CertificateOptions改用。这有一个类似的_context缓存,您可以清除它:
sslContext = CertificateOptions(...) # Or PrivateCertificate(...).options(...)
...
sslContext._context = None
它会在发现上下文时自动重新生成上下文,None因此至少您不必以cacheContext这种方式调用。但是,您再次依赖于私有接口。
另一种更类似于 Daniel F 建议的技术是为已经在监听的套接字提供一个新工厂。这避免了stopListening和之间的服务短暂中断listenSSL。这将是这样的:
from twisted.protocols.tls import TLSMemoryBIOFactory
# DefaultOpenSSLContextFactory or CertificateOptions or whatever
newContextFactory = ...
tlsWebsiteFactory = TLSMemoryBIOFactory(
newContextFactory,
isClient=False,
websiteFactory,
)
listeningPortFileno = sslPort.fileno()
websiteFactory.sslPort.stopReading()
websiteFactory.sslPort = reactor.adoptStreamPort(
listeningPortFileno,
AF_INET,
tlsWebsiteFactory,
)
这基本上只是让反应堆停止为sslPort具有过时配置的旧设备提供服务,并告诉它开始为新工厂上该端口的底层套接字服务事件。在这种方法中,您必须下降到稍低级别的 TLS 接口,因为您不能采用“TLS 端口”,因为没有这样的东西。相反,您采用 TCP 端口并自己应用必要的 TLS 包装(这正是 listenSSL 在幕后为您所做的)。
请注意,这种方法比其他方法更受限制,因为并非所有反应器都提供filenooradoptStreamPort方法。如果您想在受支持的地方使用它并在其他地方优雅地降级,您可以测试各种对象提供的接口。
另请注意,因为TLSMemoryBIOFactory无论如何它总是在幕后工作,如果你有对它的引用,你也可以旋转它的私有接口:
tlsMemoryBIOFactory._connectionCreator = IOpenSSLServerConnectionCreator(
newContextFactory,
)
它将开始将其用于新连接。但是,再次,私人...

TA贡献1859条经验 获得超6个赞
有可能的。
reactor.listenSSL返回一个twisted.internet.tcp.Port实例,您可以将其存储在可访问的位置,例如服务器的网站资源中,以便您以后可以访问它:
website_resource = Website()
website_factory = server.Site(website_resource)
website_resource.sslPort = reactor.listenSSL( # <---
port=https_server_port,
factory=website_factory,
contextFactory=sslContext,
interface=https_server_interface
)
然后稍后在您的 http 处理程序(render函数)中,您可以执行以下操作:
if request.path == b'/server/reload-certificates':
request.setHeader("connection", "close")
self.sslPort.connectionLost(reason=None)
self.sslPort.stopListening()
self.sslListen()
return b'ok'
self.sslListen初始设置代码在哪里:
website_resource = Website()
website_factory = server.Site(website_resource)
def sslListen():
sslContext = ssl.DefaultOpenSSLContextFactory(
'/home/user/certs/letsencrypt-privkey.pem',
'/home/user/certs/letsencrypt-fullchain.pem',
)
website_resource.sslPort = reactor.listenSSL(
port=https_server_port,
factory=website_factory,
contextFactory=sslContext,
interface=https_server_interface
)
website_resource.sslListen = sslListen # <---
sslListen() # invoke once initially
# ...
reactor.run()
请注意,这request.setHeader("connection", "close")是可选的。它指示浏览器应该关闭连接,而不是在下一次获取服务器时重用它(HTTP/1.1 连接通常保持打开至少 30 秒以便重用)。
如果connection: close未发送标头,那么一切仍然有效,连接仍然有效且可用,但仍将使用旧证书,如果您只是重新加载证书以在更新后刷新它们,这应该没certbot问题他们。来自其他浏览器的新连接将立即开始使用新证书。
添加回答
举报