繁体   English   中英

Websocket Autobahn Python客户端:如何使用服务器和客户端证书连接到服务器?

[英]Websocket Autobahn Python client: how to connect to server using server and client certificates?

websocket客户端(使用Autobahn / Python和Twisted)需要连接到websocket服务器:客户端需要将其客户端证书提供给服务器,客户端需要检查服务器的证书。 例如,在设置Kubernetes minikube安装期间创建了这些证书。 尤其是:

  • 服务器证书~/.minikube/ca.crt (从我理解的X509格式)。
  • 客户端证书~/.minikube/client.crt带有密钥~/.minikube/client.key

我已经检查过我可以使用curl成功使用这些证书+密钥来发出Kubernetes远程API调用。

从Autobahn的echo_tls / client.py示例中我了解到我可能需要使用ssl.ClientContextFactory() 这里的ssl指的是自动扭曲导入的pyopenssl包。

但是,我无法弄清楚如何将证书传递给工厂?

  • 如何告诉websocket因子将客户端证书提供给服务器?
  • 如何判断websocket检查服务器的证书以检测MITM攻击?

经过一些试验和错误,我现在已经到了下面的解决方案。 为了帮助其他人,我不仅会显示代码,还会提供参考设置来测试驱动示例代码。

首先,安装minikube,然后启动minikube实例; 我已经使用minikube 1.0.0进行了测试,然后运行Kubernetes 1.14,这是撰写本文时的最新内容。 然后启动一个简单的websocket服务器,它只显示发送给它的内容,并将发回给你连接的websocket客户端的任何输入。

minikube start
kubectl run wsserver --generator=run-pod/v1 --rm -i --tty \
  --image ubuntu:disco -- bash -c "\
    apt-get update && apt-get install -y wget && \
    wget https://github.com/vi/websocat/releases/download/v1.4.0/websocat_1.4.0_ssl1.1_amd64.deb && \
    dpkg -i webso*.deb && \
    websocat -vv -s 0.0.0.0:8000"

接下来是Python代码。 它尝试连接到我们刚刚从minikube通过Kubernetes的远程API启动的wsserver,使用远程API作为其反向代理。 minikube设置通常使用客户端和服务器的相互SSL / TLS身份验证,因此这是一个“硬”测试。 请注意,还有其他方法,例如服务器证书和承载令牌(而不是客户端证书)。

import kubernetes.client.configuration
from urllib.parse import urlparse
from twisted.internet import reactor
from twisted.internet import ssl
from twisted.python import log
from autobahn.twisted.websocket import WebSocketClientFactory, WebSocketClientProtocol, connectWS
import sys

if __name__ == '__main__':
    log.startLogging(sys.stdout)

    class EchoClientProto(WebSocketClientProtocol):
        def onOpen(self):
            print('onOpen')
            self.sendMessage('testing...\n'.encode('utf8'))
        def onMessage(self, payload, isBinary):
            print('onMessage')
            if not isBinary:
                print('message %s' % payload.decode('utf8'))
        def onClose(self, wasClean, code, reason):
            print('onClose', wasClean, code, reason)
            print('stopping reactor...')
            reactor.stop()

    # Select the Kubernetes cluster context of the minikube instance,
    # and see what client and server certificates need to be used in
    # order to talk to the minikube's remote API instance...
    kubernetes.config.load_kube_config(context='minikube')
    ccfg = kubernetes.client.configuration.Configuration._default
    print('Kubernetes API server CA certificate at %s' % ccfg.ssl_ca_cert)
    with open(ccfg.ssl_ca_cert) as ca_cert:
        trust_root = ssl.Certificate.loadPEM(ca_cert.read())
    print('Kubernetes client key at %s' % ccfg.key_file)
    print('Kubernetes client certificate at %s' % ccfg.cert_file)
    with open(ccfg.key_file) as cl_key:
        with open(ccfg.cert_file) as cl_cert:
            client_cert = ssl.PrivateCertificate.loadPEM(cl_key.read() + cl_cert.read())

    # Now for the real meat: construct the secure websocket URL that connects
    # us with the example wsserver inside the minikube cluster, via the
    # remote API proxy verb.
    ws_url = 'wss://%s/api/v1/namespaces/default/pods/wsserver:8000/proxy/test' % urlparse(ccfg.host).netloc
    print('will contact: %s' % ws_url)
    factory = WebSocketClientFactory(ws_url)
    factory.protocol = EchoClientProto

    # We need to attach the client and server certificates to our websocket
    # factory so it can successfully connect to the remote API.
    context = ssl.optionsForClientTLS(
        trust_root.getSubject().commonName.decode('utf8'),
        trustRoot=trust_root,
        clientCertificate=client_cert
    )

    connectWS(factory, context)
    print('starting reactor...')
    reactor.run()
    print('reactor stopped.')

使用optionsForClientTLS附加客户端和服务器证书时,棘手的部分是Twisted / SSL希望被告知我们要与之交谈的服务器名称。 这也需要通知虚拟服务器他们需要提供多个服务器证书中的哪一个 - 在有任何HTTP头之前!

不幸的是,现在这是一个丑陋的领域 - 我很乐意在这里得到反馈! 简单地使用urlparse(ccfg.host).hostname可以在某些minikube实例上运行,但不能在其他实例上使用。 我还没弄清楚为什么看似相似的实例表现不同。

我目前的解决方法是从服务器的证书中简单地使用主题的CN(通用名称)。 当远程API服务器的URL使用IP地址文字而不是DNS名称(或至少是标签)时,可能更强大的方式可能只是采用这种策略。

唉,运行python3 wssex.py上面的Python 3代码。 如果脚本正确连接,那么您应该看到类似于2019-05-03 12:34:56+9600 [-] {"peer": "tcp4:192.168.99.100:8443", "headers": {"sec-websocket-accept": ...的日志消息2019-05-03 12:34:56+9600 [-] {"peer": "tcp4:192.168.99.100:8443", "headers": {"sec-websocket-accept": ...

此外,您之前启动的websocket服务器应显示日志消息,例如[INFO websocat::net_peer] Incoming TCP connection from Some(V4(172.17.0.1:35222))等等。

这证明客户端脚本已通过安全websocket成功连接到minikube的远程API,通过身份验证和访问控制,现在连接到minikube内的(不安全)websocket演示服务器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM