CoTURN: How to use TURN REST API?
Asked Answered
R

9

40

I have build coturn and run it successfully. ip:192.168.1.111. Now the question I faced is to get the Turn credential through REST API. https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00 According to the passage the request format should be

GET /?service=turn&username=mbzrxpgjys

and response should be JSON. Now my question is:

a) How to configure and command TURN SERVER to make it run in REST API mode?

b) How to write a http request in the right format so TURN SERVER can reply correctly? could you give me an example?

Rinker answered 3/3, 2016 at 8:1 Comment(0)
H
74

Few things to be clarified here are:

  • GET /?service=turn&username=mbzrxpgjys which returns a JSON, is just a suggested uri for retrieving time-limited TURN credentials from the server, you do not have to follow that, your uri can be just /?giveMeCredentials. In fact, I use my socket connection to retrieve this data, not direct http call with json response. End of day, it does not matter how you( the client that uses said TURN) get those credentials as long as they are valid.

  • You do not make any requests to the TURN server directly, no rest api call to TURN server is under your control.

  • you allocate a secret key when you are starting the TURN server, this can be taken from a db(thus dynamically changable), but lazy that I am, just hard-coded, and gave it in the turn config file, also remember to enable REST API. As part of turn command, turnserver ... --use-auth-secret --static-auth-secret=MySecretKey

  • Now, in your application server, you would use the same secret key to generate credentials, for username, it is UNIX timestamp and some string( can be random or user id or something) seperated by : and the password would be HMAC of the username with your secret key.

  • about the UNIX timestamp, this has be the time in TURN server till which your credentials has to be valid, so which calculating this make sure you take into account of the clock time difference between your application server and your turn server.

Now some sample code taken from my answer to another question

command for stating TURN server:

turnserver -v --syslog -a -L xx.xxx.xx.xx -X yy.yyy.yyy.yy -E zz.zzz.zz.zzz --max-bps=3000000 -f -m 3 --min-port=32355 --max-port=65535 --use-auth-secret --static-auth-secret=my_secret --realm=north.gov --cert=turn_server_cert.pem --pkey=turn_server_pkey.pem --log-file=stdout -q 100 -Q 300 --cipher-list=ALL

node.js code for creating TURN credentials in application server:

var crypto = require('crypto');

function getTURNCredentials(name, secret){    

    var unixTimeStamp = parseInt(Date.now()/1000) + 24*3600,   // this credential would be valid for the next 24 hours
        username = [unixTimeStamp, name].join(':'),
        password,
        hmac = crypto.createHmac('sha1', secret);
    hmac.setEncoding('base64');
    hmac.write(username);
    hmac.end();
    password = hmac.read();
    return {
        username: username,
        password: password
    };
}

Browser code for using this:

  ...
  iceServers:[
    {
      urls: "turn:turn_server_ip",
      username: username,
      credential:password
    }
  ...
Heber answered 3/3, 2016 at 8:48 Comment(4)
Thanks for mentioning that the timestamp is not the actual timestamp but the expiration timestamp, I forgot that. I also had to use the RAW output of HMAC, like @SparX said, otherwise I got this error: 489: session 001000000000000004: realm <> user <>: incoming packet message processed, error 401: Unauthorized 489: check_stun_auth: Cannot find credentials of user <1564836087:webrtc>. By the way, the end of the username :randomText is not needed, you can just use the timestamp, like I read in coturn config file.Throckmorton
If someone is looking for C# code: gist.github.com/robingenz/e47f42452d82fa9c1fa529b9ae3be95cTamer
can anyone help me with this please #64155050Viscose
I was pulling my hair out all day today trying this, just to find out somewhat randomly that in order for this to work, one has to also specify a realm. If the realm option is not set in the configuration, it doesn't work and one gets the realm <> user <> error messages. Maybe this can save someone some hair!Charismatic
B
12

After (many) hours of frustration, @Mido's excellent answer here was the only thing that actually got CoTurn's REST API working for me.

My credential server is PHP and I use CoTurn's config file 'turnserver.conf' so here's a tested and working translation of Mido's work for that situation:

Assuming a 'shared secret' of '3575819665154b268af59efedee8826e', here are the relevant turnserver.conf entries:

lt-cred-mech
use-auth-secret
static-auth-secret=3575819665154b268af59efedee8826e

...and the PHP (which misled me for ages):

$ttl = 24 * 3600;  // Time to live
$time = time() + $ttl;
$username = $time . ':' . $user;
$password = base64_encode(hash_hmac('sha1', $username, '3575819665154b268af59efedee8826e', true));
Brentbrenton answered 16/2, 2019 at 16:18 Comment(2)
The secret hash you used in your answer reminded me that coturn says the secret should be hashed with MD5 but I tested without hashing the secret and it worked too. Is there an advantage of using a hashed secret? I guess using a secret that is not hashed is secure enough. I also noticed that the configuration entries lt-cred-mech and use-auth-secret are not needed. You just have to use static-auth-secret (and cert + pkey to use an SSL certificate). In the WebRTC JavaScript code, the port 5349 should be used for secure connection, otherwise it uses unencrypted port 3478.Throckmorton
@Throckmorton The only reason I use a secret which is a hash of something, is to make it easy to automate changing it, while being pretty impossible to guess or brute force. I also own only so many cats, so run out of cat-name-passwords pretty quickly.Brentbrenton
B
8

Building upon @Mido and @HeyHeyJC answers, here is the Python implementation to build credentials for coturn.

import hashlib
import hmac
import base64
from time import time

user = 'your-arbitrary-username'
secret = 'this-is-the-secret-configured-for-coturn-server'

ttl = 24 * 3600 # Time to live
timestamp = int(time()) + ttl
username = str(timestamp) + ':' + user
dig = hmac.new(secret.encode(), username.encode(), hashlib.sha1).digest()
password = base64.b64encode(dig).decode()

print('username: %s' % username)
print('password: %s' % password)

Here is a web application to test the login to your coturn server. Use turn:host.example.com as the server name.

Burks answered 6/6, 2020 at 14:44 Comment(1)
I had to use bytes(secret, 'utf-8') and bytes(username, 'utf-8') (Python3.9)Thorn
I
4

I came across similar issue (getting REST API working with TURN server) recently and learned that TURN server doesn't support REST API calls at all and just provides support for an authentication format with shared secret when we enable REST API support in TURN config. The draft only provides info on things that we need to consider while implementing such REST API and WE need to create the API on our own or use something like turnhttp to generate the temporary username password combo.

As @mido detailed, you can implement the username/password generation part in the application itself. But if you have reasons to separate this from the application and want to implement it as an entirely different API service, instead of implementing a complete API as per the draft, I came across another post in which the OP provided a PHP script to generate temp username & password and this one works pretty well once you modify the hash_hmac() function to the following,

$turn_password = hash_hmac('sha1', $turn_user, $secret_key, true);

We need to base64 encode the RAW output of hash_hmac to get it working and I believe this is why it was not working for the OP in that link.

You should be able to test authentication using turnutils_uclient command to verify that the temp username/password combo is working as expected.

turnutils_uclient -y -u GENERATED_USERNAME -w GENERATED_PASSWORD yourturnserver.com

Once you have verified authentication and confirmed that it's working, you can setup webserver for the PHP script to make it available to your application and fetch the temporary username/password combo. Also, you would need to implement other security setup (authentication) to protect the API from unauthorized access.

I know this is an old post, just sharing my findings here hoping that it will be useful for someone someday.

Incarnate answered 4/12, 2018 at 11:29 Comment(1)
Thanks for mentioning that we have to use the RAW output, it helped me to get it working. But if people read this, I would recommend to do a real test (e.g. with WebRTC) even if the turnutils_uclient command fails with 0: error 401 (Unauthorized). It my case the command failed but WebRTC worked when I forced Firefox to use a TURN server, maybe you have an idea why? I tested the command on the Linux server where the TURN server is installed.Throckmorton
T
4

Here is my c# implementation with TTL

public string[] GenerateTurnPassword(string username)
{
    long ttl = 3600 * 6;
    var time = DateTimeOffset.Now.ToUnixTimeSeconds() + ttl;
    var newuser = time + ":" + username;
    byte[] key = Encoding.UTF8.GetBytes("YOURSECRET");
    HMACSHA1 hmacsha1 = new HMACSHA1(key);
    byte[] buffer = Encoding.UTF8.GetBytes(newuser);
    MemoryStream stream = new MemoryStream(buffer);
    var hashValue = hmacsha1.ComputeHash(stream);
    string[] arr = new string[2];
    arr[0] = Convert.ToBase64String(hashValue);
    arr[1] = newuser;
    return arr;
}

Trapper answered 2/7, 2020 at 12:9 Comment(0)
L
3

Well @Augusto Destrero provided implementation will cause TypeError: key: expected bytes or bytearray, but got 'str' on Python 3.7.6, for anyone looking for another Python implementation, here is an example:

import time
import hmac
import hashlib
import base64

secret = b'abcdefghijkmln'

def generateTurnUsernamePwd():
    username = "arbitry username here"
    password = hmac.new(secret, bytes(username, 'UTF-8'), hashlib.sha1).digest()
    passwordStr = base64.b64encode(password).decode("utf-8")
    
    return username,passwordStr

print(generateTurnUsernamePwd())

The main difference is key and message keyword arguments in hmac lib has to be bytes in newer version , while in older versions, it requires str.

Lactam answered 25/11, 2020 at 7:1 Comment(0)
A
0

I thought it worthwhile to add to the answer the actual text of the documentation of coturn regardingg this topic and a link to it for those interested:

--auth-secret TURN REST API flag. Flag that sets a special WebRTC authorization option that is based upon authentication secret. The feature purpose is to support "TURN Server REST API" as described in the TURN REST API section below. This option uses timestamp as part of combined username: usercombo -> "timestamp:username", turn user -> usercombo, turn password -> base64(hmac(input_buffer = usercombo, key = shared-secret)). This allows TURN credentials to be accounted for a specific user id. If you don't have a suitable id, the timestamp alone can be used. This option is just turns on secret-based authentication. The actual value of the secret is defined either by option static-auth-secret, or can be found in the turn_secret table in the database.

Amalburga answered 24/7, 2021 at 18:33 Comment(0)
S
0

Here is an example for go with ttl:

import (
    "crypto/hmac"
    "crypto/sha1"
    "encoding/base64"
    "fmt"
    "time"
)

const turnTokenTtl = time.Hour * 24
const turnSecret = "your secret"

func getTurnCredentials(name string) (string, string) {
    timestamp := time.Now().Add(turnTokenTtl).Unix()
    username := fmt.Sprintf("%d:%s", timestamp, name)
    h := hmac.New(sha1.New, []byte(turnSecret))
    h.Write([]byte(username))
    credential := base64.StdEncoding.EncodeToString(h.Sum(nil))
    return username, credential
}
Shandy answered 20/2, 2023 at 17:28 Comment(0)
C
0

Here is the solution I found in bash (simply replace <static-auth-secret> with your own Turn secret) :

turn_secret=<static-auth-secret>
current_timestamp=$(date +%s)
future_timestamp=$((current_timestamp + 86400))
username="${future_timestamp}:foo"
password=$(echo -n "$username" | openssl sha1 -hmac "$turn_secret" -binary | base64)
echo $username
echo $password

You can then use username and password as your credentials.

What it does :

  • Compute a Unix timestamp (date +%s,number of seconds since January 1st 1970)
  • Add 86400 seconds (one day) for end of validity
  • Create username with : timestamp + ":" + foo ("foo" could be replaced by anything !)
  • Compute a HMAC-SHA1 hash of username and Turn secret (openssl sha1...)
  • Convert from binary (ASCII) to base64

You can test your username and password on Trickle Ice

This page from the BigBlueButton project also give hints on how you can test your own Turn server

Collencollenchyma answered 12/5, 2023 at 9:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.