authzed-py icon indicating copy to clipboard operation
authzed-py copied to clipboard

Failed to connect to all addresses; Endpoint is neither UDS or TCP loopback address

Open marlenekoh opened this issue 1 year ago • 9 comments

Hi, I am receiving this error when connecting to the spicedb client. The code only works when I am using localhost:50051 as the client address. Any help will be appreciated please 🙂

Traceback (most recent call last):
  File "/examples/write_schemas.py", line 20, in <module>
    resp = client.WriteSchema(WriteSchemaRequest(schema=SCHEMA))
  File "/usr/local/lib/python3.8/site-packages/grpc/_channel.py", line 1030, in __call__
    return _end_unary_response_blocking(state, call, False, None)
  File "/usr/local/lib/python3.8/site-packages/grpc/_channel.py", line 910, in _end_unary_response_blocking
    raise _InactiveRpcError(state)  # pytype: disable=not-instantiable
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
       status = StatusCode.UNAVAILABLE
       details = "failed to connect to all addresses; last error: UNKNOWN: ipv4:172.21.0.3:50051: Endpoint is neither UDS or TCP loopback address."
       debug_error_string = "UNKNOWN:failed to connect to all addresses; last error: UNKNOWN: ipv4:172.21.0.3:50051: Endpoint is neither UDS or TCP loopback address. {grpc_status:14, created_time:"2023-06-23T09:18:19.7666023+00:00"}"

write_schemas.py - https://github.com/authzed/authzed-py/tree/main/examples/v1

from authzed.api.v1 import Client
from authzed.api.v1 import WriteSchemaRequest
from grpcutil import insecure_bearer_token_credentials

SCHEMA = """definition blog/user {}

definition blog/post {
    relation reader: blog/user
    relation writer: blog/user

    permission read = reader + writer
    permission write = writer
}"""

client = Client(
    "spicedb:50051", # this does not work, change to localhost:50051 to work
    insecure_bearer_token_credentials("presharedkey"),
)

resp = client.WriteSchema(WriteSchemaRequest(schema=SCHEMA))

docker-compose.yml

version: "3"

x-spicedb-env: &spicedb-env
  environment:
    SPICEDB_DATASTORE_ENGINE: postgres
    SPICEDB_DATASTORE_CONN_URI: postgres://postgres:password@database:5432/spicedb
    SPICEDB_GRPC_PRESHARED_KEY: presharedkey

services:
  api:
    image: python:3.8
    volumes:
      - ./write_schemas.py:/examples/write_schemas.py
    command: bash -c "pip install authzed==0.9.0 && python3 /examples/write_schemas.py"
    networks:
      - spicedb

  database:
    image: postgres:14.3
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: spicedb
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - spicedb

  spicedb:
    image: authzed/spicedb:v1.20.0
    command: serve
    <<: *spicedb-env
    ports:
      - 50051:50051
    networks:
      - spicedb

  migrate:
    image: authzed/spicedb:v1.20.0
    command: migrate head
    <<: *spicedb-env
    networks:
      - spicedb

networks:
  spicedb:
    driver: bridge

Steps to reproduce

  1. Create write_schemas.py and docker-compose.yml
  2. Run the following commands
docker-compose up -d database
docker-compose up migrate
docker-compose up -d spicedb
docker-compose up api

marlenekoh avatar Jun 23 '23 09:06 marlenekoh

This is a bug for sure.

insecure_bearer_token_credentials is restricting the usage to only local addresses by adding the following to the channel credentials: grpc.local_channel_credentials(grpc.LocalConnectionType.LOCAL_TCP).

I think this behavior was well-intentioned, but there are definitely cases where folks are going to need plaintext outside of localhost: docker compose is an excellent example.

jzelinskie avatar Jul 05 '23 22:07 jzelinskie

There's a workaround you can do while we wait for fix:


x-spicedb-env: &spicedb-env
services:
  api:
    image: python:3.8
    volumes:
      - ./write_schemas.py:/examples/write_schemas.py
    command: bash -c "pip install authzed==0.9.0 && python3 /examples/write_schemas.py"
    network_mode: 'container:api'

  spicedb:
    image: authzed/spicedb:v1.20.0
    command: serve
    <<: *spicedb-env
    networks:
      - spicedb
networks:
  spicedb:
    driver: bridge

I've removed extra services information that stays the same but what you essentially do is move SpiceDB ports to api and map spicedb so that it runs on the same interface as api and that way tou can connect to localhost:50051 from within api.

jurecuhalev avatar Sep 13 '23 11:09 jurecuhalev

adding some more context to this thread, it seems like regardless of @jzelinskie 's thread over on the grpc GH page, the experimental code that they reference would not improve the situation here without additional changes to this codebase or spicedb's codebase (though I could have missed a cli arg to spice).

Some things I observed while hacking on the problem include,

  1. an insecure channel cannot be passed credentials. (https://github.com/grpc/grpc/issues/29643)
<AioRpcError of RPC that terminated with:
 status = StatusCode.UNAUTHENTICATED
 details = "Established channel does not have a sufficient security level to transfer call credential."
  1. redef'ing the bearer_token_credentials function to only use an insecure channel, raises the following from the authzed-py library
def bearer_token_credentials(token: str, certChain: Optional[bytes] = None):
    """
    gRPC credentials for a service that requires a Bearer Token.
    """
    return grpc.experimental.insecure_channel_credentials()


<AioRpcError of RPC that terminated with:
 status = StatusCode.UNAUTHENTICATED
 details = "invalid preshared key: rpc error: code = Unauthenticated desc = Request unauthenticated with bearer"

which, the above, appears to be a consequence of this library requiring a sercure channel; regardless.

  • https://github.com/authzed/authzed-py/blob/main/authzed/api/v1/init.py#L53
  1. redef'ing the client to remove the credential options doesn't work because authzed requires the preshared key be provided apparently.
client side
<_InactiveRpcError of RPC that terminated with:
        status = StatusCode.UNAUTHENTICATED
        details = "invalid preshared key: rpc error: code = Unauthenticated desc = Request unauthenticated with bearer"

and on the server-side
{"level":"info","requestID":"5d7c6b923354476e7e27dc392c4e5ca1","protocol":"grpc","grpc.component":"server",
"grpc.service":"authzed.api.v1.PermissionsService","grpc.method":"WriteRelationships","grpc.method_type":"unary",
"peer.address":"127.0.0.1:33106","grpc.start_time":"2023-09-14T03:19:03Z","grpc.code":"Unauthenticated",
"grpc.error":"rpc error: code = Unauthenticated desc = invalid preshared key: rpc error: code = Unauthenticated 
desc = Request unauthenticated with bearer","grpc.time_ms":"0.076","time":"2023-09-14T03:19:03Z",
"message":"finished call"}

so except for the workaround mentioned by @jurecuhalev (which I haven't tried, but I assume works) you're either out-of-luck, or you can try using the HTTP API which (per my understanding) is just a call to spicedb's loopback address and shouldn't be affected by this?

ghost avatar Sep 14 '23 03:09 ghost

I think this is also what I'm facing here 👉🏼 https://github.com/orgs/authzed/discussions/1644

FernandoArteaga avatar Nov 13 '23 01:11 FernandoArteaga

Any progress on this? I'm testing out spicedb for the first time, trying to make calls from within a k8s cluster, and I'd prefer using this client over the HTTP API if I can help it

mateenkasim avatar Nov 16 '23 19:11 mateenkasim

+1 that this is a serious issue for local docker compose setups

another workaround is to use socat to proxy the connection from localhost:50051 to the spicedb instance

did this by adding the following line to an entrypoint script:

# Proxy localhost:50051 to host.docker.internal:50051 (could do spicedb:50051 for same goal)
socat TCP-LISTEN:50051,fork TCP:host.docker.internal:50051 & 

nediamond avatar Jan 22 '24 18:01 nediamond

+1 Also run into this with our local docker compose dev environment

jtfidje avatar Mar 08 '24 07:03 jtfidje

+1 still happening

andrii-harbour avatar Jun 13 '24 16:06 andrii-harbour

https://gist.github.com/jakedt/100d75048c7ebefdf2fb666de58cda9b has a workaround for the insecure token to a non-localhost address

josephschorr avatar Jun 17 '24 17:06 josephschorr

I've added an implementation of an InsecureClient in the referenced PR. I'll probably get it reviewed and in tomorrow, but I'd love some additional eyes on it if any of y'all have some time.

tstirrat15 avatar Sep 03 '24 23:09 tstirrat15

The InsecureClient is now available in v0.18.0. Let me know what you think!

tstirrat15 avatar Sep 04 '24 14:09 tstirrat15

Apologies, v0.18.0 never made it to pypi. Try v0.18.1.

tstirrat15 avatar Sep 04 '24 17:09 tstirrat15

InsecureClient works like a charm – thank you! The new init_stubs method made it easy to add the HealthStub, too.

from authzed.api.v1 import InsecureClient
from grpc_health.v1.health_pb2_grpc import HealthStub
from grpc_health.v1.health_pb2 import HealthCheckRequest

class SpiceClient(InsecureClient, HealthStub):
    def __init__(self, options=None, compression=None):
        target = 'my-custom-target'
        token = 'my-personal-token'
        super().__init__(target, token, options, compression)

    def init_stubs(self, channel):
        super().init_stubs(channel)
        HealthStub.__init__(self, channel)

client = SpiceClient()
check = client.Check(HealthCheckRequest())
print(check) # status: SERVING

mateenkasim avatar Sep 10 '24 16:09 mateenkasim

Thank you for adding this support! Just one small comment—on our CI/CD pipelines, mypy raises a complaint about the following code:

Screenshot 2024-09-12 at 9 09 26 AM

with the failing error:

python -m mypy . --strict --ignore-missing-imports --exclude clients/python --exclude alembic_app_db --exclude alembic_ts_db --exclude tests --implicit-reexport --explicit-package-bases
config/config.py:143: error: Incompatible return value type (got "SyncClient", expected "Client")  [return-value]
Found 1 error in 1 file (checked 164 source files)

In the __init__.py file, the inheritance that should cause this check to pass shows up as expected, with SyncClient inheriting from Client: image

but in the __init__.pyi file, the inheritance isn't shown, which I'm thinking might be what's causing the error:

Screenshot 2024-09-12 at 9 24 25 AM

Is there any chance you could release a patch for this? Thank you!

alexshanabrook avatar Sep 13 '24 14:09 alexshanabrook

@alexshanabrook tracking in #207

tstirrat15 avatar Sep 19 '24 17:09 tstirrat15

Great, thank you!

alexshanabrook avatar Sep 19 '24 21:09 alexshanabrook