Клиент Python Websocket Autobahn: как подключиться к серверу, используя серверные и клиентские сертификаты? - PullRequest
0 голосов
/ 22 апреля 2019

Клиент веб-сокета (использующий Autobahn / Python и Twisted) должен подключиться к серверу веб-сокетов: клиент должен представить свой сертификат клиента серверу, а клиент должен проверить сертификат сервера.Эти сертификаты были созданы, например, во время установки мини-куба Kubernetes.В частности:

  • сертификат сервера ~/.minikube/ca.crt (в формате X509, насколько я понимаю).
  • сертификат клиента ~/.minikube/client.crt с ключом ~/.minikube/client.key.

Я проверил, что могу успешно использовать эти сертификаты + ключ для выполнения удаленных вызовов API Kubernetes с использованием curl.

Из примера echo_tls / client.py , представленного Autobahn, я понимаю, чтоМне может понадобиться использовать ssl.ClientContextFactory().ssl здесь относится к пакету pyopenssl, который автоматически импортируется витой.

Однако я не могу понять, как передать сертификаты на завод?

  • Как мне сказатьфактор веб-сокета для представления сертификата клиента серверу?
  • Как мне указать веб-сокету проверить сертификат сервера для обнаружения атак MITM?

1 Ответ

0 голосов
/ 03 мая 2019

После некоторых проб и ошибок я пришел к этому решению ниже.Чтобы помочь другим, я покажу не только код, но и справочную настройку для тестирования примера кода.

Сначала установите minikube, затем запустите экземпляр minikube;Я тестировал Minikube 1.0.0, который затем запускает Kubernetes 1.14, который был актуален на момент написания этой статьи.Затем запустите простой сервер веб-сокетов, который просто показывает, что отправлено на него, и отправит обратно все данные, которые вы сделали для подключенного клиента веб-сокетов.

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.Он пытается подключиться к wsserver, который мы только что начали, через удаленный API Kubernetes из мини-куба, используя удаленный API в качестве обратного прокси-сервера.При настройке мини-куба обычно используется взаимная аутентификация 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 работает на некоторых экземплярах мини-куба, но не на других.Я еще не выяснил, почему, по-видимому, похожие экземпляры ведут себя по-разному.

Мой текущий способ решения этой проблемы - просто использовать CN (общее имя) субъекта из сертификата сервера.Возможно, более надежный способ - прибегать к такой тактике только в том случае, если URL-адрес удаленного сервера API использует литерал IP-адреса, а не DNS-имя (или хотя бы метку).

Увы, запустите Python3 код выше python3 wssex.py.Если сценарий правильно подключен, вы должны увидеть сообщение журнала, подобное 2019-05-03 12:34:56+9600 [-] {"peer": "tcp4:192.168.99.100:8443", "headers": {"sec-websocket-accept": ...

. Кроме того, сервер веб-сокета, который вы запустили ранее, должен отображать сообщения журнала, такие как [INFO websocat::net_peer] Incoming TCP connection from Some(V4(172.17.0.1:35222)) и некоторые другие.

Это является доказательством того, что клиентский скрипт успешно подключился к удаленному API-интерфейсу minikube через защищенную веб-розетку, прошел проверку подлинности и контроль доступа и теперь подключен к (небезопасному) демонстрационному серверу веб-сокетов внутри minikube.

...