Coturn cannot find credentials

3,159 views
Skip to first unread message

Jo Yum

unread,
Jun 27, 2015, 8:09:31 PM6/27/15
to turn-server-project...@googlegroups.com
To Oleg or anyone else,

I'm testing coturn with ephemeral credentials.

When my app tries to connect the two users TEST002 and test005 over TURN relay, coturn writes this:

0: log file opened: /var/log/turn_11232_2015-06-27.log
0:
RFC 3489/5389/5766/5780/6062/6156 STUN/TURN Server
Version Coturn-4.4.5.2 'Ardee West'
0:
Max number of open files/sockets allowed for this process: 4096
0:
Due to the open files/sockets limitation,
max supported number of TURN Sessions possible is: 2000 (approximately)
0:

==== Show him the instruments, Practical Frost: ====

0: TLS supported
0: DTLS supported
0: DTLS 1.2 is not supported
0: TURN/STUN ALPN is not supported
0: Third-party authorization (oAuth) supported
0: GCM (AEAD) supported
0: OpenSSL compile-time version: OpenSSL 1.0.1e-fips 11 Feb 2013
0:
0: SQLite is not supported
0: Redis is not supported
0: PostgreSQL is not supported
0: MySQL supported
0: MongoDB is not supported
0:
0: Default Net Engine version: 3 (UDP thread per CPU core)

=====================================================

0: Listener address to use: 1.2.3.4
0: 64000 bytes per second allowed per session
0: Domain name:
0: Default realm: public
0: SSL23: Certificate file found: /etc/ssl/example.com.crt
0: SSL23: Private key file found: /etc/ssl/example.com.key
0: TLS1.0: Certificate file found: /etc/ssl/example.com.crt
0: TLS1.0: Private key file found: /etc/ssl/example.com.key
0: TLS1.1: Certificate file found: /etc/ssl/example.com.crt
0: TLS1.1: Private key file found: /etc/ssl/example.com.key
0: TLS1.2: Certificate file found: /etc/ssl/example.com.crt
0: TLS1.2: Private key file found: /etc/ssl/example.com.key
0: TLS cipher suite: DEFAULT
0: DTLS: Certificate file found: /etc/ssl/example.com.crt
0: DTLS: Private key file found: /etc/ssl/example.com.key
0: DTLS cipher suite: DEFAULT
0: Relay address to use: 1.2.3.4
0: pid file created: /var/run/turnserver.pid
0: IO method (main listener thread): epoll (with changelist)
0: WARNING: I cannot support STUN CHANGE_REQUEST functionality because only one IP address is provided
0: Wait for relay ports initialization...
0:   relay 1.2.3.4 initialization...
0:   relay 1.2.3.4 initialization done
0: Relay ports initialization done
0: IO method (general relay thread): epoll (with changelist)
0: turn server id=0 created
0: IPv4. TLS/SCTP listener opened on : 1.2.3.4:3478
0: IPv4. TLS/TCP listener opened on : 1.2.3.4:3478
0: IPv4. TLS/SCTP listener opened on : 1.2.3.4:5349
0: IPv4. TLS/TCP listener opened on : 1.2.3.4:5349
0: IO method (general relay thread): epoll (with changelist)
0: turn server id=1 created
0: IPv4. TLS/TCP listener opened on : 1.2.3.4:3478
0: IPv4. TLS/TCP listener opened on : 1.2.3.4:5349
0: IO method (general relay thread): epoll (with changelist)
0: turn server id=2 created
0: IPv4. TLS/TCP listener opened on : 1.2.3.4:3478
0: IPv4. TLS/TCP listener opened on : 1.2.3.4:5349
0: IO method (general relay thread): epoll (with changelist)
0: turn server id=3 created
0: IPv4. TLS/TCP listener opened on : 1.2.3.4:3478
0: IPv4. DTLS/UDP listener opened on: 1.2.3.4:3478
0: IPv4. TLS/TCP listener opened on : 1.2.3.4:5349
0: IPv4. DTLS/UDP listener opened on: 1.2.3.4:5349
0: Total General servers: 4
0: IO method (auth thread): epoll (with changelist)
0: IO method (auth thread): epoll (with changelist)
0: IO method (admin thread): epoll (with changelist)
0: IPv4. CLI listener opened on : 127.0.0.1:5766
0: MySQL DB connection success: host=localhost dbname=turn_db user=turn_usr password=xyz connect_timeout=60
0: handle_udp_packet: New UDP endpoint: local addr 1.2.3.4:3478, remote addr 5.6.7.8:2155
0: session 001000000000000001: realm <public> user <>: incoming packet BINDING processed, success
0: handle_udp_packet: New UDP endpoint: local addr 1.2.3.4:3478, remote addr 5.6.7.8:2300
0: session 000000000000000001: realm <public> user <>: incoming packet BINDING processed, success
10: session 000000000000000001: realm <public> user <>: incoming packet BINDING processed, success
10: session 001000000000000001: realm <public> user <>: incoming packet BINDING processed, success
31: handle_udp_packet: New UDP endpoint: local addr 1.2.3.4:3478, remote addr 5.6.7.8:2306
31: session 003000000000000001: realm <public> user <>: incoming packet BINDING processed, success
31: handle_udp_packet: New UDP endpoint: local addr 1.2.3.4:3478, remote addr 5.6.7.8:2305
31: session 000000000000000002: realm <public> user <>: incoming packet BINDING processed, success
31: handle_udp_packet: New UDP endpoint: local addr 1.2.3.4:3478, remote addr 5.6.7.8:2307
31: session 001000000000000002: realm <public> user <>: incoming packet BINDING processed, success
31: handle_udp_packet: New UDP endpoint: local addr 1.2.3.4:3478, remote addr 5.6.7.8:2304
31: session 001000000000000003: realm <public> user <>: incoming packet BINDING processed, success
31: session 001000000000000002: realm <public> user <>: incoming packet message processed, error 401: Unknown error
31: session 003000000000000001: realm <public> user <>: incoming packet message processed, error 401: Unknown error
31: session 000000000000000002: realm <public> user <>: incoming packet message processed, error 401: Unknown error
31: session 001000000000000003: realm <public> user <>: incoming packet message processed, error 401: Unknown error
31: handle_udp_packet: New UDP endpoint: local addr 1.2.3.4:3478, remote addr 5.6.7.8:2362
31: session 003000000000000002: realm <public> user <>: incoming packet BINDING processed, success
31: ERROR: check_stun_auth: Cannot find credentials of user <1435453531:TEST002>
31: session 001000000000000002: realm <public> user <1435453531:TEST002>: incoming packet message processed, error 401: Unknown error
31: ERROR: check_stun_auth: Cannot find credentials of user <1435453531:TEST002>
31: session 000000000000000002: realm <public> user <1435453531:TEST002>: incoming packet message processed, error 401: Unknown error
31: ERROR: check_stun_auth: Cannot find credentials of user <1435453531:TEST002>
31: ERROR: check_stun_auth: Cannot find credentials of user <1435453531:TEST002>
31: session 001000000000000003: realm <public> user <1435453531:TEST002>: incoming packet message processed, error 401: Unknown error
31: session 003000000000000001: realm <public> user <1435453531:TEST002>: incoming packet message processed, error 401: Unknown error
31: session 003000000000000002: realm <public> user <>: incoming packet message processed, error 401: Unknown error
31: ERROR: check_stun_auth: Cannot find credentials of user <1435455209:test005>
31: session 003000000000000002: realm <public> user <1435455209:test005>: incoming packet message processed, error 401: Unknown error
^C


Coturn is connecting to MySQL, but when the TURN requests arrive, coturn can't find credentials for the requesting users.

The same client code works fine when username and password are given in the config file as user=u:p and those credentials are sent in TURN requests.

Can you tell what's wrong?

Thank you.

Jo Yum

unread,
Jun 27, 2015, 11:24:40 PM6/27/15
to turn-server-project...@googlegroups.com
To Oleg or anyone else,

The IP's and domains shown below are not real ... but the real IP's and domains are correct.

Coturn works fine in anonymous mode, for two WebRTC clients (Chrome 43) running on a different network from the network of the Coturn server.

The two WebRTC clients and the Coturn server are each running on different machines.

To run Coturn with ephemeral credentials, I have this MySQL setup:
CREATE TABLE turnusers_lt ( 
realm varchar(127) default '',
name varchar(128),
hmackey char(128),
PRIMARY KEY (realm,name) );

CREATE TABLE turn_secret (
        realm varchar(127) default '',
        value varchar(128),
        primary key (realm,value)
);

CREATE TABLE allowed_peer_ip (
        realm varchar(127) default '',
        ip_range varchar(127),
        primary key (realm,ip_range)
);

CREATE TABLE denied_peer_ip (
        realm varchar(127) default '',
        ip_range varchar(127),
        primary key (realm,ip_range)
);

For the turnusers_lt table I had to shorten the name field to varchar(128) because with the original schema, the resulting index was too long, and MySQL didn't want to create the table until I shortened the name field.

The same happened for the allowed_peer_ip table and the denied_peer_ip table. I had to shorten both ip_range fields to varchar(127) because otherwise the indexes were too long for MySQL.

The allowed_peer_ip and denied_peer_ip tables are empty. Without them in the database, Coturn throws an error, so I created those tables but left them empty.

I'm starting Coturn like this:
turnserver -s 64000 -c turntest5.conf

turntest5.conf contains this:
listening-port=3478
tls-listening-port=5349
listening-ip=1.2.3.4
lt-cred-mech
use-auth-secret
static-auth-secret=abcdef
cert=/etc/ssl/example.com.ssl.crt
pkey=/etc/ssl/example.com.ssl.key
verbose
realm=public
mysql-userdb="host=localhost dbname=turn_db user=turn_usr password=xyz connect_timeout=60"

I tried including the static-auth-secret flag in the config file, and not including the static-auth-secret flag, forcing Coturn to use the MySQL table turn_secret

The turn_secret table contains:
{realm:public, value:abcdef}

The Coturn documentation is not clear as to what exactly should be in the turnusers_lt table. 

Before calling for TURN services, how should the web-server populate the turnusers_lt table? Which of these is the correct way?

realm: public
name: 1435455209:test005
hmackey: ZDY0NmM4MzllODZjY2Q0YmUyODRjZTJhYzg2NTIwYTU2NTAzNjFhMg==

realm: public
name: test005
hmackey: ZDY0NmM4MzllODZjY2Q0YmUyODRjZTJhYzg2NTIwYTU2NTAzNjFhMg==

realm: public
name: 1435455209:test005
hmackey:

realm: public
name: test005
hmackey:

Since I wasn't sure, I tried populating the turnusers_lt table each of the above ways, before requesting TURN services.

When the WebRTC app tries to connect the two clients TEST002 and test005 over TURN relay, Coturn writes this:
# turnserver -s 64000 -c turntest5.conf
Coturn is connecting to MySQL, but when the TURN requests arrive, Coturn cannot find credentials for the requesting users.

This is the Wireshark trace on WebRTC client (Chrome 43) of user TEST002:
client sends STUN request with {requested-transport:UDP}
Coturn sends 401 with {realm:public, nonce:91677bcb34bea256}
client sends STUN request with {username:1435453531:TEST002, realm:public, nonce:91677bcb34bea256, message-integrity-HMAC:28:21:5e:a0:04:c0:cf:83:1f:02:49:c5:3d:72:9d:bc:9d:2b:5e:1e}
Coturn sends another 401 with {realm:public, nonce:91677bcb34bea256}

The same WebRTC client code works fine when username and password are given in the config file as user=u:p and those credentials are sent in TURN requests.

Jo Yum

unread,
Jun 28, 2015, 3:19:15 AM6/28/15
to turn-server-project...@googlegroups.com
Update,

Wanted to see the results of using MySQL in Coturn for plain credentials (instead of ephemeral credentials).

So I commented out the secret flags in the config file:
#use-auth-secret
#static-auth-secret=abcdef

Then used turnadmin to set a password ('jolly'), for each of the users TEST002 and test005

Then started Coturn using the same command as before:
# turnserver -s 64000 -c turntest5.conf

Then modified the WebRTC app to NOT send ephemeral credentials, but just send plain credentials, like this:
pc_config = {'iceServers': [
   {'url': 'stun:1.2.3.4:3478','username':'','credential':''}, 
   {'url': 'turn:1.2.3.4:3478','username':'TEST002','credential':'jolly'}
]};

Now Coturn works !

Coturn queries MySQL, finds the username and its password, and matches them up with the incoming request. It does that for both clients.

The two clients get connected through TURN relay, and both remote streams work fine.

So the problem with ephemeral credentials is not that Coturn can't read the MySQL tables, the ephemeral credentials problem is something else.

With ephemeral credentials, Coturn has to parse the time-stamp and the username from the request's username field ('timestamp:username').

Then Coturn has to query MySQL for the secret_key.

Then Coturn has to generate the ephemeral password ( ephemeral_password = BASE64( HMAC-SHA1( turn_username, secret_key ) )

Then Coturn has to compare its generated ephemeral password with the one provided in the request.

Something in that Coturn chain is broken.

Any ideas what it could be?

Thank you.

Oleg Moskalenko

unread,
Jun 28, 2015, 1:48:43 PM6/28/15
to Jo Yum, turn-server-project...@googlegroups.com
what is the version of MySQL that you are using ?

Jo Yum

unread,
Jun 28, 2015, 2:34:27 PM6/28/15
to turn-server-project...@googlegroups.com, conr...@hotmail.com
Server version: 5.5.42-cll - MySQL Community Server (GPL)
Protocol version: 10

Oleg Moskalenko

unread,
Jun 28, 2015, 2:54:39 PM6/28/15
to Jo Yum, turn-server-project...@googlegroups.com
I cannot reproduce that problem. Try to upgrade to a newer version (or
to MariaDB).


On Sun, Jun 28, 2015 at 11:34 AM, Jo Yum <conr...@hotmail.com> wrote:
> Server version: 5.5.42-cll - MySQL Community Server (GPL)
> Protocol version: 10
>
> --
> You received this message because you are subscribed to the Google Groups
> "TURN Server (Open-Source project)" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to
> turn-server-project-rfc57...@googlegroups.com.
> To post to this group, send email to
> turn-server-project...@googlegroups.com.
> Visit this group at
> http://groups.google.com/group/turn-server-project-rfc5766-turn-server.
> For more options, visit https://groups.google.com/d/optout.

Jo Yum

unread,
Jun 28, 2015, 3:50:12 PM6/28/15
to turn-server-project...@googlegroups.com, conr...@hotmail.com
Oleg, 

I don't think MySQL is the problem. 

When I disable ephemeral credentials, (by commenting out the secret flags in the config file), Coturn has to query the MySQL turnusers_lt table for the username and realm, to match them up with the incoming TURN requests from that user/realm, and Coturn works fine in that mode. 

That means that Coturn is querying MySQL just fine, in non-ephemeral mode, for namerealm, and hmackey.

So why would MySQL fail to query correctly, in ephemeral mode, for name and realm only?

I assume that hmackey in turnusers_lt table, is irrelevant in ephemeral mode. 

In other words, Coturn probably doesn't care what's in the hmackey field in ephemeral mode, because it generates its own CALCULATED_HMAC_KEY, and then compares that with the incoming ephemeral password, like so:

temporary-username = "timestamp" + ":" + "username"
CALCULATED_HMAC_KEY = base64_encode( hmac-sha1( temporary-username, shared-secret ) )
Coturn compares its CALCULATED_HMAC_KEY with the incoming ephemeral password

I'm also assuming that Coturn decodes the incoming ephemeral password from Base64, before it compares that with its own CALCULATED_HMAC_KEY.

Is all that correct? If some of my assumptions are incorrect, that could be the problem.

As a side issue, can you tell me the procedure in detail, that turnadmin and turnserver use to generate the hmackey for long-term users ?

For example, is this the algorithm:

inString = username + realm + password ;
hmackey = HMAC-SHA1( inString  ) ;

Knowing this procedure, can the web-server safely generate a custom hmackey by using the session variable as the plain-text password ? ... like so:

inString = username + realm + session;
hmackey = HMAC-SHA1( inString  ) ;

Then, from the web-server, update hmackey in turnusers_lt table for the user in question.

Then tell the client to use the session variable as the plain-text password.

Then ask the client to request TURN services with the plain-text username and password.

This avoids the so-called "TURN REST API for Coturn", because we know that Coturn works fine with plain-text credentials.

But since we're using the time-limited session variable as the plain-text password, we still protect our TURN server from unauthorized users.

To make this work, I need the exact algorithm that Coturn uses to make the hmackey.

What do think?

Jo Yum

unread,
Jun 28, 2015, 7:58:05 PM6/28/15
to turn-server-project...@googlegroups.com, conr...@hotmail.com
Oleg,

To be sure that the MySQL table schema wasn't causing the problem, I rebuilt the MySQL turnusers_lt table like this:
CREATE TABLE turnusers_lt ( 
realm varchar(127) default '',
name varchar(512),
hmackey char(128),
PRIMARY KEY (realm(64),name(64)) );

The only difference with the schema given in the Coturn docs is that the index is limited to the first 64 characters of realm and the first 64 chars of name
Limiting the index of the turnusers_lt table to the first 64 chars in each column, should not affect the MySQL queries in any important way.
The column types and lengths are exactly those of the schema given in the Coturn docs.

The schema of the turn_secret table is exactly as given in the Coturn docs:
CREATE TABLE turn_secret (
        realm varchar(127) default '',
        value varchar(128),
        primary key (realm,value)
);

I added the secret flags in the config file:
use-auth-secret
static-auth-secret=abcdef
#
listening-port=3478
listening-ip=1.2.3.4
lt-cred-mech
verbose
realm=public
mysql-userdb="host=localhost dbname=turn_db user=turn_usr password=xyz connect_timeout=60"

I added the secret to the turn_secret table like this:
turnadmin -s abcdef -r public -M "host=localhost dbname=turn_db user=turn_usr password=xyz connect_timeout=60"

After rebuilding the turnusers_lt table I ran the WebRTC app (Chrome 43), and passed encoded credentials from PHP to the WebRTC app, as follows,
// PHP
$user = "TEST002" ;
$expire_time_unix_epoch = time() + (3 * 60 * 60) ;
$turn_username = $expire_time_unix_epoch . ":" . $user ;
$turn_secret_key = "abcdef" ;
$hmac_sha1 = hash_hmac( "sha1", $turn_username, $turn_secret_key ) ;
$turn_password = base64_encode( $hmac_sha1 ) ;
//
$turn_username ... "1435546325:TEST002"
$turn_password ...  "YWFjZGJlNDkyNmU0YjE3OTUyZTNiOWEwNzY5OGQ4NWQ3MTAxODkwMg=="

// JavaScript
pc_config = {'iceServers': [
   {'url': 'stun:1.2.3.4:3478','username':'','credential':''}, 
   {'url': 'turn:1.2.3.4:3478','username':"1435546325:TEST002",'credential':"YWFjZGJlNDkyNmU0YjE3OTUyZTNiOWEwNzY5OGQ4NWQ3MTAxODkwMg=="}
]};

Notice that the turn_password is Base64 encoded, and it, (among other things), is what the WebRTC app uses to generate the Message-Integrity attribute for the TURN request.

The WebRTC app (Chrome 43) does not send a Fingerprint attribute.

I got the same errors:
30: ERROR: check_stun_auth: Cannot find credentials of user <1435546325:TEST002>
30: session 001000000000000003: realm <public> user <1435546325:TEST002>: incoming packet message processed, error 401: Unknown error
30: ERROR: check_stun_auth: Cannot find credentials of user <1435546325:TEST002>
30: session 001000000000000002: realm <public> user <1435546325:TEST002>: incoming packet message processed, error 401: Unknown error
30: ERROR: check_stun_auth: Cannot find credentials of user <1435546325:TEST002>
30: session 001000000000000004: realm <public> user <1435546325:TEST002>: incoming packet message processed, error 401: Unknown error
30: ERROR: check_stun_auth: Cannot find credentials of user <1435546325:TEST002>
30: session 001000000000000001: realm <public> user <1435546325:TEST002>: incoming packet message processed, error 401: Unknown error
30: session 001000000000000005: realm <public> user <>: incoming packet message processed, error 401: Unknown error
30: ERROR: check_stun_auth: Cannot find credentials of user <1435540753:test003>

Then, I  commented out the secret flags in the config file:
#use-auth-secret
#static-auth-secret=abcdef
#
listening-port=3478
listening-ip=1.2.3.4
lt-cred-mech
verbose
realm=public
mysql-userdb="host=localhost dbname=turn_db user=turn_usr password=xyz connect_timeout=60"

Then I added the HMAC encoded clear-text password to each user in the turnusers_lt table, like this:
turnadmin -a -u TEST002 -r public -p jolly -M "host=localhost dbname=turn_db user=turn_usr password=xyz connect_timeout=60"

Then I ran the same WebRTC app (Chrome 43), and passed clear-text credentials, like this:
// JavaScript
pc_config = {'iceServers': [
   {'url': 'stun:1.2.3.4:3478','username':'','credential':''}, 
   {'url': 'turn:1.2.3.4:3478','username':"TEST002",'credential':"jolly"}
]};

Coturn worked perfectly !

The error "ERROR: check_stun_auth: Cannot find credentials of user" suggests that Coturn cannot find the "credentials" in the place where it was looking for them.
Is that place MySQL?
And, what "credentials" does the error refer to. The username, the password, the secret?

Any other ideas?

Thank you.

Jo Yum

unread,
Jun 28, 2015, 8:20:20 PM6/28/15
to turn-server-project...@googlegroups.com, conr...@hotmail.com
Oleg,

I made certain that the TURN username and password, which are created by PHP on the web-server:
1435546325:TEST002
YWFjZGJlNDkyNmU0YjE3OTUyZTNiOWEwNzY5OGQ4NWQ3MTAxODkwMg==

arrive intact at the WebRTC app (Chrome 43), on the JavaScript client:
1435546325:TEST002
YWFjZGJlNDkyNmU0YjE3OTUyZTNiOWEwNzY5OGQ4NWQ3MTAxODkwMg==

In case you were wondering.

Thank you.

Jo Yum

unread,
Jun 29, 2015, 2:08:15 AM6/29/15
to turn-server-project...@googlegroups.com, conr...@hotmail.com
Oleg,

Can you please help me diagnose this problem?

I need to get TURN working securely ASAP.

Thank you.

Oleg Moskalenko

unread,
Jun 29, 2015, 2:18:14 AM6/29/15
to Jo Yum, turn-server-project...@googlegroups.com
Sorry, Jo, I have no time to go into the fine details of the users
configuration. This is an open source non-commercial project. I can
assure you that:

1) It is working.
2) Other people figured that out and were able to use it properly.
3) The documentation is available and is correct.
4) There are other sources of information in the Internet about the
coturn configuration.

That's the best what I can do for you.

Regards,
Oleg

Fernando Escardó

unread,
Jun 29, 2015, 12:44:13 PM6/29/15
to turn-server-project...@googlegroups.com
Have you tried, not using "auth" for STUN, only for TURN? That is the "normal" behavior. You need to manually activate STUN auth (--secure-stun). And i don't even know if ephemeral is possible for STUN.

Then i suggest you activate mysql query log:

That should give an idea of what is trying to read from DB (if it is reading from db to authenticate).

Jo Yum

unread,
Jun 29, 2015, 3:47:27 PM6/29/15
to turn-server-project...@googlegroups.com, conr...@hotmail.com
Sorted, HMAC in PHP should be binary.

Thank you all

Tomasz G

unread,
Jul 2, 2015, 2:01:43 AM7/2/15
to turn-server-project...@googlegroups.com
Nice research Jo Yum!

Jo Yum

unread,
Jul 2, 2015, 10:51:05 AM7/2/15
to turn-server-project...@googlegroups.com
Thank you Tomasz,

Cheers

Max Allen

unread,
Sep 14, 2016, 11:43:12 AM9/14/16
to TURN Server (Open-Source project), conr...@hotmail.com
Wow! Thank you so much. I spent 2 days trying to find out what was wrong with my configuration. It's really not well documented. No real life examples for php, java web servers.

понедельник, 29 июня 2015 г., 23:47:27 UTC+4 пользователь Jo Yum написал:

_Boris _

unread,
Sep 7, 2017, 9:15:38 AM9/7/17
to TURN Server (Open-Source project)
Hi Jo Yum

I am facing similar problem ("Coturn cannot find credentials")  and I suspect that I calculate password incorrectly.
Could you please kindly check password for user:
1504790034:user is 4fJPIbDDpaQiz33kmzIyjpsAh8I=
with secret is set to secret

Thanks,
Boris
Reply all
Reply to author
Forward
0 new messages