kragniz / kragniz/python-etcd3

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

Open
#2,713 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
Python
Stars
450
Forks
194
PR merge metrics
No merged PRs in 30d

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.
```shell
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.

```python
@_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.

```python
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))
```

Contributor guide

Open the contributing guide

Research direction

Start at Etcd3Client.__init__ and the put method shown in the issue, then trace how password authentication sets metadata and call_credentials for secure and insecure channels. Verify that password authentication works without SSL while the SSL path continues to use call credentials, using the existing test entry points if available.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, authentication
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.