stunner

package module
v0.9.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2022 License: MIT Imports: 16 Imported by: 1

README

STUNner: A Kubernetes ingress gateway for WebRTC

Ever wondered how to deploy your WebRTC infrastructure into the cloud? Frightened away by the complexities of Kubernetes container networking, and the surprising ways in which it may interact with your UDP/RTP media? Tried to read through the endless stream of Stack Overflow questions asking how to scale WebRTC services with Kubernetes, just to get (mostly) insufficient answers? Want to safely connect your users behind a NAT, without relying on expensive third-party TURN services?

Worry no more! STUNner allows you to deploy any WebRTC service into Kubernetes, smoothly integrating it into the cloud-native ecosystem. STUNner exposes a standards-compliant STUN/TURN gateway for clients to access your virtualized WebRTC infrastructure running in Kubernetes, maintaining full browser compatibility and requiring minimal or no modification to your existing WebRTC codebase.

Table of Contents

  1. Description
  2. Features
  3. Getting started
  4. Examples
  5. Documentation
  6. Caveats
  7. Milestones

Description

Currently WebRTC lacks a vitualization story: there is no easy way to deploy a WebRTC backend service into Kubernetes to benefit from the resiliency, scalability, and high availability features we have come to expect from modern network services. Worse yet, the entire industry relies on a handful of public STUN servers and hosted TURN services to connect clients behind a NAT/firewall, which may create a useless dependency on externally operated services, introduce a bottleneck, raise security concerns, and come with a non-trivial price tag.

The main goal of STUNner is to allow anyone to deploy their own WebRTC infrastructure into Kubernetes, without relying on any external service other than the cloud-provider's standard hosted Kubernetes offering. This is achieved by STUNner acting as a gateway for ingesting WebRTC media traffic into the Kubernetes cluster, exposing a public-facing STUN/TURN server that WebRTC clients can connect to.

In the standalone deployment model STUNner acts as a simple scalable STUN/TURN server that WebRTC clients can use as a NAT traversal facility for establishing a media connection. This is not that much different from a standard public STUN/TURN server setup, but in this case the STUN/TURN servers are deployed into Kubernetes, which makes lifecycle management, scaling and cost optimization infinitely simpler.

STUNner standalone deployment architecture

In the fully fledged media-plane deployment model STUNner implements a STUN/TURN ingress gateway service that WebRTC clients can use to open a transport relay connection to the media servers running inside the Kubernetes cluster. This makes it possible to deploy WebRTC application servers and media servers into ordinary Kubernetes pods, taking advantage of Kubernetes's excellent tooling to manage, scale, monitor and troubleshoot the WebRTC infrastructure like any other cloud-bound workload.

STUNner media-plane deployment architecture

Don't worry about the performance implications of processing all your media through a TURN server: STUNner is written in Go so it is extremely fast, it is co-located with your media server pool so you don't pay the round-trip time to a far-away public STUN/TURN server, and STUNner can be easily scaled up if needed, just like any other "normal" Kubernetes service.

Features

Kubernetes has been designed and optimized for the typical HTTP/TCP Web workload, which makes streaming workloads, and especially UDP/RTP based WebRTC media, feel like a foreign citizen. STUNner aims to change this state-of-the-art, by exposing a single public STUN/TURN server port for ingesting all media traffic into a Kubernetes cluster in a controlled and standards-compliant way.

  • Seamless integration with Kubernetes. STUNner can be deployed into any Kubernetes cluster, even into restricted ones like GKE Autopilot, using a single command. Manage your HTTP/HTTPS application servers with your favorite service mesh, and STUNner takes care of all UDP/RTP media.

  • Expose a WebRTC media server on a single external UDP port. Get rid of the Kubernetes hacks, like privileged pods and hostNetwork/hostPort services, typically recommended as a prerequisite to containerizing your WebRTC media plane. Using STUNner a WebRTC deployment needs only two public-facing ports, one HTTPS port for the application server and a single UDP port for all your media.

  • No reliance on external services for NAT traversal. Can't afford a decent hosted TURN service for client-side NAT traversal? Can't get good audio/video quality because the TURN service poses a bottleneck? STUNner can be deployed into the same cluster as the rest of your WebRTC infrastructure, and any WebRTC client can connect to it directly, without the use of any external STUN/TURN service apart from STUNner itself.

  • Easily scale your WebRTC infrastructure. Tired of manually provisioning your WebRTC media servers? STUNner lets you deploy the entire WebRTC infrastructure into ordinary Kubernetes pods, thus scaling the media plane is as easy as issuing a kubectl scale command. STUNner itself can be scaled with similar ease, completely separately from the media servers.

  • Secure perimeter defense. No need to open thousands of UDP/TCP ports on your media server for potentially malicious access; with STUNner all media is received through a single ingress port that you can tightly monitor and control. STUNner stores all STUN/TURN credentials and DTLS keys in secure Kubernetes vaults, and uses standard Kubernetes Access Control Lists (ACLs) to lock down network access between your application servers and the media plane.

  • Simple code and extremely small size. Written in pure Go using the battle-tested pion/webrtc framework, STUNner is just a couple of hundred lines of fully open-source code. The server is extremely lightweight: the typical STUNner container image size is only about 5 Mbytes.

Getting Started

STUNner comes with prefab deployment manifests to fire up a fully functional STUNner-based WebRTC media gateway in minutes. Note that the default deployment does not contain an application server and a media server: STUNner in itself is not a WebRTC backend, it is just an enabler for you to deploy your own WebRTC infrastructure into Kubernetes. Once installed, STUNner makes sure that your media servers are readily reachable to WebRTC clients, despite running with a private IP address inside a Kubernetes pod.

With a minimal understanding of WebRTC and Kubernetes, deploying STUNner should take less than 5 minutes.

Installation

The simplest way to deploy STUNner is through Helm. In this case, all STUNner configuration parameters are available for customization as Helm Values.

$ helm repo add stunner https://l7mp.io/stunner
$ helm repo update
$ helm install stunner stunner/stunner

And that's all: a standalone deployment of STUNner is up and running, waiting for WebRTC clients to connect to it. See the STUNner installation guide on how to customize STUNner or deploy it without Helm, using a static Kubernetes manifest.

Configuration

Wait until Kubernetes assigns a public IP address for STUNner; this should not take more than a minute.

$ until [ -n "$(kubectl get svc stunner -o jsonpath='{.status.loadBalancer.ingress[0].ip}')" ]; do sleep 1; done

Query the actual STUNner configuration in order to learn the public IP address and port assigned by Kubernetes for the STUNner service.

$ kubectl get cm stunner-config -o yaml

The result should be something like the below. The public IP address allocated by Kubernetes for STUNner is marked with a placeholder A.B.C.D below.

apiVersion: v1
kind: ConfigMap
metadata:
  name: stunner-config
  namespace: default
data:
  STUNNER_AUTH_TYPE: plaintext
  STUNNER_PUBLIC_ADDR: A.B.C.D
  STUNNER_PUBLIC_PORT: 3478
  STUNNER_USERNAME: user1
  STUNNER_PASSWORD: passwd1
  ...

Note that any change to the STUNner ConfigMap will take effect only once STUNner is restarted.

$ kubectl rollout restart deployment stunner

Configuring WebRTC clients to reach STUNner

The last step is to configure your WebRTC clients to use STUNner as the TURN server. STUNner is compatible with all client-side TURN auto-discovery mechanisms. When no auto-discovery mechanism is available, clients will need to be manually configured to stream audio/video media over STUNner.

The below JavaScript snippet will direct a WebRTC client to use STUNner; make sure to substitute the placeholders (like <STUNNER_PUBLIC_ADDR>) with the correct configuration from the above.

var ICE_config = {
  'iceServers': [
    {
      'url': "turn:<STUNNER_PUBLIC_ADDR>:<STUNNER_PUBLIC_PORT>?transport=udp',
      'username': <STUNNER_USERNAME>,
      'credential': <STUNNER_PASSWORD>,
    },
  ],
};
var pc = new RTCPeerConnection(ICE_config);

Note that STUNner comes with a small Node.js library that simplifies generating ICE configurations and STUNner credentials in the application server.

Examples

STUNner comes with several demos to show how to use it to deploy a WebRTC application into Kubernetes.

  • Opening a UDP tunnel via STUNner: This introductory demo shows how to tunnel an external connection via STUNner to a UDP service deployed into Kubernetes. The demo can be used to quickly check a STUNner installation.
  • Standalone mode: Direct one to one video call via STUNner: This introductory tutorial showcases the standalone deployment model of STUNner, that is, when WebRTC clients connect to each other directly via STUNner, without a media server. The tutorial has been adopted from the Kurento one-to-one video call tutorial, but this time the clients connect to each other via STUNner, without the assistance of a media server. The demo contains a Node.js application server for creating a browser-based two-party WebRTC video-call, plus a STUNner service that clients use as a TURN server to connect to each other. Note that no transcoding/transsizing option is available in this demo, since there is no media server in the media pipeline.
  • Media-plane mode: One to one video call with Kurento via STUNner: This tutorial extends the previous demo to showcase the fully fledged media-plane deployment model of STUNner, that is, when WebRTC clients connect to each other via a media server deployed into Kubernetes, this time provided by Kurento. The media servers in turn are exposed to the clients via a STUNner gateway. The demo has been adopted from the Kurento one-to-one video call tutorial, with minimal modifications to deploy it into Kubernetes and integrate it with STUNner. The demo contains a Node.js application server for creating a browser-based two-party WebRTC video-call, plus the Kurento media server deployed behind STUNner for media exchange and, potentially, automatic audio/video transcoding.
  • Media-plane mode: Magic mirror via STUNner: This example has been adopted from the Kurento magic mirror demo. The demo shows a basic WebRTC loopback server with some media processing added: the application uses computer vision and augmented reality techniques to add a funny hat on top of faces. The computer vision functionality is again provided by the Kurento media server, being exposed to the clients via a STUNner gateway.

Documentation

See further documentation here.

Caveats

STUNner is a work-in-progress. Some features are missing, others may not work as expected. The notable limitations at this point are as follows.

  • STUNner is not intended to be used as a public STUN/TURN server; the intended use is as a Kubernetes ingress gateway for WebRTC. (For implementing a public TURN service, see alternatives). Being deployed into a Kubernetes service, STUNner will not be able to identify the public IP address of a client sending a STUN binding request to it (without special hacks), and the TURN transport relay connection opened by a WebRTC client via STUNner is reachable only to clients configured to use the same STUNner service (again, without further hacks). This is intended: STUNner is a Kubernetes ingress gateway which happens to expose a STUN/TURN compatible service to WebRTC clients, and not a public TURN service.
  • Access through STUNner to the rest of the cluster must be locked down with a Kubernetes NetworkPolicy. Otherwise, certain internal Kubernetes services would become available externally; see the notes on access control.
  • STUNner supports arbitrary scale-up without dropping active calls, but scale-down might disconnect calls established through the STUNner pods and/or media server replicas being removed from the load-balancing pool. Note that this problem is universal in WebRTC, but we plan to do something about it in a later STUNner release so stay tuned.
  • The WebRTC DataChannel API is not supported at the moment.

Milestones

  • v0.9.2: Day-2 operations: STUNner basic UDP/TURN connectivity + helm chart + simple use cases (Kurento demos).
  • v0.10.1: Onboarding: long-term STUN/TURN credentials and STUN/TURN over TCP/TLS/DTLS.
  • v0.11.1: Day-2 operations: STUNner Kubernetes operator.
  • v0.12.1: Performance: eBPF STUN/TURN acceleration.
  • v0.13.1: Observability: Prometheus + Grafana dashboard.
  • v0.14.1: Ubiquity: make STUNner work with Jitsi, Janus, mediasoup and pion-SFU.
  • v1.0: GA
  • v2.0: Service mesh: adaptive scaling & resiliency

Help

STUNner development is coordinated in Discord, send us an email to ask an invitation.

License

Copyright 2021-2022 by its authors. Some rights reserved. See AUTHORS.

MIT License - see LICENSE for full text.

Acknowledgments

Initial code adopted from pion/stun and pion/turn.

Documentation

Index

Constants

View Source
const ApiVersion string = "v1alpha1"
View Source
const DefaultAuthType = "plaintext"
View Source
const DefaultLogLevel = "all:INFO"
View Source
const DefaultMaxRelayPort int = 1<<16 - 1
View Source
const DefaultMinRelayPort int = 1 << 10
View Source
const DefaultPassword = "passwd1"
View Source
const DefaultPort int = 3478
View Source
const DefaultRealm = "stunner.l7mp.io"
View Source
const DefaultUsername = "user1"
View Source
const UDP_PACKET_SIZE = 1500

Variables

This section is empty.

Functions

func NewLoggerFactory

func NewLoggerFactory(levelSpec string) *logging.DefaultLoggerFactory

NewLoggerFactory sets up a scoped logger for STUNner

Types

type AdminConfig

type AdminConfig struct {
	// Name is the name of the server, optional
	Name string `json:"name",omitempty`
	// LogLevel is the desired log verbosity, e.g.: "stunner:TRACE,all:INFO"
	LogLevel string `json:"logLevel",omitempty`
	// Realm is the STUN/TURN realm
	Realm string `json:"realm",omitempty`
}

AdminConfig holds the administrative configuration

type AuthConfig

type AuthConfig struct {
	// Type is the type of the STUN/TURN authentication mechanism ("plaintext" or "longterm")
	Type string `json:"type",omitempty`
	// Credentials specifies the authententication credentials: for "plaintext" at least the
	// keys "username" and "password" must be set, for "longterm" the key "secret" will hold
	// the shared authentication secret
	Credentials map[string]string `json:"credentials"`
}

Auth defines the specification of the STUN/TURN authentication mechanism used by STUNner

type AuthGen

type AuthGen func() (string, string, error)

type AuthType

type AuthType int

authType

const (
	AuthTypePlainText AuthType = iota + 1
	AuthTypeLongTerm
	AuthTypeUnknown
)

func NewAuthType

func NewAuthType(raw string) (AuthType, error)

func (AuthType) String

func (a AuthType) String() string

type ListenerConfig

type ListenerConfig struct {
	// Name is the name of the listener
	Name string `json:"name",omitempty`
	// Protocol is the transport protocol used by the listener ("UDP", "TCP", "TLS", "DTLS")
	Protocol string `json:"protocol",omitempty`
	// Addr is the IP address for the listener
	Addr string `json:"address",omitempty`
	// Port is the port for the listener
	Port int `json:"port",omitempty`
	// MinRelayPort is the smallest relay port assigned for the relay connections spawned by
	// the listener
	MinRelayPort int `json:"min_relay_port",omitempty`
	// MaxRelayPort is the highest relay port assigned for the relay connections spawned by the
	// listener
	MaxRelayPort int `json:"max_relay_port",omitempty`
	// Cert is the TLS cert
	Cert string `json:"cert",omitempty`
	// Key is the TLS key
	Key string `json:"key",omitempty`
}

ListenerConfig specifies a particular listener for the STUNner deamon

func (ListenerConfig) String

func (l ListenerConfig) String() string

String returns a string ID for the Listener, suitable only for being stored as a key in a map or for logging

type ListenerProtocol

type ListenerProtocol int

listenerProtocol

const (
	ListenerProtocolUdp ListenerProtocol = iota + 1
	ListenerProtocolTcp
	ListenerProtocolTls
	ListenerProtocolDtls
	ListenerProtocolUnknown
)

func NewListenerProtocol

func NewListenerProtocol(raw string) (ListenerProtocol, error)

func (ListenerProtocol) String

func (l ListenerProtocol) String() string

type StaticResourceConfig

type StaticResourceConfig struct {
	// Auth defines the specification of the STUN/TURN authentication mechanism used by STUNner
	Auth AuthConfig `json:"auth"`
	// Listeners defines the listeners for the STUNner deamon
	Listeners []ListenerConfig `json:"listeners",omitempty`
}

StaticResourceConfig defines the static resources for the daemon

type Stunner

type Stunner struct {
	// contains filtered or unexported fields
}

Stunner is an instance of the STUNner deamon

func NewStunner

func NewStunner(conf *StunnerConfig) (*Stunner, error)

NewStunner creates the STUNner deamon using the specified configuration

func (*Stunner) Close

func (s *Stunner) Close()

Close stops the STUNner daemon. It cleans up any associated state and closes all connections it is managing

func (*Stunner) GetConfig

func (s *Stunner) GetConfig() StunnerConfig

func (*Stunner) GetServer

func (s *Stunner) GetServer() *turn.Server

GetServer returns the TURN server instance running the STUNner daemon

func (*Stunner) Reconcile

func (s *Stunner) Reconcile(newConfig *StunnerConfig) error

Reconcile handles the updates to the STUNner configuration. At the moment, all updates are destructive: the server is closed and restarted with the new configuration

type StunnerConfig

type StunnerConfig struct {
	// ApiVersion is the version of the STUNner API implemented
	ApiVersion string `json:"version"`
	// AdminConfig holds the administrative configuration
	Admin AdminConfig `json:"admin",omitempty`
	// StaticResourceConfig defines the static resources for the daemon
	Static StaticResourceConfig `json:"static"`
	// Net is used for testing
	Net *vnet.Net
}

StunnerConfig configures the STUnner daemon

func NewDefaultStunnerConfig

func NewDefaultStunnerConfig(uri, logLevel string) (*StunnerConfig, error)

NewDefaultStunnerConfig builds a default configuration from a STUNner URI. Example: the URI `turn://user:pass@127.0.0.1:3478` will be parsed into a STUNner configuration with a server running on the localhost at port 3478, with plain-text authentication using the username/password pair `user:pass`.

type StunnerUri

type StunnerUri struct {
	Protocol, Address, Username, Password string
	Port                                  int
	Addr                                  net.Addr
}

StunnerUri is the specification of a STUNner listener URI

func ParseUri

func ParseUri(uri string) (*StunnerUri, error)

ParseUri parses a STUN/TURN server URI, e.g., "turn://user1:passwd1@127.0.0.1:3478?transport=udp"

type Turncat

type Turncat struct {
	// contains filtered or unexported fields
}

Turncat is the internal structure for representing a turncat relay

func NewTurncat

func NewTurncat(config *TurncatConfig) (*Turncat, error)

NewTurncat creates a new turncat relay from the specified config, creating a listener socket for clients to connect and relaying client connections through the speficied STUN/TURN server to the peer.

func (*Turncat) Close

func (t *Turncat) Close()

Close terminates all relay connections created via turncat and deletes it. Errors in this phase are not critical and not propagated back to the caller.

type TurncatConfig

type TurncatConfig struct {
	// ListenAddr is the listeninging socket address (local tunnel endpoint)
	ListenerAddr string
	// ServerAddr is the TURN server addrees (e.g. "turn:turn.abc.com:3478")
	ServerAddr string
	// PeerAddr specifies the remote peer to connect to
	PeerAddr string
	// Realm is the STUN/TURN realm
	Realm string
	// AuthGet specifies the function to generate auth tokens
	AuthGen       AuthGen
	LoggerFactory logging.LoggerFactory
}

TurncatConfig is the main configuration for the turncat relay

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL