Uploaded image for project: 'Couchbase Python Client Library'
  1. Couchbase Python Client Library
  2. PYCBC-702

Python SDK mistakenly throwing MixedAuthError error, expecting certificate-based authentication, when trying to use username/password authentication over SSL

    XMLWordPrintable

Details

    • Bug
    • Status: Resolved
    • Major
    • Resolution: Fixed
    • None
    • 2.5.11
    • None
    • None
    • 1

    Description

      Back in the days, before introducing certificate based authentication, users were able to do a "normal/standard/traditional" username/password over SSL authentication. This would be done with something like:

      from couchbase.cluster import Cluster
      from couchbase.cluster import PasswordAuthenticator
       
      cluster = Cluster('couchbases://localhost?certpath=/home/vagrant/cbcert.pem')
      authenticator = PasswordAuthenticator('david', 'password')
      cluster.authenticate(authenticator)
      cb = cluster.open_bucket('BUCKET_NAME')
      

      This is documented in https://docs.couchbase.com/python-sdk/current/managing-connections.html#ssl

      But with the introduction of certificate based authentication, the Python SDK now (mistakenly) expects that when you pass “certpath”, that you need to use the CertAuthenticator. You would receive something like:

      raise MixedAuthError(str(self.critical_complaints))
      couchbase.cluster.MixedAuthError: <["clashing params: got authenticator type PasswordAuthenticator but parameters defaultdict(None, {'connstr': set(['certpath'])}) overlap on CertAuthenticator"]>
      

      But this should not happen because for certificate based (“passwordless”) authentication, you need two additional parameters “truststorepath” and “keypath” ... you also need to create the client certificates and set up the cluster to accept certificate based auth.

      It seems that the Python SDK now does not allow you to do “traditional” username/password based auth over SSL.

      The workaround is doing something like this:

      from couchbase.bucket import Bucket
      connstr='couchbases://localhost/{}?certpath=/home/vagrant/cbcert.pem'
      credentials=dict(username='david',password='password')
      cb = Bucket(connstr.format('BUCKET_NAME'),**credentials)
      

      But I think it is worth looking into it and fixing.

      I have only tested in 2.5.7, but this likely affect all versions since the implementation of certificate based authentication, likely since PYCBC-453

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          Ellis.Breen Ellis Breen added a comment -

          http://review.couchbase.org/c/119851/ - here is a WIP which passes additional tests for this scenario but I want to test further first.

          Ellis.Breen Ellis Breen added a comment - http://review.couchbase.org/c/119851/  - here is a WIP which passes additional tests for this scenario but I want to test further first.
          Ellis.Breen Ellis Breen added a comment - - edited

          Roi Katz
          David Saadeh

          OK, have done various server tests with the code now and revised the code, please try:

          pip install --upgrade git+http://review.couchbase.org/couchbase-python-client@refs/changes/51/119851/17

          And let me know how you get on.

          Thanks,

          Ellis

          Ellis.Breen Ellis Breen added a comment - - edited Roi Katz David Saadeh OK, have done various server tests with the code now and revised the code, please try: pip install --upgrade git+ http://review.couchbase.org/couchbase-python-client@refs/changes/51/119851/17 And let me know how you get on. Thanks, Ellis
          austin Austin Gonyou added a comment - - edited

          FYI with python 2.7.16 on OSX catalina I used the above and tested the following code:

          from couchbase.cluster import Cluster
          from couchbase.bucket  import Bucket
          from couchbase.cluster import PasswordAuthenticator
          from couchbase.cluster import CertAuthenticator
           
           
          #cluster = Cluster('couchbase://localhost')
          cluster = Cluster('couchbases://localhost?certpath=cluster.crt')
          sauthenticator = CertAuthenticator('Answers', 'couchbase123',certpath='cluster.crt')
          #pauthenticator = PasswordAuthenticator('Answers', 'couchbase123')
          cluster.authenticate(sauthenticator)
          #cluster.authenticate(pauthenticator)
           
           
          #credentials=dict(username='Answers',password='couchbase123')
          bucket = cluster.open_bucket('questionnaire',**credentials)
          bucket = cluster.open_bucket('questionnaire')
          bucket.upsert('document-id', {'application': 'data'})
          

          I swap out sauthenticator and the different cluster options and use david's work around and the code works over 18091. I use pauthenticator options and it does not. 

          Output is here with sauthenticator chosen like above:

          MacBook-Pro-4:examples austin$ python test.py Traceback (most recent call last):   File "test.py", line 8, in <module>     sauthenticator = CertAuthenticator('Answers', 'couchbase123',certpath='cluster.crt') TypeError: __init__() got an unexpected keyword argument 'certpath'
          

           

          So is THIS fixed in 2.5.11?

           

          austin Austin Gonyou added a comment - - edited FYI with python 2.7.16 on OSX catalina I used the above and tested the following code: from couchbase.cluster import Cluster from couchbase.bucket  import Bucket from couchbase.cluster import PasswordAuthenticator from couchbase.cluster import CertAuthenticator     #cluster = Cluster( 'couchbase://localhost' ) cluster = Cluster( 'couchbases://localhost?certpath=cluster.crt' ) sauthenticator = CertAuthenticator( 'Answers' , 'couchbase123' ,certpath= 'cluster.crt' ) #pauthenticator = PasswordAuthenticator( 'Answers' , 'couchbase123' ) cluster.authenticate(sauthenticator) #cluster.authenticate(pauthenticator)     #credentials=dict(username= 'Answers' ,password= 'couchbase123' ) bucket = cluster.open_bucket( 'questionnaire' ,**credentials) bucket = cluster.open_bucket( 'questionnaire' ) bucket.upsert( 'document-id' , { 'application' : 'data' }) I swap out sauthenticator and the different cluster options and use david's work around and the code works over 18091. I use pauthenticator options and it does not.  Output is here with sauthenticator chosen like above: MacBook-Pro-4:examples austin$ python test.py Traceback (most recent call last):   File "test.py", line 8, in <module>     sauthenticator = CertAuthenticator('Answers', 'couchbase123',certpath='cluster.crt') TypeError: __init__() got an unexpected keyword argument 'certpath'   So is THIS fixed in 2.5.11?  
          Ellis.Breen Ellis Breen added a comment -

          Should be cert_path for the CertAuthenticator constructor, IIRC

          Ellis.Breen Ellis Breen added a comment - Should be cert_path for the CertAuthenticator constructor, IIRC

          OH my bad. let me change, but I got an error there as well. Here is the error after the change:

          MacBook-Pro-4:examples austin$ python test.py

          Traceback (most recent call last):

            File "test.py", line 8, in <module>

              sauthenticator = CertAuthenticator('Answers', 'couchbase123',cert_path='cluster.crt')

          TypeError: _init_() got multiple values for keyword argument 'cert_path'

           

          austin Austin Gonyou added a comment - OH my bad. let me change, but I got an error there as well. Here is the error after the change: MacBook-Pro-4:examples austin$ python test.py Traceback (most recent call last):   File "test.py", line 8, in <module>     sauthenticator = CertAuthenticator('Answers', 'couchbase123',cert_path='cluster.crt') TypeError: _ init _() got multiple values for keyword argument 'cert_path'  

          I'm not understanding why "multiple values for keyword"

          austin Austin Gonyou added a comment - I'm not understanding why "multiple values for keyword"
          Ellis.Breen Ellis Breen added a comment - - edited

          The CertAuthenticator constructor is as follows:

           

          def __init__(self, cert_path=None, key_path=None, trust_store_path=None, cluster_username=None, cluster_password=None):
          

           
          So in the absence of parameter names for cluster_username/cluster_password, it is expecting the cert_path argument first. So in effect, it is taking the first arg as a cert_path, and then a named arg as a cert_path.

          You need to either name all args or put them in the order listed, up to and until you start naming args (and then name all following args).

          Ellis.Breen Ellis Breen added a comment - - edited The CertAuthenticator constructor is as follows:   def __init__(self, cert_path=None, key_path=None, trust_store_path=None, cluster_username=None, cluster_password=None):   So in the absence of parameter names for cluster_username/cluster_password, it is expecting the cert_path argument first. So in effect, it is taking the first arg as a cert_path, and then a named arg as a cert_path. You need to either name all args or put them in the order listed, up to and until you start naming args (and then name all following args).

          OH I get it now. Sorry for being so thick headed. This makes total sense. I needed to go into the api and see. My apologies. Thank you!

          austin Austin Gonyou added a comment - OH I get it now. Sorry for being so thick headed. This makes total sense. I needed to go into the api and see. My apologies. Thank you!
          Ellis.Breen Ellis Breen added a comment -

          No problem, Austin, hope it's all working as expected now.

          Ellis.Breen Ellis Breen added a comment - No problem, Austin, hope it's all working as expected now.

          I feel like I did that here: 

           

          from couchbase.cluster import Cluster
          from couchbase.bucket  import Bucket
          from couchbase.cluster import PasswordAuthenticator
          from couchbase.cluster import CertAuthenticator
           
          cluster = Cluster('couchbases://localhost')
          sauthenticator = CertAuthenticator(cert_path='cluster.crt',key_path=None,trust_store_path=None,cluster_username='Answers',cluster_password='couchbase123')
          cluster.authenticate(sauthenticator)
          bucket = cluster.open_bucket('questionnaire')
          

           

          but got this error set:

           

          MacBook-Pro-4:examples austin$ python test.py 
          /Users/austin/Library/Python/2.7/lib/python/site-packages/couchbase/cluster.py:233: UserWarning: auth_credential and CertAuthenticator options overlap on keys set(['truststorepath', 'keypath'])
            warnings.warn("{} and {} options overlap on keys {}".format(item[0][0], item[1][0], clashes))
          Traceback (most recent call last):
            File "test.py", line 15, in <module>
              bucket = cluster.open_bucket('questionnaire')
            File "/Users/austin/Library/Python/2.7/lib/python/site-packages/couchbase/cluster.py", line 147, in open_bucket
              rv = self.bucket_class(str(connstr), **kwargs)
            File "/Users/austin/Library/Python/2.7/lib/python/site-packages/couchbase/bucket.py", line 245, in __init__
              super(Bucket, self).__init__(*args, **kwargs)
          couchbase.exceptions.NotSupportedError: <RC=0x13[Operation not supported], Couldn't create instance. Either bad credentials/hosts/bucket names were passed, or there was an internal error in creating the object, C Source=(src/bucket.c,979)>
          

           

          based on the example provided: 

           

          cert_path=None, key_path=None, trust_store_path=None, cluster_username=None, cluster_password=None):
          

           

          I feel like I honored those requirements and the cert is indeed in the local directory. 

          -rw-r--r--  1 austin  staff    1103 Jan  9 09:15 cluster.crt
          

           

           

           

           

          austin Austin Gonyou added a comment - I feel like I did that here:    from couchbase.cluster import Cluster from couchbase.bucket  import Bucket from couchbase.cluster import PasswordAuthenticator from couchbase.cluster import CertAuthenticator   cluster = Cluster( 'couchbases://localhost' ) sauthenticator = CertAuthenticator(cert_path= 'cluster.crt' ,key_path=None,trust_store_path=None,cluster_username= 'Answers' ,cluster_password= 'couchbase123' ) cluster.authenticate(sauthenticator) bucket = cluster.open_bucket( 'questionnaire' )   but got this error set:   MacBook-Pro- 4 :examples austin$ python test.py /Users/austin/Library/Python/ 2.7 /lib/python/site-packages/couchbase/cluster.py: 233 : UserWarning: auth_credential and CertAuthenticator options overlap on keys set([ 'truststorepath' , 'keypath' ])   warnings.warn( "{} and {} options overlap on keys {}" .format(item[ 0 ][ 0 ], item[ 1 ][ 0 ], clashes)) Traceback (most recent call last):   File "test.py" , line 15 , in <module>     bucket = cluster.open_bucket( 'questionnaire' )   File "/Users/austin/Library/Python/2.7/lib/python/site-packages/couchbase/cluster.py" , line 147 , in open_bucket     rv = self.bucket_class(str(connstr), **kwargs)   File "/Users/austin/Library/Python/2.7/lib/python/site-packages/couchbase/bucket.py" , line 245 , in __init__     super (Bucket, self).__init__(*args, **kwargs) couchbase.exceptions.NotSupportedError: <RC= 0x13 [Operation not supported], Couldn't create instance. Either bad credentials/hosts/bucket names were passed, or there was an internal error in creating the object, C Source=(src/bucket.c, 979 )>   based on the example provided:    cert_path=None, key_path=None, trust_store_path=None, cluster_username=None, cluster_password=None):   I feel like I honored those requirements and the cert is indeed in the local directory.  -rw-r--r--  1 austin  staff    1103 Jan  9 09:15 cluster.crt        
          Ellis.Breen Ellis Breen added a comment - - edited

          CertAuthenticator is just for X509-style certificate auth without passwords, not SSL with passwords. The user/pass there is just for admin use. TBH all the cert_auth, trust_store_path, key_store_path should be mandatory, not sure why I didn't make them so.

          For SSL with passwords, use PasswordAuthenticator and a couchbases://hostname...?certpath=xxx connstr in the Cluster constructor, as you were doing before with pauthenticator. What error if any was this giving?

          Ellis.Breen Ellis Breen added a comment - - edited CertAuthenticator is just for X509-style certificate auth without passwords, not SSL with passwords. The user/pass there is just for admin use. TBH all the cert_auth, trust_store_path, key_store_path should be mandatory, not sure why I didn't make them so. For SSL with passwords, use PasswordAuthenticator and a couchbases://hostname...?certpath=xxx connstr in the Cluster constructor, as you were doing before with pauthenticator. What error if any was this giving?
          austin Austin Gonyou added a comment - - edited

          Thank you for that! So then when using just a username/password with the cluster cert as I'm trying to do needs to use the workaround David had noted or is there a different syntax? 

           

          My apologies. You noted this above. Thank you!

          austin Austin Gonyou added a comment - - edited Thank you for that! So then when using just a username/password with the cluster cert as I'm trying to do needs to use the workaround David had noted or is there a different syntax?    My apologies. You noted this above. Thank you!

          People

            Ellis.Breen Ellis Breen
            david.saadeh David Saadeh (Inactive)
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes

                PagerDuty