-
Notifications
You must be signed in to change notification settings - Fork 193
Description
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))