centrifugal / centrifugo

Scalable real-time messaging server in a language-agnostic way. Self-hosted alternative to Pubnub, Pusher, Ably. Set up once and forever.
https://centrifugal.dev
Apache License 2.0
8.12k stars 581 forks source link

[question] Subscribe to pro channel denied. #837

Open ghstahl opened 2 weeks ago

ghstahl commented 2 weeks ago

Could you give some insight as to why I am getting a permission denied using PRO(latest) to a private namespaced channel.

REQUEST: The logs, especially trace, should state why the permission was denied.

my jwt

{
  "caps": [
    {
      "allow": [
        "pub",
        "sub",
        "prs",
        "hst"
      ],
      "channels": [
        "*"
      ],
      "channelsdd": [
        "connector:*",
        "connector_pos:*",
        "$cg:*",
        "cg:*",
        "$cg:*#*",
        "*"
      ],
      "match": "wildcard"
    }
  ],
  "client_id": "centrifugo-connector-sa",
  "exp": 1719355004,
  "iat": 1719351404,
  "iss": "http://localhost:9802",
  "sub": "centrifugo-connector-sa"
}

I have tried other combinations of channels in my claims. The channelsdd is the ones I have tried for the cg namespace.

2024-06-25 14:50:25 {"level":"warn","time":"2024-06-25T21:50:25Z","message":"Centrifugo PRO license not found, run in sandbox mode: max connections 20, max nodes 2, max api rps 5"}
2024-06-25 14:50:25 {"level":"info","version":"5.4.1","release_time":"1718853019","edition":"pro","runtime":"go1.22.4","pid":1,"engine":"memory","gomaxprocs":20,"time":"2024-06-25T21:50:25Z","message":"starting Centrifugo"}
2024-06-25 14:50:25 {"level":"info","owner":"unlicensed","max_connections":20,"max_nodes":2,"max_api_rps":5,"time":"2024-06-25T21:50:25Z","message":"license information"}
2024-06-25 14:50:25 {"level":"info","path":"/centrifugo/config.json","time":"2024-06-25T21:50:25Z","message":"using config file"}
2024-06-25 14:50:25 {"level":"info","time":"2024-06-25T21:50:25Z","message":"serving websocket, api, admin endpoints on :8000"}
2024-06-25 14:50:32 {"level":"debug","method":"GET","status":101,"path":"/connection/websocket","addr":"192.168.48.1:57196","duration":"158.722µs","time":"2024-06-25T21:50:32Z","message":"http request"}
2024-06-25 14:50:32 {"level":"debug","client":"d1361224-f228-4fce-9c31-66904f1c7a16","transport":"websocket","time":"2024-06-25T21:50:32Z","message":"client connection established"}
2024-06-25 14:50:32 {"level":"trace","client":"d1361224-f228-4fce-9c31-66904f1c7a16","command":"{\"id\":1,\"connect\":{\"token\":\"eyJhbGciOiJFUzI1NiIsImtpZCI6IjBiMmNkMmU1NGM5MjRjZTg5ZjAxMGYyNDI4NjIzNjdkIiwidHlwIjoiSldUIn0.eyJjYXBzIjpbeyJhbGxvdyI6WyJwdWIiLCJzdWIiLCJwcnMiLCJoc3QiXSwiY2hhbm5lbHMiOlsiKiJdLCJjaGFubmVsc2RkIjpbImNvbm5lY3RvcjoqIiwiY29ubmVjdG9yX3BvczoqIiwiJGNnOioiLCJjZzoqIiwiJGNnOiojKiIsIioiXSwibWF0Y2giOiJ3aWxkY2FyZCJ9XSwiY2xpZW50X2lkIjoiY2VudHJpZnVnby1jb25uZWN0b3Itc2EiLCJleHAiOjE3MTkzNTU4MzIsImlhdCI6MTcxOTM1MjIzMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo5ODAyIiwic3ViIjoiY2VudHJpZnVnby1jb25uZWN0b3Itc2EifQ.Cn0hdrUBGs-50Tll_dY62BqHBfN8C5Q1uIk2p_Ihl94paVxVbh9QTooRGw3EdgK12sbSHFEilm1x--sfyL23tg\",\"name\":\"go\"}}","user":"","time":"2024-06-25T21:50:32Z","message":"<--"}
2024-06-25 14:50:32 {"level":"debug","client":"d1361224-f228-4fce-9c31-66904f1c7a16","user":"centrifugo-connector-sa","time":"2024-06-25T21:50:32Z","message":"client authenticated"}
2024-06-25 14:50:32 {"level":"trace","client":"d1361224-f228-4fce-9c31-66904f1c7a16","reply":"{\"id\":1,\"connect\":{\"client\":\"d1361224-f228-4fce-9c31-66904f1c7a16\",\"version\":\"5.4.1\",\"expires\":true,\"ttl\":3600,\"ping\":25,\"pong\":true}}","user":"centrifugo-connector-sa","time":"2024-06-25T21:50:32Z","message":"-->"}
2024-06-25 14:50:32 {"level":"trace","client":"d1361224-f228-4fce-9c31-66904f1c7a16","command":"{\"id\":2,\"subscribe\":{\"channel\":\"$cg:myConsumerGroup#centrifugo-connector-sa\",\"token\":\"eyJhbGciOiJFUzI1NiIsImtpZCI6IjBiMmNkMmU1NGM5MjRjZTg5ZjAxMGYyNDI4NjIzNjdkIiwidHlwIjoiSldUIn0.eyJjYXBzIjpbeyJhbGxvdyI6WyJwdWIiLCJzdWIiLCJwcnMiLCJoc3QiXSwiY2hhbm5lbHMiOlsiKiJdLCJjaGFubmVsc2RkIjpbImNvbm5lY3RvcjoqIiwiY29ubmVjdG9yX3BvczoqIiwiJGNnOioiLCJjZzoqIiwiJGNnOiojKiIsIioiXSwibWF0Y2giOiJ3aWxkY2FyZCJ9XSwiY2xpZW50X2lkIjoiY2VudHJpZnVnby1jb25uZWN0b3Itc2EiLCJleHAiOjE3MTkzNTU4MzIsImlhdCI6MTcxOTM1MjIzMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo5ODAyIiwic3ViIjoiY2VudHJpZnVnby1jb25uZWN0b3Itc2EifQ.Cn0hdrUBGs-50Tll_dY62BqHBfN8C5Q1uIk2p_Ihl94paVxVbh9QTooRGw3EdgK12sbSHFEilm1x--sfyL23tg\",\"recoverable\":true,\"join_leave\":true}}","user":"centrifugo-connector-sa","time":"2024-06-25T21:50:32Z","message":"<--"}
2024-06-25 14:50:32 {"level":"info","client":"d1361224-f228-4fce-9c31-66904f1c7a16","error":"invalid token","user":"centrifugo-connector-sa","time":"2024-06-25T21:50:32Z","message":"invalid subscription token"}
2024-06-25 14:50:32 {"level":"info","client":"d1361224-f228-4fce-9c31-66904f1c7a16","code":103,"command":"id:2 subscribe:{channel:\"$cg:myConsumerGroup#centrifugo-connector-sa\" token:\"eyJhbGciOiJFUzI1NiIsImtpZCI6IjBiMmNkMmU1NGM5MjRjZTg5ZjAxMGYyNDI4NjIzNjdkIiwidHlwIjoiSldUIn0.eyJjYXBzIjpbeyJhbGxvdyI6WyJwdWIiLCJzdWIiLCJwcnMiLCJoc3QiXSwiY2hhbm5lbHMiOlsiKiJdLCJjaGFubmVsc2RkIjpbImNvbm5lY3RvcjoqIiwiY29ubmVjdG9yX3BvczoqIiwiJGNnOioiLCJjZzoqIiwiJGNnOiojKiIsIioiXSwibWF0Y2giOiJ3aWxkY2FyZCJ9XSwiY2xpZW50X2lkIjoiY2VudHJpZnVnby1jb25uZWN0b3Itc2EiLCJleHAiOjE3MTkzNTU4MzIsImlhdCI6MTcxOTM1MjIzMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo5ODAyIiwic3ViIjoiY2VudHJpZnVnby1jb25uZWN0b3Itc2EifQ.Cn0hdrUBGs-50Tll_dY62BqHBfN8C5Q1uIk2p_Ihl94paVxVbh9QTooRGw3EdgK12sbSHFEilm1x--sfyL23tg\" recoverable:true join_leave:true}","error":"permission denied","reply":"id:2 error:{code:103 message:\"permission denied\"}","user":"centrifugo-connector-sa","time":"2024-06-25T21:50:32Z","message":"client command error"}
2024-06-25 14:50:32 {"level":"trace","client":"d1361224-f228-4fce-9c31-66904f1c7a16","reply":"{\"id\":2,\"error\":{\"code\":103,\"message\":\"permission denied\"}}","user":"centrifugo-connector-sa","time":"2024-06-25T21:50:32Z","message":"-->"}
2024-06-25 14:50:49 {"level":"trace","client":"d1361224-f228-4fce-9c31-66904f1c7a16","reply":"{}","user":"centrifugo-connector-sa","time":"2024-06-25T21:50:49Z","message":"-->"}
2024-06-25 14:50:49 {"level":"trace","client":"d1361224-f228-4fce-9c31-66904f1c7a16","command":"{}","user":"centrifugo-connector-sa","time":"2024-06-25T21:50:49Z","message":"<--"}

Centrifugo Config.

{
  "token_jwks_public_endpoint": "http://mock-oauth2-pro:50053/.well-known/jwks",
  "admin_password": "password",
  "admin_secret": "secret",
  "admin": true,
  "allowed_origins": [
    "http://localhost:3000"
  ],
  "namespaces": [
    {
      "name": "cg",
      "presence": true,
      "join_leave": true,
      "force_push_join_leave": true,
      "history_size": 0,
      "allow_publish_for_subscriber": true,
      "allow_presence_for_subscriber": true,
      "allow_user_limited_channels": true
    },
    {
      "name": "connector",
      "presence": true,
      "join_leave": true,
      "history_size": 200,
      "history_ttl": "300h",
      "force_positioning": true,
      "force_recovery": true,
      "allow_subscribe_for_client": true,
      "allow_subscribe_for_anonymous": true,
      "allow_publish_for_subscriber": true,
      "allow_publish_for_anonymous": true,
      "allow_history_for_subscriber": true,
      "allow_history_for_anonymous": true,
      "allow_presence_for_subscriber": true,
      "allow_presence_for_anonymous": true
    },
    {
      "name": "connector_pos",
      "force_recovery": true,
      "force_recovery_mode": "cache",
      "history_size": 1,
      "history_ttl": "300h",
      "allow_subscribe_for_client": true,
      "allow_subscribe_for_anonymous": true,
      "allow_publish_for_subscriber": true,
      "allow_publish_for_anonymous": true,
      "allow_history_for_subscriber": true,
      "allow_history_for_anonymous": true
    },
    {
      "name": "connector_private",
      "presence": true,
      "join_leave": true,
      "history_size": 200,
      "history_ttl": "300h",
      "force_positioning": true,
      "force_recovery": true
    }
  ]
}
FZambia commented 2 weeks ago

Hello @ghstahl

From what I see: you are trying to subscribe to channel $cg:myConsumerGroup#centrifugo-connector-sa, but you are subscribing to it using invalid subscription JWT. I suppose, in this case it's invalid because does not contain channel claim. Agree, this should be stated in error message in this case, i.e. need to wrap error here:

https://github.com/centrifugal/centrifugo/blob/57b383e5c8eab26500f59e14a076ce0d216b2875/internal/jwtverify/token_verifier_jwt.go#L665

At the same time, I do not understand why you are using connection token (with caps, without channel) for subscription. If you are subscribing with token you need to use subscription JWT. caps are not used there. channel is required, etc.

ghstahl commented 2 weeks ago

I have other examples in pro where I can subscribe using the same token for everything. My caps token does contain the channel claim.

  "channels": [
        "*"
      ],

Another example.

 "channels": [
       "connector:*",
        "connector_pos:*"
      ],

the ones that do work are when I subscribe to something like.

connector:foobar

Subscribing to

$cg:myConsumerGroup#centrifugo-connector-sa

Give a permission denied. I tried the following in my caps jwt.

 "channels": [
       "connector:*",
        "connector_pos:*",
        "cg:*,
        "$cg:*,
        etc
      ],

Subscribe example using caps.

My tokensource gives me tokens you see above.
A single jwt caps token for the PRO version.

endpoint := fmt.Sprintf("ws://localhost:%d/connection/websocket", internal.Port)
client := centrifuge.NewJsonClient(
endpoint,
centrifuge.Config{
    Name: ClientName,
    GetToken: func(centrifuge.ConnectionTokenEvent) (string, error) {
        token, err := tokenSource.Token()
        if err != nil {
            return "", err
        }
        return token.AccessToken, nil
    },
},
)
err = client.Connect()
sub, err := client.NewSubscription(internal.Channel,
                    centrifuge.SubscriptionConfig{
                        Recoverable: true,
                        JoinLeave:   true,
                        Positioned:  true,
                    })
err = sub.Subscribe()
ghstahl commented 2 weeks ago

This may be a private channel thing. $cg:{channel}#{sub}

I am able to subscribe to cg:foobar using a caps jwt token that has.

{
  "caps": [
    {
      "allow": [
        "pub",
        "sub",
        "prs",
        "hst"
      ],
      "channels": [
        "*"
      ],

      "match": "wildcard"
    }
  ],
  "client_id": "centrifugo-connector-sa",
  "exp": 1719415287,
  "iat": 1719411687,
  "iss": "http://localhost:9802",
  "sub": "centrifugo-connector-sa"
}
2024-06-26 07:21:27 {"level":"debug","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","transport":"websocket","time":"2024-06-26T14:21:27Z","message":"client connection established"}
2024-06-26 07:21:27 {"level":"trace","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","command":"{\"id\":1,\"connect\":{\"token\":\"eyJhbGciOiJFUzI1NiIsImtpZCI6IjBiMmNkMmU1NGM5MjRjZTg5ZjAxMGYyNDI4NjIzNjdkIiwidHlwIjoiSldUIn0.eyJjYXBzIjpbeyJhbGxvdyI6WyJwdWIiLCJzdWIiLCJwcnMiLCJoc3QiXSwiY2hhbm5lbHMiOlsiKiJdLCJjaGFubmVsc2RkIjpbImNvbm5lY3RvcjoqIiwiY29ubmVjdG9yX3BvczoqIiwiJGNnOioiLCJjZzoqIiwiJGNnOiojKiIsIioiXSwibWF0Y2giOiJ3aWxkY2FyZCJ9XSwiY2xpZW50X2lkIjoiY2VudHJpZnVnby1jb25uZWN0b3Itc2EiLCJleHAiOjE3MTk0MTUyODcsImlhdCI6MTcxOTQxMTY4NywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo5ODAyIiwic3ViIjoiY2VudHJpZnVnby1jb25uZWN0b3Itc2EifQ.KiVtplFZOL5Z-kz4LtirVe-2JZLMyldm1sWjDFv5-ypTTWH6fnRuJuPi1XC2oETfz3tFvlsIc6353OJ0qM_rhA\",\"name\":\"cg:foobar\"}}","user":"","time":"2024-06-26T14:21:27Z","message":"<--"}
2024-06-26 07:21:27 {"level":"debug","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","user":"centrifugo-connector-sa","time":"2024-06-26T14:21:27Z","message":"client authenticated"}
2024-06-26 07:21:27 {"level":"trace","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","reply":"{\"id\":1,\"connect\":{\"client\":\"47a553a3-a137-4df6-a68f-3eb3694d59de\",\"version\":\"5.4.1\",\"expires\":true,\"ttl\":3600,\"ping\":25,\"pong\":true}}","user":"centrifugo-connector-sa","time":"2024-06-26T14:21:27Z","message":"-->"}
2024-06-26 07:21:27 {"level":"trace","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","command":"{\"id\":2,\"subscribe\":{\"channel\":\"cg:foobar\",\"positioned\":true,\"recoverable\":true,\"join_leave\":true}}","user":"centrifugo-connector-sa","time":"2024-06-26T14:21:27Z","message":"<--"}
2024-06-26 07:21:27 {"level":"trace","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","reply":"{\"id\":2,\"subscribe\":{\"recoverable\":true,\"epoch\":\"osWY\",\"positioned\":true}}","user":"centrifugo-connector-sa","time":"2024-06-26T14:21:27Z","message":"-->"}
2024-06-26 07:21:27 {"level":"debug","channel":"cg:foobar","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","user":"centrifugo-connector-sa","time":"2024-06-26T14:21:27Z","message":"client subscribed to channel"}
2024-06-26 07:21:27 {"level":"trace","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","push":"{\"channel\":\"cg:foobar\",\"join\":{\"info\":{\"user\":\"centrifugo-connector-sa\",\"client\":\"47a553a3-a137-4df6-a68f-3eb3694d59de\"}}}","user":"centrifugo-connector-sa","time":"2024-06-26T14:21:27Z","message":"-->"}
2024-06-26 07:21:47 {"level":"trace","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","reply":"{}","user":"centrifugo-connector-sa","time":"2024-06-26T14:21:47Z","message":"-->"}
2024-06-26 07:21:47 {"level":"trace","client":"47a553a3-a137-4df6-a68f-3eb3694d59de","command":"{}","user":"centrifugo-connector-sa","time":"2024-06-26T14:21:47Z","message":"<--"}
FZambia commented 1 week ago

$cg:myConsumerGroup#centrifugo-connector-sa

Can you show logs for this case? I suppose in that case the following code results into permission denied:

https://github.com/centrifugal/centrifugo/blob/cc802148ddf20613316632ec0674c36528ac256e/internal/client/handler.go#L709

We had slightly different permission model in previous Centrifugo versions, having private channels and handling $ was actually a way to not introduce security problems. Though I think caps should ideally work with such channels too. In general - if you don't really need current private channel logic - you can change channel_private_prefix option from $ to sth different as workaround for the behavior. But let's make sure this is the problem - in logs you should see attempt to subscribe on private channel without token on WARN level in this case.

ghstahl commented 1 week ago

The log for the $cg:myConsumerGroup#centrifugo-connector-sa case is in the first entry of this issue.

FZambia commented 1 week ago

The log for the $cg:myConsumerGroup#centrifugo-connector-sa case is in the first entry of this issue.\

I explained the reason in https://github.com/centrifugal/centrifugo/issues/837#issuecomment-2190727327 - you tried to subscribe to it with malformed subscription JWT. That's what those logs show. You don't have to use subscription JWT if you want to use caps. Then you should hit https://github.com/centrifugal/centrifugo/issues/837#issuecomment-2193655633

ghstahl commented 1 week ago

I need direction on the caps and authorizations.

I am able to use a single jwt to subscribe to channels it wasn't meant to subscribe to. I have a channels entry in that jwt that doesn't specify the cg:* namespace.

EXPECTATION: I should not be allowed to subscribe to the cg:* channel.

The connection code.

    c.client = centrifuge.NewJsonClient(
        c.config.Endpoint,
        centrifuge.Config{
            GetToken: func(centrifuge.ConnectionTokenEvent) (string, error) {
                token, err := c.config.TokenSource.Token()
                if err != nil {
                    return "", err
                }
                return token.AccessToken, nil
            },
        },
    )
err := c.client.Connect()

c.sub, err = c.client.NewSubscription(c.channelName, centrifuge.SubscriptionConfig{
        Recoverable: true,
        JoinLeave:   true,
        Positioned:  true,
    })  
err = c.sub.Subscribe()

Logs

2024-06-27 09:09:42 {"level":"warn","time":"2024-06-27T16:09:42Z","message":"Centrifugo PRO license not found, run in sandbox mode: max connections 20, max nodes 2, max api rps 5"}
2024-06-27 09:09:42 {"level":"info","version":"5.4.1","release_time":"1718853019","edition":"pro","runtime":"go1.22.4","pid":1,"engine":"memory","gomaxprocs":20,"time":"2024-06-27T16:09:42Z","message":"starting Centrifugo"}
2024-06-27 09:09:42 {"level":"info","owner":"unlicensed","max_connections":20,"max_nodes":2,"max_api_rps":5,"time":"2024-06-27T16:09:42Z","message":"license information"}
2024-06-27 09:09:42 {"level":"info","path":"/centrifugo/config.json","time":"2024-06-27T16:09:42Z","message":"using config file"}
2024-06-27 09:09:42 {"level":"info","time":"2024-06-27T16:09:42Z","message":"serving websocket, api, admin endpoints on :8000"}
2024-06-27 09:09:51 {"level":"debug","method":"GET","status":101,"path":"/connection/websocket","addr":"192.168.160.1:54282","duration":"135.583µs","time":"2024-06-27T16:09:51Z","message":"http request"}
2024-06-27 09:09:51 {"level":"debug","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","transport":"websocket","time":"2024-06-27T16:09:51Z","message":"client connection established"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","command":"{\"id\":1,\"connect\":{\"token\":\"eyJhbGciOiJFUzI1NiIsImtpZCI6IjBiMmNkMmU1NGM5MjRjZTg5ZjAxMGYyNDI4NjIzNjdkIiwidHlwIjoiSldUIn0.eyJjYXBzIjpbeyJhbGxvdyI6WyJwdWIiLCJzdWIiLCJwcnMiLCJoc3QiXSwiY2hhbm5lbHMiOlsiY29ubmVjdG9yOioiLCJjb25uZWN0b3JfcG9zOioiXSwianVuayI6WyIqIl0sIm1hdGNoIjoid2lsZGNhcmQifV0sImNsaWVudF9pZCI6ImNlbnRyaWZ1Z28tY29ubmVjdG9yLXNhIiwiZXhwIjoxNzE5NTA4MTkxLCJpYXQiOjE3MTk1MDQ1OTEsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTgwMiIsInN1YiI6ImNlbnRyaWZ1Z28tY29ubmVjdG9yLXNhIn0.JmdyFOAQEsCTWSNy-HYsL2J4Y-CzDp5Y-B_0-MbQxujeZ01mXR7M8kGCizXcewHXq69fMvnZKqOkCE1N_FHWVA\",\"name\":\"go\"}}","user":"","time":"2024-06-27T16:09:51Z","message":"<--"}
2024-06-27 09:09:51 {"level":"debug","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"client authenticated"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","reply":"{\"id\":1,\"connect\":{\"client\":\"8ae0c391-f4a3-4beb-9d91-6373792d26c3\",\"version\":\"5.4.1\",\"expires\":true,\"ttl\":3600,\"ping\":25,\"pong\":true}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"-->"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","command":"{\"id\":2,\"subscribe\":{\"channel\":\"cg:myConsumerGroup#centrifugo-connector-sa\",\"positioned\":true,\"recoverable\":true,\"join_leave\":true}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"<--"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","reply":"{\"id\":2,\"subscribe\":{}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"-->"}
2024-06-27 09:09:51 {"level":"debug","channel":"cg:myConsumerGroup#centrifugo-connector-sa","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"client subscribed to channel"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","push":"{\"channel\":\"cg:myConsumerGroup#centrifugo-connector-sa\",\"join\":{\"info\":{\"user\":\"centrifugo-connector-sa\",\"client\":\"8ae0c391-f4a3-4beb-9d91-6373792d26c3\"}}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"-->"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","command":"{\"id\":3,\"publish\":{\"channel\":\"cg:myConsumerGroup#centrifugo-connector-sa\",\"data\":{\"type\":\"JOIN\",\"ts\":1719504591038785900}}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"<--"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","push":"{\"channel\":\"cg:myConsumerGroup#centrifugo-connector-sa\",\"pub\":{\"data\":{\"type\":\"JOIN\",\"ts\":1719504591038785900},\"info\":{\"user\":\"centrifugo-connector-sa\",\"client\":\"8ae0c391-f4a3-4beb-9d91-6373792d26c3\"}}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"-->"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","reply":"{\"id\":3,\"publish\":{}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"-->"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","command":"{\"id\":4,\"presence_stats\":{\"channel\":\"cg:myConsumerGroup#centrifugo-connector-sa\"}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"<--"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","reply":"{\"id\":4,\"presence_stats\":{\"num_clients\":1,\"num_users\":1}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"-->"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","command":"{\"id\":5,\"publish\":{\"channel\":\"cg:myConsumerGroup#centrifugo-connector-sa\",\"data\":{\"type\":\"TS\",\"ts\":1719504591038785900}}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"<--"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","push":"{\"channel\":\"cg:myConsumerGroup#centrifugo-connector-sa\",\"pub\":{\"data\":{\"type\":\"TS\",\"ts\":1719504591038785900},\"info\":{\"user\":\"centrifugo-connector-sa\",\"client\":\"8ae0c391-f4a3-4beb-9d91-6373792d26c3\"}}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"-->"}
2024-06-27 09:09:51 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","reply":"{\"id\":5,\"publish\":{}}","user":"centrifugo-connector-sa","time":"2024-06-27T16:09:51Z","message":"-->"}
2024-06-27 09:10:06 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","reply":"{}","user":"centrifugo-connector-sa","time":"2024-06-27T16:10:06Z","message":"-->"}
2024-06-27 09:10:06 {"level":"trace","client":"8ae0c391-f4a3-4beb-9d91-6373792d26c3","command":"{}","user":"centrifugo- 
FZambia commented 1 week ago

@ghstahl could you please share namespace configuration also?

FZambia commented 1 week ago

Found it above, you are using "allow_user_limited_channels": true for cg namespace. That's why cg:myConsumerGroup#centrifugo-connector-sa is allowed

ghstahl commented 1 week ago

I would argue that the "allow_user_limited_channels": true is a security issue.

Reason. It bypasses the caps/channels claims.

I have the ability to mint tokens whose claims can change but the sub:centrifugo-connector-sa remains the same.

This token, MUST not be allowed to get access to the cg:{channel}#centrifugo-connector-sa.

What I would like to see happen is that the caps/channels MUST be honored, then the user/sub must be honored.

To subscribe to the cg:blah#centrifugo-connector-sa channel, the following must be in the claims. The caps/channels must pass verification. It has the cg:*.
The sub:{user_id} must pass verification.

{
  "caps": [
    {
      "allow": [
        "pub",
        "sub",
        "prs",
        "hst"
      ],
      "channels": [
        "cg:*"
      ],
      "match": "wildcard"
    }
  ],
  "client_id": "my_client",
  "exp": 1719604570,
  "iat": 1719600970,
  "iss": "http://localhost:9802",
  "sub": "centrifugo-connector-sa"
}

The following claims are missing the cg:* channels claim. So the caps/channel check would fail.

{
  "caps": [
    {
      "allow": [
        "pub",
        "sub",
        "prs",
        "hst"
      ],
      "channels": [
        "hacker:*"
      ],
      "match": "wildcard"
    }
  ],
  "client_id": "stolen-identity",
  "exp": 1719604570,
  "iat": 1719600970,
  "iss": "http://localhost:9802",
  "sub": "centrifugo-connector-sa"
}
FZambia commented 1 week ago

It's possible to not enable allow_user_limited_channels and try building permissions fully based on caps. sub corresponds to user ID in JWT spec and thus in Centrifugo, so by enabling the allow_user_limited_channels feature you explicitly provide permissions for specific users to specific channels on namespace level.

I do not agree that if client passed a user-limited check then Centrifugo should also check caps (if exist) – because channel name itself (which uses user-limited separator #) and the explicitly enabled option already tell enough to allow subscription. Different client where only caps should be taken into account should have a different sub.

ghstahl commented 1 week ago

I can redesign the current project to not have `allow_user_limited_channels.

I still have a feeling I am going to run into a situation where a user will have access to stuff they shouldn't. It's all about context.

In our enterprise model a single user can be part of many orgs. When the user switches from one org to the other (a toggle in a browser), a new token is minted with all the claims that only apply to that org for that user.

If I have channels that are org specific, that users token can cross org boundaries.

FZambia commented 1 week ago

You of course should be very careful in that case. Channel names should include org part I suppose. It's hard to give exact advice without full understanding of the use case. But some thoughts to give you directions to think about: