1
0
Fork 0
mirror of https://gitlab.com/famedly/conduit.git synced 2025-06-27 16:35:59 +00:00

Merge branch 'turn-config' into 'next'

feat(turn): move config to table & error when no config is set

See merge request famedly/conduit!733
This commit is contained in:
Matthias Ahouansou 2025-02-04 15:43:14 +00:00
commit 7963a9d23f
4 changed files with 106 additions and 89 deletions

View file

@ -7,17 +7,18 @@
## Edit/Add a few settings to your existing conduit.toml ## Edit/Add a few settings to your existing conduit.toml
``` ```
[turn]
# Refer to your Coturn settings. # Refer to your Coturn settings.
# `your.turn.url` has to match the REALM setting of your Coturn as well as `transport`. # `your.turn.url` has to match the REALM setting of your Coturn as well as `transport`.
turn_uris = ["turn:your.turn.url?transport=udp", "turn:your.turn.url?transport=tcp"] uris = ["turn:your.turn.url?transport=udp", "turn:your.turn.url?transport=tcp"]
# static-auth-secret of your turnserver # static-auth-secret of your turnserver
turn_secret = "ADD SECRET HERE" secret = "ADD SECRET HERE"
# If you have your TURN server configured to use a username and password # If you have your TURN server configured to use a username and password
# you can provide these information too. In this case comment out `turn_secret above`! # you can provide these information too. In this case comment out `turn_secret above`!
#turn_username = "" #username = ""
#turn_password = "" #password = ""
``` ```
## Apply settings ## Apply settings

View file

@ -1,7 +1,10 @@
use crate::{services, Result, Ruma}; use crate::{config::TurnAuth, services, Error, Result, Ruma};
use base64::{engine::general_purpose, Engine as _}; use base64::{engine::general_purpose, Engine as _};
use hmac::{Hmac, Mac}; use hmac::{Hmac, Mac};
use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch}; use ruma::{
api::client::{error::ErrorKind, voip::get_turn_server_info},
SecondsSinceUnixEpoch,
};
use sha1::Sha1; use sha1::Sha1;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
@ -9,40 +12,41 @@ type HmacSha1 = Hmac<Sha1>;
/// # `GET /_matrix/client/r0/voip/turnServer` /// # `GET /_matrix/client/r0/voip/turnServer`
/// ///
/// TODO: Returns information about the recommended turn server. /// Returns information about the recommended turn server.
pub async fn turn_server_route( pub async fn turn_server_route(
body: Ruma<get_turn_server_info::v3::Request>, body: Ruma<get_turn_server_info::v3::Request>,
) -> Result<get_turn_server_info::v3::Response> { ) -> Result<get_turn_server_info::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let turn_secret = services().globals.turn_secret().clone(); if let Some(turn) = services().globals.turn() {
let (username, password) = match turn.auth {
TurnAuth::Secret { secret } => {
let expiry = SecondsSinceUnixEpoch::from_system_time(
SystemTime::now() + Duration::from_secs(turn.ttl),
)
.expect("time is valid");
let (username, password) = if !turn_secret.is_empty() { let username: String = format!("{}:{}", expiry.get(), sender_user);
let expiry = SecondsSinceUnixEpoch::from_system_time(
SystemTime::now() + Duration::from_secs(services().globals.turn_ttl()),
)
.expect("time is valid");
let username: String = format!("{}:{}", expiry.get(), sender_user); let mut mac = HmacSha1::new_from_slice(secret.as_bytes())
.expect("HMAC can take key of any size");
mac.update(username.as_bytes());
let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes()) let password: String =
.expect("HMAC can take key of any size"); general_purpose::STANDARD.encode(mac.finalize().into_bytes());
mac.update(username.as_bytes());
let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes()); (username, password)
}
TurnAuth::UserPass { username, password } => (username, password),
};
(username, password) Ok(get_turn_server_info::v3::Response {
username,
password,
uris: turn.uris,
ttl: Duration::from_secs(turn.ttl),
})
} else { } else {
( Err(Error::BadRequest(ErrorKind::NotFound, "No TURN config set"))
services().globals.turn_username().clone(), }
services().globals.turn_password().clone(),
)
};
Ok(get_turn_server_info::v3::Response {
username,
password,
uris: services().globals.turn_uris().to_vec(),
ttl: Duration::from_secs(services().globals.turn_ttl()),
})
} }

View file

@ -72,17 +72,15 @@ pub struct Config {
pub trusted_servers: Vec<OwnedServerName>, pub trusted_servers: Vec<OwnedServerName>,
#[serde(default = "default_log")] #[serde(default = "default_log")]
pub log: String, pub log: String,
#[serde(default)] pub turn_username: Option<String>,
pub turn_username: String, pub turn_password: Option<String>,
#[serde(default)] pub turn_uris: Option<Vec<String>>,
pub turn_password: String, pub turn_secret: Option<String>,
#[serde(default = "Vec::new")]
pub turn_uris: Vec<String>,
#[serde(default)]
pub turn_secret: String,
#[serde(default = "default_turn_ttl")] #[serde(default = "default_turn_ttl")]
pub turn_ttl: u64, pub turn_ttl: u64,
pub turn: Option<TurnConfig>,
pub emergency_password: Option<String>, pub emergency_password: Option<String>,
#[serde(flatten)] #[serde(flatten)]
@ -95,13 +93,36 @@ pub struct TlsConfig {
pub key: String, pub key: String,
} }
#[derive(Clone, Debug, Deserialize)]
pub struct TurnConfig {
pub uris: Vec<String>,
#[serde(default = "default_turn_ttl")]
pub ttl: u64,
#[serde(flatten)]
pub auth: TurnAuth,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum TurnAuth {
UserPass { username: String, password: String },
Secret { secret: String },
}
#[derive(Clone, Debug, Deserialize, Default)] #[derive(Clone, Debug, Deserialize, Default)]
pub struct WellKnownConfig { pub struct WellKnownConfig {
pub client: Option<Url>, pub client: Option<Url>,
pub server: Option<OwnedServerName>, pub server: Option<OwnedServerName>,
} }
const DEPRECATED_KEYS: &[&str] = &["cache_capacity"]; const DEPRECATED_KEYS: &[&str] = &[
"cache_capacity",
"turn_username",
"turn_password",
"turn_uris",
"turn_secret",
"turn_ttl",
];
impl Config { impl Config {
pub fn warn_deprecated(&self) { pub fn warn_deprecated(&self) {
@ -144,6 +165,32 @@ impl Config {
} }
} }
} }
pub fn turn(&self) -> Option<TurnConfig> {
if self.turn.is_some() {
self.turn.clone()
} else if let Some(uris) = self.turn_uris.clone() {
if let Some(secret) = self.turn_secret.clone() {
Some(TurnConfig {
uris,
ttl: self.turn_ttl,
auth: TurnAuth::Secret { secret },
})
} else if let (Some(username), Some(password)) =
(self.turn_username.clone(), self.turn_password.clone())
{
Some(TurnConfig {
uris,
ttl: self.turn_ttl,
auth: TurnAuth::UserPass { username, password },
})
} else {
None
}
} else {
None
}
}
} }
impl fmt::Display for Config { impl fmt::Display for Config {
@ -199,37 +246,18 @@ impl fmt::Display for Config {
} }
&lst.join(", ") &lst.join(", ")
}), }),
( ("TURN URIs", {
"TURN username", if let Some(turn) = self.turn() {
if self.turn_username.is_empty() { let mut lst = vec![];
"not set" for item in turn.uris.iter().cloned().enumerate() {
let (_, uri): (usize, String) = item;
lst.push(uri);
}
&lst.join(", ")
} else { } else {
&self.turn_username "unset"
},
),
("TURN password", {
if self.turn_password.is_empty() {
"not set"
} else {
"set"
} }
}), }),
("TURN secret", {
if self.turn_secret.is_empty() {
"not set"
} else {
"set"
}
}),
("Turn TTL", &self.turn_ttl.to_string()),
("Turn URIs", {
let mut lst = vec![];
for item in self.turn_uris.iter().cloned().enumerate() {
let (_, uri): (usize, String) = item;
lst.push(uri);
}
&lst.join(", ")
}),
("Well-known server name", well_known_server.as_str()), ("Well-known server name", well_known_server.as_str()),
("Well-known client URL", &self.well_known_client()), ("Well-known client URL", &self.well_known_client()),
]; ];

View file

@ -7,7 +7,7 @@ use ruma::{
use crate::api::server_server::DestinationResponse; use crate::api::server_server::DestinationResponse;
use crate::{services, Config, Error, Result}; use crate::{config::TurnConfig, services, Config, Error, Result};
use futures_util::FutureExt; use futures_util::FutureExt;
use hickory_resolver::TokioAsyncResolver; use hickory_resolver::TokioAsyncResolver;
use hyper_util::client::legacy::connect::dns::{GaiResolver, Name as HyperName}; use hyper_util::client::legacy::connect::dns::{GaiResolver, Name as HyperName};
@ -348,6 +348,10 @@ impl Service {
&self.config.trusted_servers &self.config.trusted_servers
} }
pub fn turn(&self) -> Option<TurnConfig> {
self.config.turn()
}
pub fn dns_resolver(&self) -> &TokioAsyncResolver { pub fn dns_resolver(&self) -> &TokioAsyncResolver {
&self.dns_resolver &self.dns_resolver
} }
@ -356,26 +360,6 @@ impl Service {
self.jwt_decoding_key.as_ref() self.jwt_decoding_key.as_ref()
} }
pub fn turn_password(&self) -> &String {
&self.config.turn_password
}
pub fn turn_ttl(&self) -> u64 {
self.config.turn_ttl
}
pub fn turn_uris(&self) -> &[String] {
&self.config.turn_uris
}
pub fn turn_username(&self) -> &String {
&self.config.turn_username
}
pub fn turn_secret(&self) -> &String {
&self.config.turn_secret
}
pub fn emergency_password(&self) -> &Option<String> { pub fn emergency_password(&self) -> &Option<String> {
&self.config.emergency_password &self.config.emergency_password
} }