-1

I have a web server that maintains most of its content in a local database, but needs to query a back-end directory service to retrieve user information. The directory query works fine as a stand-alone operation, but when the web server makes the query, ssl returns an error.

The server is based on CentOS 7-2.1511 / Django 1.9.3 / PostgreSQL 9.2.15 / Apache 2.4.6-40 / mod_ssl 1:2.4.6-40 / OpenSSL 1:1.0.1 / Python 3.4.3. Apache uses mod_ssl to serve https: requests from the client (browser), and I'm assuming Python's ssl.py uses the same engine to make https: requests to the directory server. Python SSL says that it's implementation is based on OpenSSL. Yum apparently can't list mod_ssl's dependencies, but I assume it also uses the installed version of openssl.

The following code will take the user's distinguished name (extracted from SSL_CLIENT_CERT) and query the directory server for the user's attributes using a RESTful interface:

import requests, urllib

URL = 'https://example.com/rest/user_info/'

def get_user_info(dn)
    query = URL + urllib.parse.quote(dn)
    return requests.get(query, cert=('server.crt', 'server.key'),
                        verify='ca_bundle.crt').json()

When I am running on the server as user apache in the server's WSGI directory the routine correctly returns a dict containing user attributes:

$ python
>>> import auth
>>> dn='cn=Me,o=Company,c=US'
>>> attr = auth.get_user_info(dn)

But when Apache calls the same function with the same DN from it's WSGI script (views.py), it raises an OSError:

OSError(0, 'Error')
Line 810, /lib64/python3.4/ssl.py

803  def do_handshake(self, block=False):
804      """Perform a TLS/SSL handshake."""
805      self._check_connected()
806      timeout = self.gettimeout()
807      try:
808          if timeout == 0.0 and block:
809              self.settimeout(None)
810          self._sslobj.do_handshake()   

I will start researching locking as suggested for OpenSSL (since I can't think of anything else that would be causing these errors) but it's hard to believe that a webserver using SSL for backend queries isn't already a well-traveled path. Questions:

  1. Is multi-threading / locking / reentrancy barking up the right tree for the cause of these errors?
  2. Are there already worked examples for using SSL for webserver-backend connections?
Community
  • 1
  • 1
Dave
  • 3,834
  • 2
  • 29
  • 44
  • The first thing that popped into my eye: you sre using relative pathes to the certificate files. Your working directory might differ in the Apache environment. – Klaus D. Apr 06 '16 at 00:57
  • Good thought, but I simplified the example code from what I'm actually running. My working directory is the same as the code running on the server (a Django app directory `/srv/django/myapp`) and I have absolute path variables for SERVER_CERT, SERVER_KEY, and CA_BUNDLE. I have a single Python virtual environment on the server that is used by the Apache virtual host WSGIDaemon command and by my command line experiments, and as I said I'm running under user:group apache:apache. – Dave Apr 06 '16 at 13:12

1 Answers1

0

I give up. The following works reliably, at the expense of both beauty and efficiency. It tries a requests query first, and if that blows up, it punts and calls wget in a subprocess. It returns a meta item $method that lets the page view know whether the inline request failed.

def get_user_info(dn, sub=True, ttl=300):
    query = URL + urllib.parse.quote(dn)
    try:
        info = requests.get(query, cert=(SERVER_CERT, SERVER_KEY),
                            verify=CA_CERT).json()
        info['$method'] = 'requests'
        return info
    except OSError:
        if sub:
            args = ['wget', '-O', '-',
                '--certificate=' + SERVER_CERT,
                '--private-key=' + SERVER_KEY,
                query]
            bytes = subprocess.check_output(args, timeout=5)
            info = json.loads(bytes.decode(encoding='utf-8'))
            info['$method'] = 'subprocess'
            return info
        else:
            raise

It sure would be nice if OpenSSL used contexts instead of global variables.

Dave
  • 3,834
  • 2
  • 29
  • 44