Support connecting to hosting services with self-signed certificates.

Review Request #9766 — Created March 12, 2018 and submitted

Information

Review Board
release-3.0.x
9743316...

Reviewers

Most hosting services are third-party systems that will be served with
valid, secure SSL certificates. A few (such as rb-gateway, gerrit, or
GitLab) are run locally, and therefore may be deployed using self-signed
certificates. Until a couple years ago, this wasn't a problem, because
Python's urllib2 implementation didn't actually check SSL certificates
for validity. As of 2.7.9, it does.

We had some infrastructure in place for accepting invalid or self-signed
certificates, but it was only implemented for SCMTools, and even then
only for SVN and Perforce. Accepting certificates for services that were
connected to via urllib2, and especially services that go through a
HostingService, was not implemented.

This change does that. We now check for SSL-related errors when
attempting to authorize the account. In that case, we'll fetch and
decode the certificate, and show the "I trust this host" prompt to the
user. If the user accepts, we'll store the certificate in the
HostingServiceAccount's data field. Future attempts to use that
account will then add that certificate to the validation change (and
disable hostname checking, since self-signed certificates often are not
pinned to the correct hostname).

Set up rb-gateway using a self-signed certificate (along with a pending
change to enable SSL in rb-gateway) and attempted to connect it. Saw
the SSL certificate prompt, and selected "I trust this host". The
repository entry was saved, and the HostingServiceAccount.data field
contained the SSL certificate. Did some operations against the new
repository and saw that they completed successfully.

Description From Last Updated

F401 'reviewboard.hostingsvcs.service.HostingServiceClient' imported but unused

reviewbotreviewbot

F841 local variable 'fingerprint' is assigned to but never used

reviewbotreviewbot

F401 'urllib2.HTTPError' imported but unused

reviewbotreviewbot

, optional

chipx86chipx86

Can we make these part of the function signature? Prevents crashes in exception handling if the keys are somehow missing.

chipx86chipx86

We should probably localize this.

chipx86chipx86

Can we introduce a named logger for this file, to provide some context?

chipx86chipx86

This can be a bit nicer as: if 'CERTIFICATE_VERIFY_FAILED' not in six.text_type(e): raise # Rest of the stuff here, sans …

chipx86chipx86

Are these in a specific format that we can document?

chipx86chipx86

Can you specify the format the fingerprint will be in?

chipx86chipx86

So I found out about this. This is an authentication realm, and is designed to provide just a bit of …

chipx86chipx86

This doesn't exist in Python pre-2.7.9. Can you add a comment about the version requirement and that we shouldn't have …

chipx86chipx86

I believe we can end up in a situation where we hit this error on older Python 2.7.x releases (predating …

chipx86chipx86
Checks run (1 failed, 1 succeeded)
flake8 failed.
JSHint passed.

flake8

david
chipx86
  1. This is excellent. Thanks for taking this on!

  2. reviewboard/hostingsvcs/forms.py (Diff revision 2)
     
     
    Show all issues

    Can we make these part of the function signature? Prevents crashes in exception handling if the keys are somehow missing.

  3. reviewboard/hostingsvcs/models.py (Diff revision 2)
     
     
     
    Show all issues

    We should probably localize this.

    1. This is just bullet-proofing and isn't intended to be user-visible.

  4. reviewboard/hostingsvcs/rbgateway.py (Diff revision 2)
     
     
     
    Show all issues

    Can we introduce a named logger for this file, to provide some context?

  5. reviewboard/hostingsvcs/service.py (Diff revision 2)
     
     
     
     
     
    Show all issues

    This can be a bit nicer as:

    if 'CERTIFICATE_VERIFY_FAILED' not in six.text_type(e):
        raise
    
    # Rest of the stuff here, sans extra indentation.
    
  6. reviewboard/scmtools/certs.py (Diff revision 2)
     
     
     
     
     
     
     
     
    Show all issues

    Are these in a specific format that we can document?

    1. I don't think so. SVN gives us a "cert_data" structure that has it in whatever format the SVN implementation gives us. I'm using ISO-8601 for the urllib-based one. It just is something to show in the "I trust this host" pop-up.

  7. reviewboard/scmtools/certs.py (Diff revision 2)
     
     
     
    Show all issues

    Can you specify the format the fingerprint will be in?

    1. Not in any super useful way. Different implementations use whatever methods they care to. SVN and perforce seem to use legacy MD5-sums, and my new code uses sha256.

    2. Okay. In that case, can you update the docs to mention that this can be in various forms and is not expected to be a particular type?

  8. reviewboard/scmtools/certs.py (Diff revision 2)
     
     
     
     
    Show all issues

    So I found out about this. This is an authentication realm, and is designed to provide just a bit of additional context as to what's being logged into, mainly for error messages. It's not really part of SSL itself, more a SVN thing, I believe.

    So perhaps what we want is to replace this with a dictionary of extra metadata to go along with the certificate, and have SVN populate that with "realm" and pull it out of there as needed.

    1. That seems like a fine solution but not for this change. I'd also be happy just getting rid of "realm" entirely since I don't think anyone actually cares about it.

    2. The SVN approval process requires it, so we still need to store it.

  9. 
      
david
chipx86
  1. 
      
  2. reviewboard/hostingsvcs/forms.py (Diff revisions 2 - 3)
     
     
    Show all issues

    , optional

    1. This is not actually optional. I provide a default value because I need it to be a kwarg but it will always be provided by the caller and authorization won't succeed if it's not present.

  3. 
      
chipx86
  1. 
      
  2. reviewboard/hostingsvcs/service.py (Diff revision 3)
     
     
     
     
     
    Show all issues

    This doesn't exist in Python pre-2.7.9. Can you add a comment about the version requirement and that we shouldn't have been able to make it here on older versions due to the lack of ssl_cert in data?

  3. reviewboard/hostingsvcs/service.py (Diff revision 3)
     
     
     
    Show all issues

    I believe we can end up in a situation where we hit this error on older Python 2.7.x releases (predating the better SSL support in 2.7.9) for bad SSL certificates, and then we'd end up calling into ssl.* functions that don't exist in that version. Let's wire things off so that we don't get any further if we're running 2.7.8 or lower:

    if ('CERTIFICATE_VERIFY_FAILED' not in six.text_type(e) or
        not hasattr(ssl, 'create_default_context')):
        raise
    
  4. 
      
david
chipx86
  1. Ship It!
  2. 
      
david
Review request changed
Status:
Completed
Change Summary:
Pushed to release-3.0.x (c3eed44)