ZSI + pyopenssl

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Hier hab ich ja schonmal ein kleines funktionstuechtiges Beispiel einer Socketverbindung mit pyopenssl gepostet. Wie man Pythons HTTPServer damit verheiraten kann, habe ich in diesem Rezept gefunden, das funktioniert auch super. Nun wollte ich gerne ZSI mit pyopenssl kombinieren (ich muss dazu sagen, dass ich von ZSI und Soap nicht so viel Ahnung habe...).

Um einen Service zu dispatchen, kann man den ServiceContainer verwenden. Da der auch nur von dem HTTPServer abgeleitet ist, hab gedacht, dass ich jetzt genauso wie in dem Rezept vorgehen kann:

Code: Alles auswählen

#!/usr/bin/env python

import os, socket

from ZSI.ServiceContainer import ServiceContainer, SOAPRequestHandler
from MatheService_services_server import SquareService
from OpenSSL import SSL, crypto
from BaseHTTPServer import HTTPServer

class SecureServiceContainer(ServiceContainer):
    def __init__(self, server_address, services=[],
                 RequestHandlerClass=SOAPRequestHandler):
        
        ServiceContainer.__init__(self, server_address, services,
                                  RequestHandlerClass)

        keystore = crypto.load_pkcs12(open("serverkey.p12").read(),
                                      "geheim")
        ctx = SSL.Context(SSL.SSLv23_METHOD)
        ctx.set_verify(SSL.VERIFY_PEER, self.verify_cb)
        ctx.use_privatekey(keystore.get_privatekey())
        ctx.use_certificate(keystore.get_certificate())
        ctx.load_verify_locations("ca-certs.pem")
        
        self.socket = SSL.Connection(ctx, socket.socket(self.address_family,
                                                        self.socket_type))
        self.server_bind()
        self.server_activate()


        
    def verify_cb(self, conn, cert, errnum, depth, ok):
        print "Got certificate: %s" % cert.get_subject()
        print "Issued by: %s" % cert.get_issuer()
        return ok


class SecureSoapRequestHandler(SOAPRequestHandler):
    def setup(self):
        self.connection = self.request
        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
        
    def do_GET(self):
        self.send_xml(SquareService._wsdl)


service_container = SecureServiceContainer(("127.0.0.1", 8080),
                                           RequestHandlerClass=SecureSoapRequestHandler)

for service in (SquareService(), ):
    path = service.getPost()
    service_container.setNode(service, path)

service_container.serve_forever()
Der Server versteht auch ein ganz normales GET, und da laeuft auch alles wie erwartet. Da ich noch nicht genau weiss, wie ich den SSL-Kram bei ZSI Client-seitig handhabe, hab ich mir zum Testen meines Webservices erstmal zwei Clients manuell gebastelt, einmal mit SSL und einmal zum vergleich ohne SSL. Es funktioniert ebenfalls wie erwartet, mit dem SSL-Client genau dann, wenn die Zertifikate auf beiden seiten richtig gesetzt sind, mit dem anderen Client gar nicht.

Was ich jetzt nicht verstehe: Nehme ich meinen ganz normalen ZSI-Client-Proxy, funktioniert die Verbinung und ich bekomme das Ergebnis meines Services-Calls zurueck:

Code: Alles auswählen

#!/usr/bin/env python

from MatheService_services import *
import sys

loc = SquareServiceLocator()
proxy = loc.getSquarePortType(tracefile=sys.stdout)

request = getSquareRequest()
request._request = 2.0
response = proxy.getSquare(request)

print ">>>", response._response
Warum? Ich wuerde jetzt erwarten, dass der Server die Verbindung ablehnt, da er ja ueberhaupt keine Zertifikate/Authentifizierung vom Client bekommt... Ich sehe ja auch, dass die callback-Funktion gar nicht aufgerufen wird, da sie keine Ausgabe produziert.

Naechste Frage ware dann, wie ich mit ZSI einen SSL-Client hinbekomme, denn mir geht es vor allem um den Client, nicht um den Server...

Hier sind die restlichen Dateien, die fuer ein lauffaehiges Beispiel benoetigt werden (ist das Standard-Beispiel mit dem Mathe-Service, den man in einigen Tutorails findet):

MatheService.wsdl
MatheService_services.py
MatheService_services_server.py
MatheService_services_types.py
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Moin Rebecca,
Rebecca hat geschrieben: [...]

Code: Alles auswählen

        ctx.set_verify(SSL.VERIFY_PEER, self.verify_cb)
[...]

Was ich jetzt nicht verstehe: Nehme ich meinen ganz normalen ZSI-Client-Proxy, funktioniert die Verbinung und ich bekomme das Ergebnis meines Services-Calls zurueck:

[...]

Warum? Ich wuerde jetzt erwarten, dass der Server die Verbindung ablehnt, da er ja ueberhaupt keine Zertifikate/Authentifizierung vom Client bekommt...
aus dem hohlen Bauch heraus verstehe ich das so: Dein Server prüft das Zertifikat vom Client nur, wenn der Client auch eines übermittelt. Das ZSI schickt dem Server aber keines und dem ist es egal, wenn keins kommt. Für den SSL-Handshake ist ja keines nötig.

Sollte funktionieren, wenn du obige Zeile durch die folgende austauschst:

Code: Alles auswählen

        ctx.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, self.verify_cb)
Aber wie gesagt: Nur ein Bauchgefühl. Gemacht habe ich sowas auch noch nicht.

Gruß,
Manuel
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Als ich nur pyopenssl und einfache sockets benutzte, oder pyopenssl + HTTPBaseServer, oder per HTTP GET auf den Webservice-Container zugriff, funktionierte es ja so, wie ich das denke: Nur mit SSL.VERIFY_PEER, und die Verbindung kommt genau dann zustande, wenn beide Seiten einen Schluessel benutzen und die richtigen CA-Zertifkate.

So erinnere ich mich jedenfalls, ich schaffe es heute wahrscheinlich nicht mehr, nochmal zu probieren. Ich schau morgen oder am Wochenende nochmal.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Ich bin mit dem Thema nicht wirklich weiter gekommen und konnte dazu auch nichts weiter im Internet finden. Ich habe den Tipp bekommen, doch einfach stunnel zu verwenden, und das funktioniert eigentlich auch ganz gut. Mir geht's erstmal nur um die Client-Seite, man kann stunnel dazu wie folgt starten:

Code: Alles auswählen

stunnel -c -r <host of service provider>:<port> -d localhost:7777 -P ~/.stunnel_pid -p privkey.pem
Wenn stunnel dann laeuft, kann man seinen Webservice ueber die Adresse localhost:7777 ansprechen:

Code: Alles auswählen

service = loc.getMyService_ServicePort(url="http://localhost:7777",
                                   tracefile=sys.stdout)
Ich hatte dabei das Problem, dass stunnel mit der Fehlermeldung "key values mismatch" beim Lesen des private Keys abbrach. Ich hatte mir das pem-file aus einem pkcs12-Keystore mit openssl umgewandelt, und in dem pem-file war auch das CA-Zertifkat vom Signer -- nachdem ich das rausgeschmissen hatte, ging es dann.

Das Verifizieren des CA-Zertifikats des Servers bekomme ich allerdings nicht hin. Man kann bei stunnel dazu die Option "-v 2 -A ca-certs.pem" angeben, und ich bin mir sehr sicher, das mein pem-file das richtige ca-Zertifkat enthaelt, dennoch bekomme ich die Fehlermeldung "Bad Certifcate", "unable to get issuer certificate"... :roll:

Bloede Sicherheit, macht immer alles so kompliziert.

Naja, jetzt kann ich wenigstens mal mit meinen Web Services weiterprobieren. :)
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Antworten