Hi, Would there be anyone with an example of a coturn config? I don't know how to setup the user and password. More about my context: I just put a coturn in place for my synapse server and it's working. I would like to use the same one in place for a future galene but I don't know if the secret for the API is in conflict with the user/password needed for galene. All examples and input welcomed. C.
"Cell" <galene.org@kn1ght.org> writes: > Hi, > > Would there be anyone with an example of a coturn config? I don't know > how to setup the user and password. > > More about my context: I just put a coturn in place for my synapse > server and it's working. I would like to use the same one in place for > a future galene but I don't know if the secret for the API is in > conflict with the user/password needed for galene. It is; you can't use both in the same coturn config. > All examples and input welcomed. What I do is just run two coturn instances on different ports, one for Galene with username/password, and one for other applications that use the REST API secret-based auth (the latter being Nextcloud Talk in my instance). -Toke
This would be my plan B then.
December 27, 2020 6:55 PM, "Toke Høiland-Jørgensen" <toke@toke.dk> wrote:
> "Cell" <galene.org@kn1ght.org> writes:
>
>> Hi,
>>
>> Would there be anyone with an example of a coturn config? I don't know
>> how to setup the user and password.
>>
>> More about my context: I just put a coturn in place for my synapse
>> server and it's working. I would like to use the same one in place for
>> a future galene but I don't know if the secret for the API is in
>> conflict with the user/password needed for galene.
>
> It is; you can't use both in the same coturn config.
>
>> All examples and input welcomed.
>
> What I do is just run two coturn instances on different ports, one for
> Galene with username/password, and one for other applications that use
> the REST API secret-based auth (the latter being Nextcloud Talk in my
> instance).
>
> -Toke
> _______________________________________________
> Galene mailing list -- galene@lists.galene.org
> To unsubscribe send an email to galene-leave@lists.galene.org
Damned. I read too fast.
Ok then, two instances is the way.
How do you declare the user/password in coturn?
December 27, 2020 6:55 PM, "Toke Høiland-Jørgensen" <toke@toke.dk> wrote:
> "Cell" <galene.org@kn1ght.org> writes:
>
>> Hi,
>>
>> Would there be anyone with an example of a coturn config? I don't know
>> how to setup the user and password.
>>
>> More about my context: I just put a coturn in place for my synapse
>> server and it's working. I would like to use the same one in place for
>> a future galene but I don't know if the secret for the API is in
>> conflict with the user/password needed for galene.
>
> It is; you can't use both in the same coturn config.
>
>> All examples and input welcomed.
>
> What I do is just run two coturn instances on different ports, one for
> Galene with username/password, and one for other applications that use
> the REST API secret-based auth (the latter being Nextcloud Talk in my
> instance).
>
> -Toke
"Cell" <galene.org@kn1ght.org> writes:
> Damned. I read too fast.
>
> Ok then, two instances is the way.
>
> How do you declare the user/password in coturn?
It's in the man page:
-u, --user
Long-term security mechanism credentials user account, in
the column-separated form username:key. Multiple user
accounts may be used in the command line. The key is
either the user password, or the key is generated by
turnadmin command. In the second case, the key must be
prepended with 0x symbols. The key is calculated over the
user name, the user realm, and the user password. This
setting may not be used with TURN REST API.
-Toke
> More about my context: I just put a coturn in place for my synapse > server and it's working. I would like to use the same one in place for > a future galene but I don't know if the secret for the API is in > conflict with the user/password needed for galene. According to what Toke explained to me, Cell is referring to this protocol: https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00 which is apparently designed to allow the WebRTC server to learn the TURN credentials on behalf of the WebRTC client. I'm not particularly keen on that particular protocol — I'd much rather we fixed ICE-TCP in Pion¹ — but I haven't made up my mind yet. ¹ https://github.com/pion/webrtc/issues/1356 -- Juliusz
>> More about my context: I just put a coturn in place for my synapse >> server and it's working. I would like to use the same one in place for >> a future galene but I don't know if the secret for the API is in >> conflict with the user/password needed for galene. > According to what Toke explained to me, Cell is referring to this protocol: > https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00 I've now read this document a second time, and I still don't get it. Could somebody please explain the purpose of this protocol? In normal TURN, there is a username/password key that must be known by the WebRTC peers. That's well known to be insecure, but it only protects access to the TURN server, so the only bad thing that could happen is that the TURN server will be used by other services. With the REST API, we introduce an extra web server. The web server and the TURN server somehow coordinate to generate ephemeral passwords (for a very lax notion of ephemeral -- the draft recommends 86400 seconds) which are communicated over HTTP to the WebRTC peers. The TURN credentials are now being rotated periodically, granted, but an attacker can simply contact the web server to find out what they are. We're adding an extra point of failure, but I don't see what we are gaining. What am I missing? -- Juliusz
Juliusz Chroboczek <jch@irif.fr> writes:
>>> More about my context: I just put a coturn in place for my synapse
>>> server and it's working. I would like to use the same one in place for
>>> a future galene but I don't know if the secret for the API is in
>>> conflict with the user/password needed for galene.
>
>> According to what Toke explained to me, Cell is referring to this protocol:
>
>> https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
>
> I've now read this document a second time, and I still don't get it.
> Could somebody please explain the purpose of this protocol?
>
> In normal TURN, there is a username/password key that must be known by the
> WebRTC peers. That's well known to be insecure, but it only protects
> access to the TURN server, so the only bad thing that could happen is that
> the TURN server will be used by other services.
>
> With the REST API, we introduce an extra web server. The web server and
> the TURN server somehow coordinate to generate ephemeral passwords (for
> a very lax notion of ephemeral -- the draft recommends 86400 seconds)
> which are communicated over HTTP to the WebRTC peers.
>
> The TURN credentials are now being rotated periodically, granted, but an
> attacker can simply contact the web server to find out what they are.
> We're adding an extra point of failure, but I don't see what we are gaining.
>
> What am I missing?
Well presumably the REST web server will only respond to the WebRTC
server (in coturn you specify an API key)? And the WebRTC server can
limit which clients it will give the actual TURN credentials to? I
suppose this is most useful in cases where users have to authenticate to
use the WebRTC service; with this you can revoke access (after the key
expires), whereas if you are using fixed TURN credentials any user that
has used the WebRTC service at any point in the past can continue to
access the TURN server indefinitely...
So yeah, I agree, it's not exactly Fort Knox, but it does provide some
additional access control as long as the WebRTC service itself is not
available without authentication (which may or may not be the case for
Galene deployments I suppose).
-Toke
> Well presumably the REST web server will only respond to the WebRTC server Ah... so the WebRTC signalling server only communicates the ICE configuration once a client has authentified. That was the bit I was missing. In Galène, authentication only happens when you join a group, so that would mean communicating the ICE configuration together with the "joined" message that carries the client's permissions. > (in coturn you specify an API key)? Ah, I see. Section 2.1 of the draft. Thanks, Toke, it makes a lot more sense now. -- Juliusz
Juliusz Chroboczek <jch@irif.fr> writes: >> Well presumably the REST web server will only respond to the WebRTC server > > Ah... so the WebRTC signalling server only communicates the ICE > configuration once a client has authentified. That was the bit I was > missing. > > In Galène, authentication only happens when you join a group, so that > would mean communicating the ICE configuration together with the "joined" > message that carries the client's permissions. Yup, that makes sense! >> (in coturn you specify an API key)? > > Ah, I see. Section 2.1 of the draft. > > Thanks, Toke, it makes a lot more sense now. You're welcome - I look forward to your implementation :) -Toke
>> In Galène, authentication only happens when you join a group, so that >> would mean communicating the ICE configuration together with the "joined" >> message that carries the client's permissions. > Yup, that makes sense! I've made the protocol change in the master branch, and made it so the TURN configuration can change at any time and Galène will notice within 5 minutes at most. We're not changing the configuration of previously joined clients yet, but the protocol could support it quite easily. (We'd simply need to send a "type=joined, kind=change" message to all the clients when we detect a change, and the clients will switch to the new TURN credentials at the next ICE restart.) > I look forward to your implementation :) I've put an implementation into the branch "tokes-folly". It's completely untested, please let me know if it works. I'm a little hesitant to merge it, since it is just as easily done by writing a five-line Python or Lua script that fetches the new configuration and dumps it into Galènes data directory. -- Juliuszp
Juliusz Chroboczek <jch@irif.fr> writes:
>>> In Galène, authentication only happens when you join a group, so that
>>> would mean communicating the ICE configuration together with the "joined"
>>> message that carries the client's permissions.
>
>> Yup, that makes sense!
>
> I've made the protocol change in the master branch, and made it so the
> TURN configuration can change at any time and Galène will notice within
> 5 minutes at most. We're not changing the configuration of previously
> joined clients yet, but the protocol could support it quite easily.
>
> (We'd simply need to send a "type=joined, kind=change" message to all the
> clients when we detect a change, and the clients will switch to the new
> TURN credentials at the next ICE restart.)
>
>> I look forward to your implementation :)
>
> I've put an implementation into the branch "tokes-folly". It's completely
> untested, please let me know if it works.
>
> I'm a little hesitant to merge it, since it is just as easily done by
> writing a five-line Python or Lua script that fetches the new
> configuration and dumps it into Galènes data directory.
Heh, I see what you mean. And indeed when looking at this I failed to
actually get coturn to return anything via any REST interface, so I went
and looked at what Nextcloud Talk is actually doing...
...And it turns out that I completely misunderstood how this is supposed
to work: there's not supposed to be any communication between the WebRTC
server and Coturn. Rather, there's a configured shared secret that the
WebRTC server can use to generate as many ephemeral credentials as it
wants. In Nextcloud Talk, the relevant code is just this:
$timestamp = $this->timeFactory->getTime() + 86400;
$rnd = $this->secureRandom->generate(16);
$username = $timestamp . ':' . $rnd;
$password = base64_encode(hash_hmac('sha1', $username, $server['secret'], true));
where $server['secret'] is the value configured as 'static-auth-secret'
in coturn. The resulting username and password is then communicated to
the client.
I didn't dig any further, so not sure if a separate set of credentials
is generated for every user in a call; but I suppose they could be? The
actual username could also be used in place of (or together with) the
random value above? Not sure what makes the most sense for Galene. TBH
I'm less concerned about the security aspects of this, and more about
compatibility with other software (i.e., being able to use
static-auth-secret at all).
My apologies for the misunderstanding! Hope the above makes (more) sense :)
-Toke
> ...And it turns out that I completely misunderstood how this is supposed > to work: Yeah, so did I. What coturn apparently implements makes a lot more sense than what the draft describes. Apparently, the credentials are computed deterministically from the username and a shared secret. In order to avoid replay, a timestamp is encoded within the username (phooey). Since both servers perform the same computation, there is no need for the brittle HTTP-based protocol. See https://github.com/coturn/coturn/blob/master/examples/etc/turnserver.conf#L209 Supposing I decide to implement this -- any ideas how this should be configured? -- Juliusz
Juliusz Chroboczek <jch@irif.fr> writes:
>> ...And it turns out that I completely misunderstood how this is supposed
>> to work:
>
> Yeah, so did I. What coturn apparently implements makes a lot more sense
> than what the draft describes.
>
> Apparently, the credentials are computed deterministically from the
> username and a shared secret. In order to avoid replay, a timestamp is
> encoded within the username (phooey). Since both servers perform the same
> computation, there is no need for the brittle HTTP-based protocol. See
>
> https://github.com/coturn/coturn/blob/master/examples/etc/turnserver.conf#L209
>
> Supposing I decide to implement this -- any ideas how this should be
> configured?
Well, Nextcloud Talk just takes server name/port, shared secret, and
whether to use UDP, TCP or both. The interval is hard-coded, and the
userid random as in the code I pasted before. So replicating that would
be fine with me. I seem to recall BBB had a similar config option...
-Toke
On 12/29/20 3:09 AM, Toke Høiland-Jørgensen wrote:
> Juliusz Chroboczek <jch@irif.fr> writes:
>> Supposing I decide to implement this -- any ideas how this should be
>> configured?
>
> Well, Nextcloud Talk just takes server name/port, shared secret, and
> whether to use UDP, TCP or both. The interval is hard-coded, and the
> userid random as in the code I pasted before. So replicating that would
> be fine with me.
+1
Ciao, Michael.
> ...And it turns out that I completely misunderstood how this is supposed
> to work: there's not supposed to be any communication between the WebRTC
> server and Coturn. Rather, there's a configured shared secret that the
> WebRTC server can use to generate as many ephemeral credentials as it
> wants.
I just pushed an implementation. Your ice-servers.json should look like
this:
[
{
"urls": [
"turn:turn.example.org:3479",
"turn:turn.example.org:3479?transport=tcp"
],
"username": "galene",
"credential": "secret",
"credentialType": "hmac-sha1"
}
]
In other words, I've kept the standard configuration syntax, just added
a non-standard value for "credentialType".
Your turnserver.conf should look like this:
use-auth-secret
static-auth-secret=secret
realm=trun.example.org
I've done some testing, but I didn't test that it will properly rotate the
key — please let me know if it survives 24h.
-- Juliusz
On Fri, 1 Jan 2021, at 23:55, Juliusz Chroboczek wrote:
> I just pushed an implementation. Your ice-servers.json should look like
> this:
>
> [
> {
> "urls": [
> "turn:turn.example.org:3479",
> "turn:turn.example.org:3479?transport=tcp"
> ],
> "username": "galene",
> "credential": "secret",
> "credentialType": "hmac-sha1"
> }
> ]
There is no username in the coturn configuration when using TURN REST API, so is the "username" key still necessary here?
--
Gabriel
>> "username": "galene", >> "credential": "secret", >> "credentialType": "hmac-sha1" > There is no username in the coturn configuration when using TURN REST API, > so is the "username" key still necessary here? It is optional. If present, it will be communicated to the TURN server in a secure manner (it cannot be spoofed by the client), so it may be used for logging or accounting. The protocol is fairly simple. The WebRTC server picks an expiration date for the credentials and encodes it as Unix time in base 10. It then sets if original_username == "" username = expires else username = expires:original_username password = BASE64(HMAC_SHA1(username, secret)) This is equivalent to the code that Toke posted earlier, except that that code picks the username at random. -- Juliusz
On 1/1/21 11:55 PM, Juliusz Chroboczek wrote:
>> ...And it turns out that I completely misunderstood how this is supposed
>> to work: there's not supposed to be any communication between the WebRTC
>> server and Coturn. Rather, there's a configured shared secret that the
>> WebRTC server can use to generate as many ephemeral credentials as it
>> wants.
>
> I just pushed an implementation.
> [..]
> In other words, I've kept the standard configuration syntax, just added
> a non-standard value for "credentialType".
>
> Your turnserver.conf should look like this:
>
> use-auth-secret
> static-auth-secret=secret
> realm=trun.example.org
>
> I've done some testing, but I didn't test that it will properly rotate the
> key — please let me know if it survives 24h.
I'm already using this (with git revision d2f7010) since 2+ days. No
issues so far.
How to ensure that it survived key rotation?
Does key rotation affect existing TURN sessions?
Maybe some logging would be good.
Ciao, Michael.
Michael Ströder <michael@stroeder.com> writes:
> On 1/1/21 11:55 PM, Juliusz Chroboczek wrote:
>>> ...And it turns out that I completely misunderstood how this is supposed
>>> to work: there's not supposed to be any communication between the WebRTC
>>> server and Coturn. Rather, there's a configured shared secret that the
>>> WebRTC server can use to generate as many ephemeral credentials as it
>>> wants.
>>
>> I just pushed an implementation.
>> [..]
>> In other words, I've kept the standard configuration syntax, just added
>> a non-standard value for "credentialType".
>>
>> Your turnserver.conf should look like this:
>>
>> use-auth-secret
>> static-auth-secret=secret
>> realm=trun.example.org
>>
>> I've done some testing, but I didn't test that it will properly rotate the
>> key — please let me know if it survives 24h.
>
> I'm already using this (with git revision d2f7010) since 2+ days. No
> issues so far.
>
> How to ensure that it survived key rotation?
> Does key rotation affect existing TURN sessions?
>
> Maybe some logging would be good.
+1 on the logging - in particular at startup. I messed up the JSON
syntax and didn't notice until the video started failing for some
people...
-Toke
On 1/7/21 1:14 PM, Toke Høiland-Jørgensen wrote:
> Michael Ströder <michael@stroeder.com> writes:
>> Maybe some logging would be good.
>
> +1 on the logging - in particular at startup. I messed up the JSON
> syntax and didn't notice until the video started failing for some
> people...
Same here...we're all humans...
Ciao, Michael.
> I'm already using this (with git revision d2f7010) since 2+ days. No > issues so far. Excellent. > How to ensure that it survived key rotation? Keep Galène running for more than 24h after the initial key is generated (which happens the first time a client joins). If the group is still accessible, then key rotation was successful. I've got unit tests for key rotation, but not for key rotation. See ice/ice_test.go. > Does key rotation affect existing TURN sessions? No. It doesn't even affect existing client sessions, you need to leave the group and re-join in order to get a new key. The protocol supports rotating a joined client's keys, but doing that properly would require maintaining key age for each client, so I didn't bother for now. A new key is generated every 2 to 5 minutes, and it has a validity of 24h, so you'll only run into the issue if you remain connected for (24h - 5min). In case anyone wants to hack on it, here's what's needed: - maintain credential age somewhere in the client structure; - at the right time, launch a goroutine to send { type: "join", kind: "changed"} to all clients nearing expiration; - for extra credit, trigger an ICE restart for all streams to and from affected clients so that existing streams pick up the new TURN credentials; not sure if that's better done in the client or in the server (either side can trigger a restart). > Maybe some logging would be good. Yeah. -- Juliusz