stunner

package module
v0.10.6 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2022 License: MIT Imports: 28 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: Observability: Prometheus + Grafana dashboard.
  • v0.13.1: Ubiquity: make STUNner work with Jitsi, Janus, mediasoup, pion-SFU and livekit.
  • v1.0: GA
  • v1.1: Performance: eBPF STUN/TURN acceleration.
  • 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

Overview

Package stunner contains the public API for l7mp/stunner, a Kubernetes ingress gateway for WebRTC

Index

Constants

View Source
const DefaultLogLevel = "all:WARN"
View Source
const UDP_PACKET_SIZE = 1500

Variables

This section is empty.

Functions

func LoadConfig added in v0.10.0

func LoadConfig(config string) (*v1alpha1.StunnerConfig, error)

LoadConfig loads a configuration from a file, substituting environment variables for placeholders in the configuration file. Returns the new configuration or error if load fails

func NewDefaultConfig added in v0.10.0

func NewDefaultConfig(uri string) (*v1alpha1.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`.

Types

type AuthGen

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

AuthGen is a function called by turncat to generate authentication tokens

type Options added in v0.10.5

type Options struct {
	// DryRun will suppress sideeffects: it will not initialize listener sockets and it will
	// not bring up the TURN server. This is mostly for testing, default is false
	DryRun bool
	// SuppressRollback controls whether to rollback the last working configuration after a
	// failed reconciliation request. Default is false, which means to always rollback
	SuppressRollback bool
	// LogLevel specifies the required loglevel for STUNner and each of its sub-objects, e.g.,
	// "all:TRACE" will force maximal loglevel throughout the daemon will
	// "all:ERROR,auth:TRACE,turn:DEBUG" will suppress all logs except in the authentication
	// subsystem and the TURN protocol logic
	LogLevel string
	// Resolver swaps the internal DNS resolver with a custom implementation (used mostly for
	// testing)
	Resolver resolver.DnsResolver
	// VNet will switch STUNner into testing mode, using a vnet.Net instance to run STUNner
	// over an emulated data-plane
	Net *vnet.Net
}

Options defines various options to define the way STUNner will run

type Stunner

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

Stunner is an instance of the STUNner deamon

func NewStunner

func NewStunner() *Stunner

NewStunner creates a new empty STUNner deamon. Call Reconcle to reconcile the daemon for the given configuration

func (*Stunner) Close

func (s *Stunner) Close()

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

func (*Stunner) GetAdmin added in v0.10.0

func (s *Stunner) GetAdmin() *object.Admin

GetAdmin returns the adminisittive information for STUNner

func (*Stunner) GetAuth added in v0.10.0

func (s *Stunner) GetAuth() *object.Auth

GetAdmin returns the STUNner authenitator

func (*Stunner) GetCluster added in v0.10.0

func (s *Stunner) GetCluster(name string) *object.Cluster

GetCluster returns a STUNner cluster or nil of no cluster with the given name found

func (*Stunner) GetConfig

func (s *Stunner) GetConfig() *v1alpha1.StunnerConfig

GetConfig returns the configuration of the running STUNner daemon

func (*Stunner) GetListener added in v0.10.0

func (s *Stunner) GetListener(name string) *object.Listener

GetListener returns a STUNner listener or nil of no listener with the given name found

func (*Stunner) GetLogger added in v0.10.5

func (s *Stunner) GetLogger() logging.LoggerFactory

GetLogger returns the logger factory of the running daemon, useful for creating a sub-logger

func (*Stunner) GetServer

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

GetServer returns the TURN server instance running the STUNner daemon

func (*Stunner) GetVersion added in v0.10.0

func (s *Stunner) GetVersion() string

GetVersion returns the STUNner API version

func (*Stunner) NewAuthHandler added in v0.10.0

func (s *Stunner) NewAuthHandler() turn.AuthHandler

NewAuthHandler returns an authentication handler callback for STUNner, suitable to be used with the TURN server for authenticating clients

func (*Stunner) NewPermissionHandler added in v0.10.0

func (s *Stunner) NewPermissionHandler(l *object.Listener) turn.PermissionHandler

NewPermissionHandler returns a callback for STUNner to handle client permsission requests to access peers

func (*Stunner) Reconcile

func (s *Stunner) Reconcile(req v1alpha1.StunnerConfig) error

Reconcile handles the updates to the STUNner configuration. Some updates are destructive so the server must be closed and restarted with the new configuration manually (see the documentation of the corresponding STUNner objects for when STUNner may restart after a reconciliation). Reconcile returns nil if is server restart was not requred, v1alpha1.ErrRestartRequired to indicate that it performed a full shutdown-restart cycle to reconcile the new config (unless DryRun is on), and an error if an error happened during reconciliation, in which case it will rollback the last working configuration (unless SuppressRollback is on)

func (*Stunner) Start added in v0.10.0

func (s *Stunner) Start() error

Start starts the STUNner server and starts listining on all requested server sockets

func (*Stunner) Stop added in v0.10.5

func (s *Stunner) Stop()

Close stops the TURN server underneath STUNner

func (*Stunner) String added in v0.10.5

func (s *Stunner) String() string

String returns a short description of the running STUNner instance

func (*Stunner) WithOptions added in v0.10.5

func (s *Stunner) WithOptions(options Options) *Stunner

WithOptions will take into effect the options passed in

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
internal
pkg
apis/v1alpha1
Package v1alpha1 is the v1alpha1 version of the STUNner API.
Package v1alpha1 is the v1alpha1 version of the STUNner API.

Jump to

Keyboard shortcuts

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