diff --git a/guides/advanced/debugging.md b/guides/advanced/debugging.md index b407dc4..25e350b 100644 --- a/guides/advanced/debugging.md +++ b/guides/advanced/debugging.md @@ -162,4 +162,4 @@ You can also configure PeerConnection to use a specific port range by doing Otherwise, the connection won't be established at all, or just in one direction. -Read more in our [Deploying tutorial](./deploying.md#allow-udp-traffic-in-your-firewall)! +Read more in our [Deploying tutorial](../deploying/bare.md#allow-udp-traffic-in-your-firewall)! diff --git a/guides/advanced/deploying.md b/guides/deploying/bare.md similarity index 91% rename from guides/advanced/deploying.md rename to guides/deploying/bare.md index f8620b5..2ae689c 100644 --- a/guides/advanced/deploying.md +++ b/guides/deploying/bare.md @@ -1,7 +1,7 @@ -# Deploying +# Deploying on bare machines Deploying WebRTC applications can be cumbersome. -Here are a few details you should keep in mind when trying to push your project into production. +Here are a few details you should keep in mind when trying to push your project into production on a bare machine. ## Allow UDP traffic in your firewall @@ -37,12 +37,6 @@ docker run -p 50000-50010/udp myapp Keep in mind that exporting a lot of ports might take a lot of time or even cause the Docker daemon to timeout. That's why we recommend using host's network. -## Choose your cloud provider wisely - -Many cloud providers do not offer good support for UDP traffic. -In such cases, deploying a WebRTC-based application might be impossible. -We recommend using bare machines that you can configure as you need. - ## Enable HTTPS in your frontend The server hosting your frontend site must have HTTPS enabled. @@ -66,7 +60,7 @@ Read more [here](https://nginx.org/en/docs/http/websocket.html). ## Configure STUN servers -If you are deploying your application behind a NAT, you have to configure a STUN +If you are deploying your application behind a NAT, you have to configure a STUN server that will allow it to discover its public IP address. In Elixir WebRTC this will be: @@ -86,6 +80,6 @@ And as a TURN server, you can always use our [Rel](https://github.com/elixir-web If your application is deployed behind a very restrictive NAT, which should be very rare (e.g. a symmetric NAT), you will need to configure a TURN server. -In most cases, TURN servers are needed on the client side as you don't have any control +In most cases, TURN servers are needed on the client side as you don't have any control over a network your clients connect from. For testing and experimental purposes, you can use our publicly available TURN called [Rel](https://github.com/elixir-webrtc/rel)! diff --git a/guides/deploying/fly.md b/guides/deploying/fly.md new file mode 100644 index 0000000..1448952 --- /dev/null +++ b/guides/deploying/fly.md @@ -0,0 +1,50 @@ +# Deploying on Fly.io + +Elixir WebRTC-based apps can be easily deployed on [Fly.io](https://fly.io)! + +There are just two things you need to do: + +- configure a STUN server both on the client and server side +- use custom Fly.io IP filter on the server side + +In theory, configuring a STUN server just on a one side should be enough but we recommend to do it on both sides. + +In JavaScript code: + +```js +pc = new RTCPeerConnection({ + iceServers: [{ urls: "stun:stun.l.google.com:19302" }], +}); +``` + +in Elixir code: + +```elixir +ip_filter = Application.get_env(:your_app, :ice_ip_filter) + +{:ok, pc} = + PeerConnection.start_link( + ice_ip_filter: ip_filter, + ice_servers: [%{urls: "stun:stun.l.google.com:19302"}] + ) +``` + +in `runtime.exs`: + +```elixir +if System.get_env("FLY_IO") do + config :your_app, ice_ip_filter: &ExWebRTC.ICE.FlyIpFilter.ip_filter/1 +end +``` + +in fly.toml: + +```toml +[env] + # add one additional env + FLY_IO = 'true' +``` + +That's it! +No special UDP port exports or dedicated IP address needed. +Just run `fly launch` and enjoy your deployment :) diff --git a/lib/ex_webrtc/ice/fly_ip_filter.ex b/lib/ex_webrtc/ice/fly_ip_filter.ex new file mode 100644 index 0000000..54f4c5f --- /dev/null +++ b/lib/ex_webrtc/ice/fly_ip_filter.ex @@ -0,0 +1,22 @@ +defmodule ExWebRTC.ICE.FlyIpFilter do + @moduledoc """ + ICE IP filter for Fly.io deployments. + + This module defines a single function, which filters out IP addresses, + which ICE Agent will use as its host candidates. + """ + + @spec ip_filter(:inet.ip_address()) :: boolean() + def ip_filter(ip_address) do + case :inet.gethostbyname(~c"fly-global-services") do + # Assume that fly-global-services has to resolve + # to a single ipv4 address. + # In other case, don't even try to connect. + {:ok, {:hostent, _, _, :inet, 4, [addr]}} -> + addr == ip_address + + _ -> + false + end + end +end diff --git a/lib/ex_webrtc/peer_connection/configuration.ex b/lib/ex_webrtc/peer_connection/configuration.ex index 5d5af28..c0d1dcf 100644 --- a/lib/ex_webrtc/peer_connection/configuration.ex +++ b/lib/ex_webrtc/peer_connection/configuration.ex @@ -5,6 +5,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do require Logger + alias ExICE.ICEAgent alias ExWebRTC.{RTPCodecParameters, SDPUtils} alias ExSDP.Attribute.{Extmap, FMTP, RTCPFeedback} @@ -148,7 +149,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do controlling_process: Process.dest(), ice_servers: [ice_server()], ice_transport_policy: :relay | :all, - ice_ip_filter: (:inet.ip_address() -> boolean()), + ice_ip_filter: ICEAgent.ip_filter(), ice_port_range: Enumerable.t(non_neg_integer()), audio_codecs: [RTPCodecParameters.t()], video_codecs: [RTPCodecParameters.t()], diff --git a/mix.exs b/mix.exs index de149e8..6c26294 100644 --- a/mix.exs +++ b/mix.exs @@ -57,7 +57,8 @@ defmodule ExWebRTC.MixProject do defp deps do [ {:ex_sdp, "~> 1.0"}, - {:ex_ice, "~> 0.8.0"}, + # {:ex_ice, "~> 0.8.0"}, + {:ex_ice, github: "elixir-webrtc/ex_ice", override: true}, {:ex_dtls, "~> 0.16.0"}, {:ex_libsrtp, "~> 0.7.1"}, {:ex_rtp, "~> 0.4.0"}, @@ -80,17 +81,19 @@ defmodule ExWebRTC.MixProject do "simulcast", "modifying", "mastering_transceivers", - "deploying", "debugging" ] + deploying_guides = ["bare", "fly"] + [ main: "readme", logo: "logo.svg", extras: ["README.md"] ++ Enum.map(intro_guides, &"guides/introduction/#{&1}.md") ++ - Enum.map(advanced_guides, &"guides/advanced/#{&1}.md"), + Enum.map(advanced_guides, &"guides/advanced/#{&1}.md") ++ + Enum.map(deploying_guides, &"guides/deploying/#{&1}.md"), assets: "guides/assets", source_ref: "v#{@version}", formatters: ["html"], @@ -98,7 +101,8 @@ defmodule ExWebRTC.MixProject do nest_modules_by_prefix: [ExWebRTC], groups_for_extras: [ Introduction: Path.wildcard("guides/introduction/*.md"), - Advanced: Path.wildcard("guides/advanced/*.md") + Advanced: Path.wildcard("guides/advanced/*.md"), + Deploying: Path.wildcard("guides/deploying/*.md") ], groups_for_modules: [ MEDIA: ~r"ExWebRTC\.Media\..*", diff --git a/mix.lock b/mix.lock index b9b74f3..d9467b9 100644 --- a/mix.lock +++ b/mix.lock @@ -13,7 +13,7 @@ "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, "ex_dtls": {:hex, :ex_dtls, "0.16.0", "3ae38025ccc77f6db573e2e391602fa9bbc02253c137d8d2d59469a66cbe806b", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2a4e30d74c6ddf95cc5b796423293c06a0da295454c3823819808ff031b4b361"}, - "ex_ice": {:hex, :ex_ice, "0.8.0", "f9bd181e8fd2f8ac9a808587ee8a47bf667143069d75f6e4892a62156d798aa7", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.1.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "b0476f6b18986f6df48fda4cecb3be5022323572790d1bb49da10b177c936b4e"}, + "ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "4c5190687e11b28a8209f2c930895647d0b2a64c", []}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"},