Skip to content

Commit

Permalink
doc: Update docs as per the upcoming v0.16.0 release
Browse files Browse the repository at this point in the history
  • Loading branch information
rg0now committed Sep 25, 2023
1 parent 48e59a8 commit 06bd535
Show file tree
Hide file tree
Showing 16 changed files with 151 additions and 162 deletions.
72 changes: 35 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
Ever wondered how to [deploy your WebRTC infrastructure into the
cloud](https://webrtchacks.com/webrtc-media-servers-in-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
with your UDP/RTP media? Read through the endless stream of [Stack
Overflow](https://stackoverflow.com/search?q=kubernetes+webrtc)
[questions](https://stackoverflow.com/questions/61140228/kubernetes-loadbalancer-open-a-wide-range-thousands-of-port)
[asking](https://stackoverflow.com/questions/64232853/how-to-use-webrtc-with-rtcpeerconnection-on-kubernetes)
Expand Down Expand Up @@ -88,15 +88,12 @@ 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.

STUNner can act as a STUN/TURN server that WebRTC clients and media servers can use as a scalable
NAT traversal facility (headless model), or it can serve as a fully-fledged ingress gateway for
clients to reach a media server deployed behind STUNner (media-plane model). 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
Kubernetes offering. STUNner can act as a standalone STUN/TURN server that WebRTC clients and media
servers can use as a scalable NAT traversal facility (headless model), or it can act as a gateway
for ingesting WebRTC media traffic into the Kubernetes cluster by exposing a public-facing
STUN/TURN server that WebRTC clients can connect to (media-plane model). 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](./docs/img/stunner_arch.svg)
Expand Down Expand Up @@ -130,7 +127,7 @@ way.
[hacks](https://kubernetes.io/docs/concepts/configuration/overview), 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.
HTTPS port for signaling and a *single* UDP port for *all* your media.

* **No reliance on external services for NAT traversal.** Can't afford a [hosted TURN
service](https://bloggeek.me/webrtc-turn) for client-side NAT traversal? Can't get decent
Expand All @@ -142,7 +139,7 @@ way.
* **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](docs/SCALING.md) is as easy as issuing a `kubectl scale`
command. Even better, use the built in Kubernetes horizontal autoscaler to *automatically* resize
command. Or you can use the built in Kubernetes horizontal autoscaler to *automatically* resize
your workload based on demand.

* **Secure perimeter defense.** No need to open thousands of UDP/TCP ports on your media server for
Expand All @@ -155,7 +152,7 @@ way.
* **Simple code and extremely small size.** Written in pure Go using the battle-tested
[pion/webrtc](https://github.com/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.
container image size is only 15 Mbytes.

## Getting Started

Expand Down Expand Up @@ -196,16 +193,14 @@ Find out more about the charts in the [STUNner-helm repository](https://github.c
### Configuration

The standard way to interact with STUNner is via the standard Kubernetes [Gateway
API](https://gateway-api.sigs.k8s.io) version
[v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec). This is much akin to the
way you configure *all* Kubernetes workloads: specify your intents in YAML files and issue a
`kubectl apply`, and the [STUNner gateway
operator](https://github.com/l7mp/stunner-gateway-operator) will automatically reconcile the
STUNner dataplane for the new configuration.
API](https://gateway-api.sigs.k8s.io). This is much akin to the way you configure *all*
Kubernetes workloads: specify your intents in YAML files and issue a `kubectl apply`, and the
[STUNner gateway operator](https://github.com/l7mp/stunner-gateway-operator) will automatically
reconcile the STUNner dataplane for the new configuration.

1. Given a fresh STUNner install, the first step is to register STUNner with the Kubernetes Gateway
API. This amounts to creating a
[GatewayClass](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass),
[GatewayClass](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.GatewayClass),
which serves as the [root level configuration](/docs/GATEWAY.md#gatewayclass) for your STUNner
deployment.

Expand All @@ -217,7 +212,7 @@ The standard way to interact with STUNner is via the standard Kubernetes [Gatewa

``` console
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1alpha2
apiVersion: gateway.networking.k8s.io/v1beta1
kind: GatewayClass
metadata:
name: stunner-gatewayclass
Expand Down Expand Up @@ -278,7 +273,7 @@ The standard way to interact with STUNner is via the standard Kubernetes [Gatewa

1. At this point, we are ready to [expose STUNner](/docs/GATEWAY.md#gateway) to clients! This occurs
by loading a
[Gateway](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.Gateway)
[Gateway](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway)
resource into Kubernetes.

In the below example, we open a STUN/TURN listener service on the UDP listener port 3478.
Expand All @@ -293,7 +288,7 @@ The standard way to interact with STUNner is via the standard Kubernetes [Gatewa

```console
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1alpha2
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: udp-gateway
Expand All @@ -303,16 +298,18 @@ The standard way to interact with STUNner is via the standard Kubernetes [Gatewa
listeners:
- name: udp-listener
port: 3478
protocol: UDP
protocol: TURN-UDP
EOF
```

1. The final step is to tell STUNner what to do with the client connections received on the
Gateway. This occurs by attaching a
[UDPRoute](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute)
resource to the Gateway and specifying the target service in the `backendRef`. A UDPRoute can be
attached to any Gateway by setting the `parentRef` to the Gateway's name, there is just one
rule: the Gateway and the UDPRoute must both live in the same Kubernetes namespace.
resource to the Gateway by setting the `parentRef` to the Gateway's name and specifying the
target service in the `backendRef`. By default attaching an UDPRoute to a Gateway will succeed
only if both live in the same namespace; see
[here](https://gateway-api.sigs.k8s.io/guides/multiple-ns) for more info on cross-namespace
routing.

The below UDPRoute will configure STUNner to [route client
connections](/docs/GATEWAY.md#udproute) received on the Gateway called `udp-gateway` to the
Expand Down Expand Up @@ -346,7 +343,7 @@ this, STUNner **ignores the protocol and port specified in the backend service**
connections to the backend pods via *any* protocol-port pair. STUNner can therefore use only a
*single* backend Service to reach any port exposed on a WebRTC media server.

> Considering the above example: even if the `default/media-plane` Service was created for the TCP:80 port, STUNner will allow connections via any protocol-port pair, say, via UDP:10000 or any other UDP port for that matter. This hack remains our only viable way to support WebRTC workloads in Kubernetes until [support for port ranges is implemented in Kubernetes services](https://github.com/kubernetes/kubernetes/issues/23864). Note that this affects only the *internal* backend services: STUNner is still exposed *externally* via a *single* protocol-port, but it can demultiplex incoming client media connections to any *internal* backend ports via a single UDPRoute.
<!-- > Considering the above example: even if the `default/media-plane` Service was created for the TCP:80 port, STUNner will allow connections via any protocol-port pair, say, via UDP:10000 or any other UDP port for that matter. This hack remains our only viable way to support WebRTC workloads in Kubernetes until [support for port ranges is implemented in Kubernetes services](https://github.com/kubernetes/kubernetes/issues/23864). Note that this affects only the *internal* backend services: STUNner is still exposed *externally* via a *single* protocol-port, but it can demultiplex incoming client media connections to any *internal* backend ports via a single UDPRoute. -->

And that's all. You don't need to worry about client-side NAT traversal and WebRTC media routing
because STUNner has you covered! Even better, every time you change a Gateway API resource in
Expand All @@ -372,7 +369,7 @@ STUN/TURN authentication type: plaintext
STUN/TURN username: user-1
STUN/TURN password: pass-1
Listener: udp-listener
Protocol: UDP
Protocol: TURN-UDP
Public address: 34.118.36.108
Public port: 3478
```
Expand All @@ -395,10 +392,9 @@ a heartwarming welcome message.
1. Fire up the UDP greeter service.

The below manifest spawns the service in the `default` namespace and wraps it in a Kubernetes
service called `media-plane`. Recall, this is the target service STUNner will route connections
to. Note that the type of the `media-plane` service is `ClusterIP`, which means that Kubernetes
will *not* expose it to the Internet: the only way for clients to obtain a response is via
STUNner.
service called `media-plane`. Recall, this is the target service in our UDPRoute. Note that the
type of the `media-plane` service is `ClusterIP`, which means that Kubernetes will *not* expose
it to the Internet: the only way for clients to obtain a response is via STUNner.

```console
kubectl apply -f deploy/manifests/udp-greeter.yaml
Expand Down Expand Up @@ -454,11 +450,11 @@ greeter) by STUNner.
```

1. Add the new TLS Gateway. Notice how the `tls-listener` now contains a `tls` object that refers
the above Secret, this way assigning the TLS certificate to use with our TLS listener.
the above Secret, this way assigning the TLS certificate to use with our TURN-TLS listener.

```console
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1alpha2
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: tls-gateway
Expand All @@ -468,7 +464,7 @@ greeter) by STUNner.
listeners:
- name: tls-listener
port: 443
protocol: TLS
protocol: TURN-TLS
tls:
mode: Terminate
certificateRefs:
Expand Down Expand Up @@ -542,7 +538,9 @@ var pc = new RTCPeerConnection(ICE_config);

Note that STUNner comes with a built-in [authentication
service](https://github.com/l7mp/stunner-auth-service) that can be used to generate a complete ICE
configuration for reaching STUNner through a [HTTP REST API](docs/AUTH.md).
configuration for reaching STUNner through a [standards
compliant](https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00) HTTP [REST
API](docs/AUTH.md).

## Tutorials

Expand Down
4 changes: 2 additions & 2 deletions deploy/manifests/stunner-expose-kube-dns.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
apiVersion: gateway.networking.k8s.io/v1alpha2
apiVersion: gateway.networking.k8s.io/v1beta1
kind: GatewayClass
metadata:
name: stunner-gatewayclass
Expand All @@ -24,7 +24,7 @@ spec:
password: "pass-1"

---
apiVersion: gateway.networking.k8s.io/v1alpha2
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: udp-gateway
Expand Down
46 changes: 22 additions & 24 deletions docs/AUTH.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# Authentication

STUNner uses the IETF STUN/TURN protocol suite to ingest media traffic into the Kubernetes cluster,
which, [by design](https://datatracker.ietf.org/doc/html/rfc5766#section-17), provides
comprehensive security. In particular, STUNner provides message integrity and, if configured with
the TLS/TCP or DTLS/UDP listeners, complete confidentiality. To complete the CIA triad, this guide
shows how to configure user authentication with STUNner.
STUNner uses the IETF STUN/TURN protocol suite to ingest media traffic into the Kubernetes cluster, which, [by design](https://datatracker.ietf.org/doc/html/rfc5766#section-17), provides comprehensive security. In particular, STUNner provides message integrity and, if configured with the TURN-TLS or TURN-DTLS listeners, confidentiality. To complete the CIA triad, this guide shows how to configure user authentication with STUNner.

## The long-term credential mechanism

Expand Down Expand Up @@ -47,7 +43,7 @@ The intended authentication workflow in STUNner is as follows.
recent dataplane configuration. This makes sure that whenever you modify the STUNner Gateway API
configuration (say, switch from `static` authentication to `ephemeral`), your clients will
always receive an ICE config that reflects these changes (that is, the username/password pair
will provide a time-windowed credential).
will provide a time-windowed ephemeral credential).

For instance, the below will query the STUnner auth service, which is by default available at
the URL `http://stunner-auth.stunner-system:8088`, for a valid ICE config.
Expand All @@ -69,10 +65,10 @@ The intended authentication workflow in STUNner is as follows.
}
```

Use the below to specify the lifetime of the generated credential to one hour (`ttl`, only makes sense when
STUNner uses `ephemeral` authentication credentials) for a user named `my-user`, and you want
the user to enter your cluster via the STUNner Gateway called `my-gateway` deployed into the
`my-namespace` namespace.
Use the below to generate a valid STUNner credential for a user called `my-user` with a lifetime
of one hour (`ttl`, only makes sense when STUNner uses `ephemeral` authentication
credentials). In addition, we select the Gateway called `my-gateway` deployed into the
`my-namespace` namespace on which we intend to receive WebRTC media from the user:

```console
curl "http://stunner-auth.stunner-system:8088/ice?service=turn?ttl=3600&username=my-user&namespace=my-namespace&gateway=my-gateway"
Expand All @@ -88,17 +84,18 @@ The intended authentication workflow in STUNner is as follows.
[`PeerConnection`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection)
to use the above ICE server configuration in order to use STUNner as the default TURN service.

```javascript
```
var iceConfig = <obtain ICE configuration sent by the application server>
var pc = new RTCPeerConnection(iceConfig);
```

## Static authentication

In STUNner, `static` authentication is the simplest and least secure authentication mode, basically
corresponding to a traditional "log-in" username and password pair given to users. STUNner accepts
(and sometimes reports) the alias `plaintext` to mean the `static` authentication mode; the use of
`plaintext` is deprecated and will be removed in a later release.
corresponding to a traditional "log-in" username and password pair given to users.

> **Note**
STUNner accepts (and sometimes reports) the alias `plaintext` to mean the `static` authentication mode; the use of `plaintext` is deprecated and will be removed in a later release.

When STUNner is configured to use `static` authentication only a single username/password pair is
used for *all* clients. This makes configuration easy; e.g., the ICE server configuration can be
Expand All @@ -109,7 +106,7 @@ credentials, see below).

The first step of configuring STUNner for the `static` authentication mode is to create a
Kubernetes Secret to hold the username/password pair. The below will set the username to `my-user`
and the password to `my-password`. Note that if no `type` is set then STUNner defaults to `static`
and the password to `my-password`. If no `type` is set then STUNner defaults to `static`
authentication.

```console
Expand Down Expand Up @@ -146,9 +143,8 @@ kubectl -n stunner edit secret stunner-auth-secret

> **Warning**
Modifying STUNner's credentials goes *without* restarting the TURN server but may affect existing
sessions, in that existing sessions will not be able to refresh the active TURN allocation with the
old credentials. The application server may also need to be restarted to learn the new TURN
credentials.
sessions, in that active sessions will not be able to refresh the TURN allocation established with
the old credentials.

## Ephemeral authentication

Expand All @@ -158,10 +154,12 @@ with a pre-configured lifetime and, once the lifetime expires, the credential ca
authenticate (or refresh) with STUNner any more. This authentication mode is more secure since
credentials are not shared between clients and come with a limited lifetime. Configuring
`ephemeral` authentication may be more complex though, since credentials must be dynamically
generated for each session and properly returned to clients. STUNner accepts (and sometimes
reports) the alias `longterm` to mean the `ephemeral` authentication mode; the use of `longterm` is
deprecated and will be removed in a later release. Note also that the alias `timewindowed` is also
accepted.
generated for each session and properly returned to clients.

> **Note**
STUNner accepts (and sometimes reports) the alias `longterm` to mean the `ephemeral` authentication
mode; the use of `longterm` is deprecated and will be removed in a later release. The alias
`timewindowed` is also accepted.

To implement this mode, STUNner adopts the [quasi-standard time-windowed TURN authentication
credential format](https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00). In this
Expand All @@ -178,8 +176,8 @@ The advantage of this mechanism is that it is enough to know the shared secret f
able to check the validity of a credential.

> **Warning**
The user-id is used only for the integrity check but STUNner in no way checks whether it identifies
a valid user-id in the system.
The user-id is to ensure that the password generated per user-id is unique, but STUNner in no way
checks whether it identifies a valid user-id in the system.

In order to switch from `static` mode to `ephemeral` authentication, it is enough to update the
Secret that holds the credentials. The below will set the shared secret `my-shared-secret` for the
Expand Down
Loading

0 comments on commit 06bd535

Please sign in to comment.