turn server tls (#38)

* tls

* acm

* use cert/key files

* turn -> turns

* turn cert config

* updates

* move panic

* tidy

* final updates
This commit is contained in:
David Colburn
2021-07-01 17:00:49 -05:00
committed by GitHub
parent 4e6eafa63c
commit e543aaa98a
7 changed files with 74 additions and 57 deletions

View File

@@ -41,8 +41,7 @@ func printPorts(c *cli.Context) error {
if conf.TURN.Enabled {
udpPorts = append(udpPorts, fmt.Sprintf("%d-%d", conf.TURN.PortRangeStart, conf.TURN.PortRangeEnd))
udpPorts = append(udpPorts, strconv.Itoa(conf.TURN.TCPPort))
tcpPorts = append(tcpPorts, strconv.Itoa(conf.TURN.TCPPort))
tcpPorts = append(tcpPorts, strconv.Itoa(conf.TURN.TLSPort))
}
fmt.Println("TCP Ports")

View File

@@ -75,6 +75,16 @@ func main() {
Name: "dev",
Usage: "sets log-level to debug, and console formatter",
},
&cli.StringFlag{
Name: "turn-cert",
Usage: "tls cert file for TURN server",
EnvVars: []string{"LIVEKIT_TURN_CERT"},
},
&cli.StringFlag{
Name: "turn-key",
Usage: "tls key file for TURN server",
EnvVars: []string{"LIVEKIT_TURN_KEY"},
},
},
Action: startServer,
Commands: []*cli.Command{

View File

@@ -65,3 +65,19 @@ keys:
# update_interval: 500
# # to prevent speaker updates from too jumpy, smooth out values over N samples
# smooth_samples: 5
# turn server
turn:
# Uses TLS. Requires cert and key pem files by either:
# - using turn.secretName if deploying with our helm chart, or
# - setting LIVEKIT_TURN_CERT and LIVEKIT_TURN_KEY env vars with file locations, or
# - using cert_file and key_file below
# defaults to false
enabled: false
# needs to match tls cert domain
domain: turn.myhost.com
# defaults to 3478 - if not using a load balancer, this must be set to 443
tls_port: 3478
# optional
# cert_file: /path/to/cert.pem
# key_file: /path/to/key.pem

View File

@@ -71,8 +71,10 @@ type RedisConfig struct {
type TURNConfig struct {
Enabled bool `yaml:"enabled"`
TCPPort int `yaml:"tcp_port"`
UDPPort int `yaml:"udp_port"`
Domain string `yaml:"domain"`
CertFile string `yaml:"cert_file"`
KeyFile string `yaml:"key_file"`
TLSPort int `yaml:"tls_port"`
PortRangeStart uint16 `yaml:"port_range_start"`
PortRangeEnd uint16 `yaml:"port_range_end"`
}
@@ -108,8 +110,7 @@ func NewConfig(confString string) (*Config, error) {
Redis: RedisConfig{},
TURN: TURNConfig{
Enabled: false,
TCPPort: 3478,
UDPPort: 3478,
TLSPort: 3478,
PortRangeStart: 12000,
PortRangeEnd: 14000,
},
@@ -143,6 +144,12 @@ func (conf *Config) UpdateFromCLI(c *cli.Context) error {
if c.IsSet("redis-password") {
conf.Redis.Password = c.String("redis-password")
}
if c.IsSet("turn-cert") {
conf.TURN.CertFile = c.String("turn-cert")
}
if c.IsSet("turn-key") {
conf.TURN.KeyFile = c.String("turn-key")
}
// expand env vars in filenames
file, err := homedir.Expand(os.ExpandEnv(conf.KeyFile))
if err != nil {

View File

@@ -480,21 +480,11 @@ func (r *RoomManager) iceServersForRoom(ri *livekit.Room) []*livekit.ICEServer {
})
}
if r.config.TURN.Enabled {
if r.config.TURN.TCPPort > 0 {
iceServers = append(iceServers, &livekit.ICEServer{
Urls: []string{fmt.Sprintf("turn:%s:%d?transport=tcp", r.currentNode.Ip, r.config.TURN.TCPPort)},
Username: ri.Name,
Credential: ri.TurnPassword,
})
}
if r.config.TURN.UDPPort > 0 {
iceServers = append(iceServers, &livekit.ICEServer{
Urls: []string{fmt.Sprintf("turn:%s:%d?transport=udp", r.currentNode.Ip, r.config.TURN.UDPPort)},
Username: ri.Name,
Credential: ri.TurnPassword,
})
}
iceServers = append(iceServers, &livekit.ICEServer{
Urls: []string{fmt.Sprintf("turns:%s:443?transport=tcp", r.config.TURN.Domain)},
Username: ri.Name,
Credential: ri.TurnPassword,
})
}
return iceServers
}

View File

@@ -1,6 +1,7 @@
package service
import (
"crypto/tls"
"fmt"
"net"
"strconv"
@@ -23,19 +24,32 @@ func NewTurnServer(conf *config.Config, roomStore RoomStore, node routing.LocalN
if !turnConf.Enabled {
return nil, nil
}
if turnConf.Domain == "" {
return nil, errors.New("TURN domain required")
}
if turnConf.TLSPort == 0 {
return nil, errors.New("invalid TURN tcp port")
}
cert, err := tls.LoadX509KeyPair(turnConf.CertFile, turnConf.KeyFile)
if err != nil {
return nil, errors.Wrap(err, "TURN tls cert required")
}
tlsListener, err := tls.Listen("tcp4", "0.0.0.0:"+strconv.Itoa(turnConf.TLSPort), &tls.Config{
MinVersion: tls.VersionTLS12,
Certificates: []tls.Certificate{cert},
})
if err != nil {
return nil, errors.Wrap(err, "could not listen on TURN TCP port")
}
serverConfig := turn.ServerConfig{
Realm: livekitRealm,
AuthHandler: newTurnAuthHandler(roomStore),
}
if turnConf.TCPPort > 0 {
tcpListener, err := net.Listen("tcp4", "0.0.0.0:"+strconv.Itoa(turnConf.TCPPort))
if err != nil {
return nil, errors.Wrap(err, "could not listen on TURN TCP port")
}
serverConfig.ListenerConfigs = []turn.ListenerConfig{
ListenerConfigs: []turn.ListenerConfig{
{
Listener: tcpListener,
Listener: tlsListener,
RelayAddressGenerator: &turn.RelayAddressGeneratorPortRange{
RelayAddress: net.ParseIP(node.Ip),
Address: "0.0.0.0",
@@ -44,31 +58,11 @@ func NewTurnServer(conf *config.Config, roomStore RoomStore, node routing.LocalN
MaxRetries: allocateRetries,
},
},
}
}
if turnConf.UDPPort > 0 {
udpListener, err := net.ListenPacket("udp4", "0.0.0.0:"+strconv.Itoa(turnConf.UDPPort))
if err != nil {
return nil, errors.Wrap(err, "could not listen on TURN UDP port")
}
serverConfig.PacketConnConfigs = []turn.PacketConnConfig{
{
PacketConn: udpListener,
RelayAddressGenerator: &turn.RelayAddressGeneratorPortRange{
RelayAddress: net.ParseIP(node.Ip), // Claim that we are listening on IP passed by user (This should be your Public IP)
Address: "0.0.0.0", // But actually be listening on every interface
MinPort: turnConf.PortRangeStart,
MaxPort: turnConf.PortRangeEnd,
MaxRetries: allocateRetries,
},
},
}
},
}
logger.Infow("Starting TURN server",
"TCP port", turnConf.TCPPort,
"UDP port", turnConf.UDPPort,
"TCP port", turnConf.TLSPort,
"portRange", fmt.Sprintf("%d-%d", turnConf.PortRangeStart, turnConf.PortRangeEnd))
return turn.NewServer(serverConfig)
}

View File

@@ -9,14 +9,15 @@ import (
"github.com/pion/turn/v2"
"github.com/stretchr/testify/require"
"github.com/livekit/protocol/utils"
"github.com/livekit/livekit-server/pkg/config"
"github.com/livekit/livekit-server/pkg/routing"
"github.com/livekit/livekit-server/pkg/service"
livekit "github.com/livekit/livekit-server/proto"
"github.com/livekit/protocol/utils"
)
func TestTurnServer(t *testing.T) {
func testTurnServer(t *testing.T) {
conf, err := config.NewConfig("")
require.NoError(t, err)
@@ -47,15 +48,15 @@ func TestTurnServer(t *testing.T) {
require.NoError(t, roomStore.CreateRoom(rm))
turnConf := &turn.ClientConfig{
STUNServerAddr: fmt.Sprintf("localhost:%d", conf.TURN.UDPPort),
TURNServerAddr: fmt.Sprintf("%s:%d", currentNode.Ip, conf.TURN.UDPPort),
STUNServerAddr: fmt.Sprintf("localhost:%d", conf.TURN.TLSPort),
TURNServerAddr: fmt.Sprintf("%s:%d", currentNode.Ip, conf.TURN.TLSPort),
Username: rm.Name,
Password: rm.TurnPassword,
Realm: "livekit",
}
t.Run("TURN works over TCP", func(t *testing.T) {
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", conf.TURN.TCPPort))
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", conf.TURN.TLSPort))
require.NoError(t, err)
tc := *turnConf