[Twisted-Python] PyOpenSSL empty socket property

Arn Vollebregt kpn.arn.vollebregt at gmail.com
Wed Sep 4 02:37:39 MDT 2019


Hi,

I noticed that PyOpenSSL SNI callbacks (set with
*ctx.set_tlsext_servername_callback*) receive a *OpenSSL.SSL.Connection*
object within Twisted that have an empty *_socket* property, while this
property *is* actually set when using *PyOpenSSL* directly. For my use-case
this is a problem as I want to call *conn._socket.getpeername()* to
determine the peer's IP address. So I am wondering: why is this behaviour
different? And how do I get the peer IP address?

---console---
user:~$ sudo python testTwisted.py &
[3] 32842
user:~$ curl -s --insecure --key clientPrivateKey.pem --cert
clientCertificate.pem https://127.0.0.1 > /dev/null
'sniCallback' called.
        conn._socket: None
'verifyCallback' called for result 0
        conn._socket: None
'verifyCallback' called for result 1
        conn._socket: None
user:~$ sudo python testPyOpenSSL.py &
[1] 33270
user:~$ curl -s --insecure --key clientPrivateKey.pem --cert
clientCertificate.pem https://127.0.0.1 > /dev/null
'sniCallback' called.
        conn._socket: <socket._socketobject object at 0x7f34c5bd3130>
<class 'OpenSSL.SSL.Connection'>
'verifyCallback' called for result 0
        conn._socket: <socket._socketobject object at 0x7f34c5bd3130>
'verifyCallback' called for result 1
        conn._socket: <socket._socketobject object at 0x7f34c5bd3130>
127.0.0.1 - - [29/Aug/2019 11:45:47] "GET / HTTP/1.1" 200 -
------

---testTwisted.py---
### Generate server key material ###
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout
serverPrivateKey.pem -out serverCertificate.pem -subj
"/C=''/O=''/OU=''/CN=server"
### Generate client key material ###
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout
clientPrivateKey.pem -out clientCertificate.pem -subj
"/C=''/O=''/OU=''/CN=client"
from __future__ import print_function
#https://twistedmatrix.com/documents/12.0.0/core/howto/ssl.html
from OpenSSL import SSL
from twisted.internet import ssl, reactor
from twisted.web import server, resource
from twisted.internet.protocol import Factory, Protocol

def verifyCallback(conn, cert, errno, depth, result):
    print('\'verifyCallback\' called for result ' + str(result))
    print('\tconn._socket: ' + str(conn._socket))
    return True

def sniCallback(conn):
    print('\'sniCallback\' called.')
    print('\tconn._socket: ' + str(conn._socket))

class MainResource(resource.Resource):
    isLeaf = True

    def render_GET(self, request):
        request.responseHeaders.addRawHeader("Content-Type", "text/html;
charset=utf-8")
        return b"<html><body>Hello World</body></html>"

if __name__ == '__main__':
    myContextFactory = ssl.DefaultOpenSSLContextFactory(
        'serverPrivateKey.pem',
        'serverCertificate.pem'
    )
    ctx = myContextFactory.getContext()
    #
https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_verify
    ctx.set_verify(SSL.VERIFY_PEER, verifyCallback)
    #
https://pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_tlsext_servername_callback
    ctx.set_tlsext_servername_callback(sniCallback)

    site = server.Site(MainResource())
    reactor.listenSSL(443, site, myContextFactory)
    reactor.run()
------

---testPyOpenSSL.py---
### Generate server key material ###
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout
serverPrivateKey.pem -out serverCertificate.pem -subj
"/C=''/O=''/OU=''/CN=server"
### Generate client key material ###
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout
clientPrivateKey.pem -out clientCertificate.pem -subj
"/C=''/O=''/OU=''/CN=client"
from __future__ import print_function
import socket, sys, os
from SocketServer import BaseServer
from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
from OpenSSL import SSL

def verifyCallback(conn, cert, errno, depth, result):
    print('\'verifyCallback\' called for result ' + str(result))
    print('\tconn._socket: ' + str(conn._socket))
    return True

def sniCallback(conn):
    print('\'sniCallback\' called.')
    print('\tconn._socket: ' + str(conn._socket))
    print(type(conn))

class SecureHTTPServer(HTTPServer):
    def __init__(self, server_address, HandlerClass):
        BaseServer.__init__(self, server_address, HandlerClass)
        ctx = SSL.Context(SSL.TLSv1_2_METHOD)
        ctx.use_privatekey_file('serverPrivateKey.pem')
        ctx.use_certificate_file('serverCertificate.pem')
        #
https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_verify
        ctx.set_verify(SSL.VERIFY_PEER, verifyCallback)
        #
https://pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_tlsext_servername_callback
        ctx.set_tlsext_servername_callback(sniCallback)
        self.socket = SSL.Connection(ctx,
socket.socket(self.address_family,self.socket_type))
        self.server_bind()
        self.server_activate()

    def shutdown_request(self,request):
        request.shutdown()

class SecureHTTPRequestHandler(SimpleHTTPRequestHandler):
    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_response(200)
        SimpleHTTPRequestHandler.end_headers(self)
        self.wfile.write('<html><body>Hello World</body></html>')

if __name__ == '__main__':
    ip,port = ('0.0.0.0', 443)
    httpd = SecureHTTPServer((ip, port), SecureHTTPRequestHandler)
    httpd.serve_forever()
------

(Please note that even though these examples are for Python2 (due to other
quirks) I am aiming to implement this in Python3.)

Regards,

Arn
-------------- next part --------------
An HTML attachment was scrubbed...
URL: </pipermail/twisted-python/attachments/20190904/62fa0936/attachment.html>


More information about the Twisted-Python mailing list