Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

turn: add TURN server #163

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
Open

turn: add TURN server #163

wants to merge 9 commits into from

Conversation

netaskd
Copy link
Contributor

@netaskd netaskd commented Dec 11, 2019

adds TURN server to the infrastructure.

Copy link
Member

@saghul saghul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for getting this effort started! ❤️ Left some comments.

prosody/Dockerfile Show resolved Hide resolved
turn.yml Show resolved Hide resolved
turn/rootfs/defaults/docker-entrypoint.sh Outdated Show resolved Hide resolved
turn/rootfs/defaults/docker-entrypoint.sh Outdated Show resolved Hide resolved
turn/rootfs/defaults/docker-entrypoint.sh Outdated Show resolved Hide resolved
turn/rootfs/defaults/docker-entrypoint.sh Outdated Show resolved Hide resolved
turn/rootfs/defaults/docker-entrypoint.sh Outdated Show resolved Hide resolved
turn/rootfs/defaults/docker-entrypoint.sh Outdated Show resolved Hide resolved
web/rootfs/defaults/config.js Outdated Show resolved Hide resolved
prosody/rootfs/defaults/conf.d/jitsi-meet.cfg.lua Outdated Show resolved Hide resolved
Copy link
Member

@saghul saghul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did another review round!

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
turn.yml Show resolved Hide resolved
turn/rootfs/defaults/docker-entrypoint.sh Outdated Show resolved Hide resolved
turn/Dockerfile Show resolved Hide resolved
turncredentials_secret = "{{ .Env.TURN_SECRET | default "keepthissecret" }}";
turncredentials = {
{ type = "{{ .Env.TURN_TYPE | default "turns" }}",
host = "{{ .Env.TURN_HOST | default .Env.DOCKER_HOST_ADDRESS }}",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the default be TURN_PUBLIC_IP ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prosody doesn't know about TURN_PUBLIC_IP in case TURN_PUBLIC_IP is not set from env file (got dynamically)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can detect it in the init file and re-export it, perhaps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to explain my view on it. TURN_HOST is an announced FQDN for reach cluster of turn. It has use inside prosody for provide XEP-0215.
TURN_PUBLIC_IP is a exactly external IP address of turn instance.
When we use TURN_HOST="turn.example.com", conditionally, A record can be turn.example.com TURN_PUBLIC_IP1, TURN_PUBLIC_IP2, TURN_PUBLIC_IP3 and client (that got TURN_HOST via XEP-0215) goes to this scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, if it's just a single setup. TURN_HOST can be DOCKER_HOST_ADDRESS (if not set) and TURN_PUBLIC_IP is dynamically set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not completely sure of the latest comment.. I guess, we should remove default set for "TURN_HOST" and make it force needed, because if TURN_PUBLIC_IP and DOCKER_HOST_ADDRESS is different, setup will broken. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow you here. I do understand that you could set a hostname. But if you don't, the IP that should be there is the publicc IP of the TURN server, right?

On hindsight DOCKER_HOST_ADDRESS was not a great name. I'll probably rename it to JVB_PUBLIC_IP in the future, which is a better name.

So, to me, it makes sense that the default is the TURN public IP, and not anything else.

Copy link
Contributor Author

@netaskd netaskd Apr 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to use only TURN_HOST for spreading hostname via XEP-0215. In this case we are suppose that:
a) the turn instance only one, and we exactly know how to reach it from outside.
b) the turn instance is multiple, but behind proxy. so, proxy knows how to reach each of them.

The TURN_HOST is mandatory variable, otherwise do not enable turn functions.

@saghul WDYT?

web/rootfs/etc/cont-init.d/10-config Show resolved Hide resolved
@saghul saghul mentioned this pull request Dec 17, 2019
env.example Show resolved Hide resolved
env.example Outdated Show resolved Hide resolved
env.example Outdated Show resolved Hide resolved
@jkroepke
Copy link
Contributor

Can I help here? I have free time...

@sliard
Copy link

sliard commented Apr 22, 2020

For information I finish a version that integrate a turn server into web container.
Why insert coturn inside web container and not create a new one ? It's for use the same SSH certificate.
https://github.com/sliard/docker-jitsi-meet/tree/upgrade_add_turn

It's not fair to create a new MR because it's base on this one at the begining.
I have one last problem, it works only with network_mode host into docker compose.

@ccoenen
Copy link

ccoenen commented Apr 23, 2020

One thing that's currently missing is documentation: which ports one would need to open additionally. This should be added here:

https://github.com/jitsi/docker-jitsi-meet#external-ports

@netaskd netaskd force-pushed the pr_turn branch 2 times, most recently from 0cca664 to e68067d Compare April 23, 2020 16:42
@netaskd
Copy link
Contributor Author

netaskd commented Apr 23, 2020

One thing that's currently missing is documentation: which ports one would need to open additionally. This should be added here:

https://github.com/jitsi/docker-jitsi-meet#external-ports

added in 14d717c

@netaskd netaskd requested a review from saghul May 5, 2020 20:57
@holtgrewe
Copy link

Hi, any news on this?

@mihaiplesa
Copy link

Having this in will be great.

@holtgrewe
Copy link

ping?

@@ -47,6 +47,9 @@ If you want to use jibri too, first configure host as described in JItsi BRoadca
and then run Docker Compose as follows: ``docker-compose -f docker-compose.yml -f jibri.yml up -d``
or to use jigasi too: ``docker-compose -f docker-compose.yml -f jigasi.yml -f jibri.yml up -d``

If you want to enable TURN server, configure it and run Docker Compose as
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything I can do to support his PR towards being merged? I'd really like to vacate Zoom with all of its problems for Jitsi Meet...

@netaskd
Copy link
Contributor Author

netaskd commented May 25, 2020

As I see we have only one unresolved conversation around it. So, if @saghul have a couple of minutes to take a look this, I beleive we will fastly resolve and merge it.

@holtgrewe
Copy link

@saghul could you have a look at this?

Only you can save mankind from Zoom meetings :)

@dsantos21
Copy link

Hi! Although I'm not really versed on all needed to make this work on docker, I could test it in my on premises installation. Would it help? We are using docker, but setting up TURN without docker. Yes... Our JMS doesn't currently have a TURN server... :(

@citosid
Copy link

citosid commented Jun 23, 2020

Thank you so much for this work! I look forward to see this! Jitsi community really rocks!

@holtgrewe
Copy link

Hi, is there anything that could be done here to move this forward?

Comment on lines 5 to 15
{{ if or (.Env.TURN_ENABLE_P2P | default "0" | toBool) (.Env.TURN_ENABLE_JVB | default "0" | toBool) }}
turncredentials_secret = "{{ .Env.TURN_SECRET | default "keepthissecret" }}";
turncredentials = {
{ type = "{{ .Env.TURN_TYPE | default "turns" }}",
host = "{{ .Env.TURN_HOST | default "8.8.8.8" }}",
port = {{ .Env.TURN_PORT | default "3478" }},
transport = "{{ .Env.TURN_TRANSPORT | default "tcp" }}"
}
}
{{ end }}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The credential must be set in the prosody.cfg.lua files and not in the jitsi-meet.cfg.lua.
In jitsi-meet.cfg.lua you have to add the "turncredantials" in the module_enabled section of the meet.jitsi Virtualhost.

@nestorconde
Copy link

I've worked on this from the latest docker-jitsi-meet adding all the changes of the PR except for two:

  1. It does not contain the changes to the README of the original PR since that page content has been moved somewhere else
  2. It changes the name of the turn docker container (from jitsi/turn which wasn't found to instrumentisto/coturn).

I attach the patch which is mostly the same of the PR except for these two changes. I'd also be able to provide the terraform script that created the machines I'm testing in (in Digital Ocean). turn.diff

Problems

Jitsi works but I'm having some problems to test whether the turn server is working.

I can't block the port 10000/udp with ufw. I've tried to block ports with ufw but it doesn't seem to have any effect perhaps due to this?.

The other thing I've tried is to checking chrome://webrtc-internals (since I haven't been able to block 10000 this is not as useful as it could be). When I test with several participants I see in the some of the tabs "iceServers: [stun:meet-jit-si-turnrelay.jitsi.net:443]", but this is the same I see in a server where I have not applied the patch

@netaskd Any way I could go on from here in order to provide help to advance this pull request?

First option I can think of is to try other ways to block port 10000. Do other iptables management utilities have the same problem as ufw with respect to docker?, any advice on how to do block 10000/udp without stumbling on this problem?

@holtgrewe
Copy link

First option I can think of is to try other ways to block port 10000. Do other iptables management utilities have the same problem as ufw with respect to docker?, any advice on how to do block 10000/udp without stumbling on this problem?

Hi, does this help?

https://stackoverflow.com/questions/49549834/ufw-firewall-is-not-working-on-ubuntu-in-digitalocean/49563279#49563279

@baccenfutter
Copy link

baccenfutter commented Jun 29, 2020

Docker generally doesn't play well out-of-the-box with firewall configuration tools such as UFW, shorewall, etc. Such tools generate IPTABLES rules based on some "easier" configuration format, while Docker itself leverages IPTABLES to "magically" configure NAT for the containers. If both tools are not aware of each other, they will overwrite or shadow each-other's IPTABLE rules.

Both UFW and Shorwall have builtin support for Docker in their latest versions which is disabled by default. Enabling this feature will make UFW "docker-aware" and preserve Dockers IPTABLE rules when run.

However, it is still rather complicated to block incoming connections to Docker via IPTABLE rules, due to the fact that NAT is processed before the FILTER rules, meaning that packets to Docker-configured ports are redirected into the corresponding container before the packets run through the FILTER chain of the host - therefor never hitting the rule.

See: https://2.bp.blogspot.com/-skGmyfTzXGI/U84PI9EbjkI/AAAAAAAAD6Q/kjjfG17_yo8/s1600/Linux-Iptables-firewall-schema.png

Solution

The correct way to prevent access into a Docker container is to explicitly state 127.0.0.1 in the --port argument when running a container, such as

docker run --port 127.0.0.1:10000:10000 ...

This will only allow access to port 10000 when the connection is coming from the Docker host itself and will deny access when coming from elsewhere.

@parruc
Copy link

parruc commented Jun 30, 2020

I also need this. How can I help the development? What's still open?

@nestorconde
Copy link

@baccenfutter What I did was modify the docker-compose.yml at the root of the project by changing

jvb
   ...
    ports:
-      '${JVB_PORT}:${JVB_PORT}/udp'
+      '127.0.0.1:${JVB_PORT}:${JVB_PORT}/udp'

Is that equivalent to what you suggested? (I don't know enough about docker and docker-compose to evaluate whether it is).

In any case after doing that I was having problems with the audio/video from the videoconference which only worked momentarily and then stopped working. Perhaps there's some unexpected side effect to that way of restricting the port?, if that's not the case then my patch is not working when 10000/udp is blocked.

@holtgrewe I did not try that because it seemed to me that I'd be changing more things than strictly needed (it would affect potentially other things). In order to evaluate whether the patch is working I need a test that isolates the changes so I wanted to only block 10000/udp and leave everything else the same.

@netaskd @saghul There's a lot of interest in this PR and some people would like to help but we don't have the information on how to do it. As far as I understand there's just this change pending review but the turncredentials code in netaskd branch have changed since the time the review was requested. Could you provide some guidance if helping is possible? If it isn't it would be useful to know that too ;)

goacid added a commit to goacid/docker-jitsi-meet that referenced this pull request Jul 3, 2020
jitsi#163

Fix prosody configuration
Fix web configuration
Fix name : turn => coturn
Update README.md
@goacid goacid mentioned this pull request Jul 3, 2020
--no-multicast-peers \
--realm=${TURN_REALM:-realm} \
--listening-ip=$(hostname -i) \
--external-ip=${TURN_PUBLIC_IP} \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per https://hub.docker.com/r/instrumentisto/coturn they recommend using the detect-external-ip tool to automatically get the external IP. I did not check how that works, but I'm not sure as well to add a dependency on icanhazip.com to get the TURN server up a good thing.

Copy link
Contributor Author

@netaskd netaskd Jul 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, should have checked it before. Awesome. I've been testing this and works great. My use case is on kubernetes and Im having some issues with networking, but that is another story.

Copy link
Contributor

@almereyda almereyda Feb 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been adopted for the upstream coturn image, too, despite the strategy it uses is a little different.

https://github.com/coturn/coturn/blob/master/docker/coturn/rootfs/usr/local/bin/detect-external-ip.sh

See previous comment https://github.com/jitsi/docker-jitsi-meet/pull/163/files#r802157850

@pandel
Copy link
Contributor

pandel commented Aug 20, 2020

Read through all of this but didn't get it... will this also support adding an external TURN server without starting an additional coturn docker container which isn't needed in that case?

@hermanbanken
Copy link

hermanbanken commented Sep 11, 2020

What is the status of this PR, what discussion is still pending?
I'm also working on integrating Jitsi, and we really need to support TURN for corporate VPNs.


The setup we are using is to split traffic with Envoy & also terminate TLS there. That way both Jitsi's NGINX/web container & CoTURN don't need to do TLS offloading. Example envoy.yaml configuration:

# docker run -v $PWD:/etc/ssl/private -v $PWD:/data envoyproxy/envoy:v1.14.4 /usr/local/bin/envoy -c /data/envoy.yaml --mode validate
#
admin:
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

static_resources:
  listeners:
  # Main listener
  - address:
      socket_address: { address: 0.0.0.0, port_value: 4430 }
    # TLS Inspector parses the SNI & ALPN settings
    listener_filters:
    - name: "envoy.filters.listener.tls_inspector"
      typed_config: {}
    # One filter chain per SNI & ALPN
    filter_chains:
      # shard-1 HTTP
      - filter_chain_match: # shard-1 http
          server_names: ["shard-1.eu.example.org"]
          application_protocols: ["http/1.1", "h2"]
        transport_socket: &tmpl_transport_socket
          name: envoy.transport_sockets.tls
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
            common_tls_context:
              tls_certificates:
              - certificate_chain: { filename: "/etc/ssl/private/tls.crt" }
                private_key: { filename: "/etc/ssl/private/tls.key" }
        filters: &tmpl_http_filters
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              stat_prefix: ingress_http
              route_config:
                virtual_hosts:
                - name: default
                  domains: "*"
                  routes:
                  - match: { prefix: "/" }
                    route: { cluster: service_shard1_nginx }
              http_filters:
                - name: envoy.filters.http.router
      # shard-1 TURN
      - filter_chain_match:
          server_names: ["shard-1.eu.example.org"]
        transport_socket: *tmpl_transport_socket
        filters:
          - name: envoy.filters.network.tcp_proxy
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
              stat_prefix: tcp
              cluster: service_shard1_turn
      # fallback HTTP (redirects to Application Level router)
      - filter_chain_match:
          server_names: ["*.example.org"]
        transport_socket: *tmpl_transport_socket
        filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              stat_prefix: ingress_http
              route_config:
                virtual_hosts:
                - name: default
                  domains: "*"
                  routes:
                  - match: { prefix: "/" }
                    route: { cluster: service_fallback }
              http_filters:
                - name: envoy.filters.http.router
      # fallback for health check
      - filter_chain_match:
        transport_socket: *tmpl_transport_socket
        filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              stat_prefix: direct_http
              route_config:
                virtual_hosts:
                - name: default
                  domains: "*"
                  routes:
                  - match: { prefix: "/" }
                    direct_response:
                      status: 200
                      body:
                        inline_string: "Fallback"
              http_filters:
                - name: envoy.filters.http.router
  clusters:
  - name: service_fallback
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    load_assignment:
      cluster_name: service_fallback
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: nginx-internal
                port_value: 80
  - name: service_shard1_nginx
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    load_assignment:
      cluster_name: service_shard1_nginx
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: nginx.shard-1.svc.cluster.local
                port_value: 80
  - name: service_shard1_turn
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    load_assignment:
      cluster_name: service_shard1_turn
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: turn.shard-1.svc.cluster.local
                port_value: 5349

I'm also working on an Envoy xDS API endpoint that serves this configuration dynamically. That would ease adding shards drastically in our Kubernetes setup.

@nestorconde
Copy link

nestorconde commented Oct 2, 2020

I've documented in the community my tests with a simple firewall and:

  1. The setup described in this PR
  2. Multiplexing (for turn to work when its ports are blocked by the firewall) based on the docs. My exact setup for multiplexing can be seen here

As far as I can see under with this setup for the videoconferences to work it's enough if the firewall allows 80, 443 (both outbound and inbound) and 10000 outbound.

I know this PR does not include the multiplexing bit but I think this contributes to the conversation and the multiplexing part is an interesting direction for turn in docker-jitsi-meet to pursue

@sapkra sapkra added the enhancement New feature or request label Oct 8, 2020
@pierreozoux pierreozoux mentioned this pull request May 25, 2021
# run coturn server with API auth method enabled.
turnserver -n ${TURN_ADMIN_OPTIONS} \
--verbose \
--prod \

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coturn/coturn@520e172

--no-software-attribute Production mode: hide the software version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good spot!

@@ -0,0 +1,13 @@
ARG VERSION
FROM instrumentisto/coturn:${VERSION:-latest}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been deprecated: instrumentisto/coturn-docker-image@0a4a7f0

It is suggested to switch to the upstream image coturn/coturn (🔗).

@VidarHUN
Copy link

Hi Folks,

This PR is still alive or somebody working on it? What kind of job is left to do?

Also, I don't understand this PR. As I can see the main goal of this PR is to make it happen to configure an external TURN server for Jitsi and you have created a new image which will be a coTURN server for testing or if somebody wants to use it. Am I right? If it's true then I would like to help you to finish this, because from the commits and because of my lack of knowledge with the configuration I can't see another way to configure Jitsi containers to use TURN servers.

If we can configure Jitsi to use TURN server, then there is a way to somehow force the relay through TURN with p2p connections? Can I just put a zero or false value into an env variable and force it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.