Open ghstahl opened 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:
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.
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
],
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()
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":"<--"}
$cg:myConsumerGroup#centrifugo-connector-sa
Can you show logs for this case? I suppose in that case the following code results into permission denied:
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.
The log for the $cg:myConsumerGroup#centrifugo-connector-sa
case is in the first entry of this issue.
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
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()
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-
@ghstahl could you please share namespace configuration also?
Found it above, you are using "allow_user_limited_channels": true
for cg
namespace. That's why cg:myConsumerGroup#centrifugo-connector-sa
is allowed
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"
}
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
.
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.
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:
caps
, but as a union
when permission are checked. But probably you could build a better solution just on top of CEL expressions?
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
I have tried other combinations of channels in my claims. The
channelsdd
is the ones I have tried for thecg
namespace.Centrifugo Config.