diff --git a/README.md b/README.md index ae5647c2..7d3dac75 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ rtmp_port: port to listen to incoming RTMP connection on (default 1935) whip_port: port to listen to incoming WHIP calls on (default 8080) http_relay_port: port used to relay data from the main service process to the per ingress handler process (default 9090) rtc_config: configuration for ICE and other RTC related settings, same settings livekit-server RTC configuration. Used for WHIP. +enable_udp_url_pull: allow URL pull ingresses to pull from udp:// urls (default false, see the security note below) +multicast_interface: network interface to join multicast groups on for UDP url pull. Empty lets the OS decide # cpu costs for various Ingress types with their default values cpu_cost: @@ -73,6 +75,21 @@ cpu_cost: The config file can be added to a mounted volume with its location passed in the INGRESS_CONFIG_FILE env var, or its body can be passed in the INGRESS_CONFIG_BODY env var. +> **Security note on `enable_udp_url_pull`** +> +> Only enable UDP url pull if you trust both the callers allowed to create ingresses and the network the +> ingress handlers run on. Unlike `http://` and `srt://` urls, a `udp://` url doesn't make the handler +> connect out to the url host: the handler binds a local socket on the address and port taken from the +> url, and joins the multicast group if one is given. As a result, a caller creating a URL pull ingress +> can: +> +> - Choose which local port the handler binds, potentially colliding with other services on the host. +> - Have the handler ingest unauthenticated traffic. UDP is connectionless, so any host able to reach +> that port can inject media into the session, or spoof the sender address to disrupt a legitimate feed. +> - Have the handler join arbitrary multicast groups and republish whatever it receives into a LiveKit +> room, using the ingress as a relay for streams on the handler's local network that the caller has no +> direct access to. + In order for the LiveKit server to be able to create Ingress sessions, an `ingress` section must also be added to the livekit-server configuration: ```yaml diff --git a/pkg/config/config.go b/pkg/config/config.go index 15bba80d..b98d6c7d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -61,6 +61,21 @@ type ServiceConfig struct { Logging logger.Config `yaml:"logging"` Development bool `yaml:"development"` PSRPCSkipClaim bool `yaml:"psrpc_skip_claim,omitempty"` // Lets psrpc servers skip the claim handshake on queue rpcs + // Allow URL pull ingresses to pull from udp:// urls. Disabled by default, and should only be + // enabled on deployments where both the API callers and the network the handlers run on are trusted. + // Unlike the http and srt sources, udpsrc doesn't connect out to the url host: it binds a local + // socket on the address and port taken from the caller provided url, and joins the multicast group + // if one is given. This has a few consequences: + // - The caller controls which local port the handler binds, and can collide with other services + // running on the host. + // - UDP is connectionless and unauthenticated, so any host able to reach that port can inject + // media into the session, or spoof the sender address to disrupt a legitimate feed. + // - The caller can make the handler join arbitrary multicast groups and republish whatever + // traffic it receives into a LiveKit room, turning the ingress into a relay for streams on + // the handler's local network that the caller couldn't otherwise reach. + EnableUDPURLPull bool `yaml:"enable_udp_url_pull,omitempty"` + // Network interface to join multicast groups on for UDP url pull. Empty means let the OS decide. + MulticastInterface string `yaml:"multicast_interface,omitempty"` // Used for WHIP transport RTCConfig rtcconfig.RTCConfig `yaml:"rtc_config"` diff --git a/pkg/media/urlpull/source.go b/pkg/media/urlpull/source.go index 867db5de..b676ca11 100644 --- a/pkg/media/urlpull/source.go +++ b/pkg/media/urlpull/source.go @@ -22,9 +22,10 @@ import ( "github.com/frostbyte73/core" "github.com/go-gst/go-gst/gst" + "github.com/livekit/protocol/logger" + "github.com/livekit/ingress/pkg/errors" "github.com/livekit/ingress/pkg/params" - "github.com/livekit/protocol/logger" ) var ( @@ -85,6 +86,24 @@ func NewURLSource(_ context.Context, p *params.Params) (*URLSource, error) { } } } + } else if p.EnableUDPURLPull && strings.HasPrefix(p.Url, "udp://") { + elem, err = gst.NewElement("udpsrc") + if err != nil { + return nil, err + } + err = elem.SetProperty("uri", p.Url) + if err != nil { + return nil, err + } + + if p.MulticastInterface != "" { + err = elem.SetProperty("multicast-iface", p.MulticastInterface) + if err != nil { + return nil, err + } + } + + // udpsrc doesn't expose a stats property, so leave printStats unset } else { return nil, errors.ErrUnsupportedURLFormat }