Skip to content

When using user password authentication, there is a problem with the usage of 0.12.0. #2713

@become-nice

Description

@become-nice

Environment

os: debian 12
python-etcd3 version: 0.12.0
grpcio version: 1.72.0

bug

When SSL is not enabled on etcd server and user password authentication is used, an error message appears, indicating that a secure channel is not used.

grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
	status = StatusCode.UNAUTHENTICATED
	details = "Established channel does not have a sufficient security level to transfer call credential."
	debug_error_string = "UNKNOWN:Error received from peer  {grpc_message:"Established channel does not have a sufficient security level to transfer call credential.", grpc_status:16, created_time:"xxxxxx"}"

Reason

Because in the current implementation, call_credentials and insecure_channel are bound together using.
The function of call_credentials is to write metadata, that is, token information, to the rpc request package. However, when calling the PUT/GET interface, call_credentials and metadata are passed in at the same time. Metadata is also token information. These two parameters are repeated. When grpc version > 1.44.0, call_credentials and insecure_channel cannot be used at the same time.

    @_handle_errors
    def put(self, key, value, lease=None, prev_kv=False):
        """
        Save a value to etcd.

        Example usage:

        .. code-block:: python

            >>> import etcd3
            >>> etcd = etcd3.client()
            >>> etcd.put('/thing/key', 'hello world')

        :param key: key in etcd to set
        :param value: value to set key to
        :type value: bytes
        :param lease: Lease to associate with this key.
        :type lease: either :class:`.Lease`, or int (ID of lease)
        :param prev_kv: return the previous key-value pair
        :type prev_kv: bool
        :returns: a response containing a header and the prev_kv
        :rtype: :class:`.rpc_pb2.PutResponse`
        """
        put_request = self._build_put_request(key, value, lease=lease,
                                              prev_kv=prev_kv)
        return self.kvstub.Put(
            put_request,
            self.timeout,
            credentials=self.call_credentials,
            metadata=self.metadata
        )

Solve

When SSL is not enabled, call_credentials can be copied to None, thus avoiding the use of call_credentials while using insecure_channel.

class Etcd3Client(object):
    def __init__(self, host='localhost', port=2379,
                 ca_cert=None, cert_key=None, cert_cert=None, timeout=None,
                 user=None, password=None, grpc_options=None):
          ......
        if all(cred_params):
            self.auth_stub = etcdrpc.AuthStub(self.channel)
            auth_request = etcdrpc.AuthenticateRequest(
                name=user,
                password=password
            )

            resp = self.auth_stub.Authenticate(auth_request, self.timeout)
            self.metadata = (('token', resp.token),)
            if ca_cert is not None:
                self.call_credentials = grpc.metadata_call_credentials(
                    EtcdTokenCallCredentials(resp.token))

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions