schlagmichdoch / PairDrop

PairDrop: Local file sharing in your browser. Inspired by Apple's AirDrop. Fork of Snapdrop.
https://pairdrop.net
GNU General Public License v3.0
3.71k stars 195 forks source link

TURN/TURNS in docker-compose is not working correctly #105

Open xundeenergie opened 1 year ago

xundeenergie commented 1 year ago

Describe the bug I tried the docker-compose-coturn.yml yesterday with turns (tls secured), and it doesn't work.

There is a lack of documentation... maybe it's the only thing.

This is just a friendly reminder for me, to dig deeper into and maybe hopefully provide the missing parts to get it running.

With extra coturn (on the same host) i got it working.

To Reproduce Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior A clear and concise description of what you expected to happen.

Screenshots If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

Smartphone (please complete the following information):

Self-Hosted No | Yes

Self-Hosted Setup Proxy: Nginx | Apache2 Deployment: docker run | docker-compose | npm run start:prod

Additional context Add any other context about the problem here.

schlagmichdoch commented 1 year ago

Thanks for digging! Could you provide the conf you used for turns when it worked on your system? Did you need to change anything else apart from specifying the tls port with tls-listening-port=5349?

I have not fully tested the docker-compose-coturn file so it would be great if you could provide an PR if you get it working.

As I understand you, currently:

Is this correct?

xundeenergie commented 1 year ago

First... it is a really strange thing... :)

My mobile-phone-provider is the same as my cable-internet-provider. P2P-Connections via STUN are working from mobile internet to cable-internet. didn't think, that this works...

But to my other phone, my work-phone, is from another provider. And there i need a TURN-Connection for PairDrop. Without it does not work.

This is, what i found out, for testing-environment (luckily i have to different internet-providers).

STUN connections are working with my ejabberd. Which provides STUN and TURN-Server. And i'm sure, i also got turn working with ejabberd. But a developer from ejabberd said, this could not work... because the implementation in ejabberd specialized for xmpp/ejabberd...

So i played around yesterday many hours to find out, what works and how.

What does not work with docker-compose-coturn.yml

So i added after the turnserver.conf the following lines

volumes:
       - ./turnserver.conf:/etc/coturn/turnserver.conf
       - ./logs/:/var/log/
       - /etc/letsencrypt/live/<my.domain>/:/etc/letsencrypt/live/<my.domain>/

And the pairdrop-server does not pick up the turn-connection.

I played around, with 2 and 3 devides... with turn, turns, stun and stuns. And it's a bit weird, that all devices see all devices at the same time, it need all 4 protocolls... The TLS-secured connections allone are not working.

So i configured rtc_config.json the following way:

{
  "sdpSemantics": "unified-plan",
  "iceServers": [
    {
        "urls": "stun:pairdrop.my.domain:3478"
    },
    {
        "urls": "stuns:pairdrop.my.domain:5349"
    },
    {
        "urls": "turn:pairdrop.my.domain:3478",
        "username": "coturnuser",
        "credential": "verY-$ecret"
    },
    {
        "urls": "turns:pairdrop.my.domain:5349",
        "username": "coturnuser",
        "credential": "verY-$ecret"
    }
  ]
}

for lets-encrypt i have to say, i have a wildcard-certificate valid for my.domain and *.my.domain so it works with pairdrop.my.domain

and THEN i realized, the rtc-settings have to be added... so i added the following block from docker-compose.yml to docker-compose-coturn.yml

    environment:
      - RTC_CONFIG=/home/node/app/rtc_config.json
      - WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
      - RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
      - TZ=Europa/Vienna # Time Zone

I will create a PR with my working config (anonymized for sure). Now it works. I can transfer files via internet from one device behind a NAT to another device behind a NAT

schlagmichdoch commented 1 year ago

Very nice! Thanks for elaborating and contributing! 👍

I added some remarks to the PR

P2P-Connections via STUN are working from mobile internet to cable-internet. didn't think, that this works... Maybe your devices both connect via IPv6. You could have a look at the sdp in the browser console to check for the used IP.

But to my other phone, my work-phone, is from another provider. And there i need a TURN-Connection for PairDrop. Without it does not work.

Someone also mentioned that his PC could connect when using Wi-Fi but needed a TURN server when connected via Ethernet. I guess its also connected to which IPs are used.

The TLS-secured connections allone are not working.

I've seen that you used tls-listening-port = 443 in your turnserver.conf but used port 5349 in the rtc_config.json. Could that be the problem?

that all devices see all devices at the same time, it need all 4 protocolls

The problem with that would be that devices would never use the tls connections as the list is always tried one by one from the first to the last specified server. If you test the stuns and turns server via https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/ and if they work they should also be enough for PairDrop without their non tls counterpart. Also Firefox always logs a warning if you specify more than two iceservers.

xundeenergie commented 1 year ago

So, i tested a lot, and improved the whole thing to get tls-secured stuns and turns working allone for pairdrop. Had some troubles with a collegue, where we did not get the connection working... After my improvements, we had no time to test it again. Tomorrow is also a new day. Hope it works then.

A new commit is following in short, which also adds in the howto "host-you-own.md" some config and description how to modify nginx to have port 443 for the website(s) and coturn turns/stuns too! This should avoid some firewall troubles.

xundeenergie commented 1 year ago

What's strange... now i can see every device on my pairdrop-server... also via internet, which are not in the same LAN... even not paired devices. Do you have an idea, what could be the reason for this?

We can try it on pairdrop.schuerz.at if you want to see this.

xundeenergie commented 1 year ago

It's very strange... I test it with 2 collegues. They are all at home in very different nets...

The visibility via internet for other devices in other nets depend on the listening- and relay-ips for coturn.

i tried different combinations from relay-ip, listening-ip external-ip is always set to the real external-ip of my host.

for relay-ip and listening-ip is set my external-ip, the vpn-ip of this host and for listening-ip additionally 0.0.0.0

so if relay-ip is set to the vpn-ip (which is not reachable from outside), the devices see each other. if no relay-ip is set, pairing works, but devices are not visible at all (verified and unverified).

i don't get it working, that devices over internet can only see if they are paird... with working stuns and turns all devices are visible as if they are in the same LAN/WLAN. I've not enough knowledge about stun/turn to understand all this... :-/

schlagmichdoch commented 1 year ago

This is not intended behavior but has probably nothing todo with the stuns/turns configuration. Instead, the Auto discovery feature works by grouping every device with the same public IP address together. If your web server is not setup correctly PairDrop sees all request as coming from the internal web server address so all devices are shown to each other.

More infos: https://github.com/schlagmichdoch/PairDrop/issues/69#issuecomment-1461807947

To get around this, you must specify the x-forwarded-for header correctly! Take a look at the docs for information on how to do this via nginx or Apache: https://github.com/schlagmichdoch/PairDrop/blob/master/docs/host-your-own.md#http-server

Additionally, you can have a look at this comment on how to test a debugging flag to show you which ip addresses PairDrop sees which should make debugging the setup simpler. It is currently still a pending PR but if it that branch works for you the master branch should work too. https://github.com/schlagmichdoch/PairDrop/issues/69#issuecomment-1462000049

xundeenergie commented 1 year ago

This was also my intention... and so i checked this before... but the settings in nginx are the same as in the documentation.

Maybe it has to do with the stream-module... i added for having coturn also listening on port 443. This is new to me, so i have to do more research on it.

schlagmichdoch commented 1 year ago

I would check whether everything works as expected if you remove the upstream by using the debugging feature. By using DEBUG_MODE="true" npm start It will log the ips to the console like so:

----DEBUGGING-PEER-IP-START----
remoteAddress: ::ffff:172.17.0.1
x-forwarded-for: 127.0.0.1
cf-connecting-ip: undefined
PairDrop uses: 127.0.0.1
IP is private: false
if IP is private, '127.0.0.1' is used instead
----DEBUGGING-PEER-IP-END----

I have also rebased the branch onto the current master.

xundeenergie commented 1 year ago

So i found out, the reason is with the stream module from nginx. In debian it must be compiled eith sn extra module, which is not build by default...

So i think, i will remove all the stuff for coturn to listen on port 443. It should work as expected on listening on Port 5349... so some cases, where firewall is blocking this port, pairdrop wont work there...

schlagmichdoch commented 1 year ago

You mean port 5349? Sounds good to me! I will probably squash your commits into one merge commit to clean up the commit history.

xundeenergie commented 1 year ago

Pairdrop uses the "lt-cred-mech". Is this right?

I'm asking, because in coturns config i found

# This way be aware that you can't use both auth mechnaism in the same time!
# Use in config either the lt-cred-mech or the use-auth-secret
# to avoid any confusion.

I setup an own coturn on another host, which listens on port 443 for tls/turns.

It works. Tests with my collegues network are outstanding. I hope, we'll do it today.

If this works, i will use this extea coturn also for xmpp and matrix voice/videocalls. But therefore pairdrop must be able to use the other auth mechanism "use-auth-secret".

Is this an "easy work"? Or does it need much more work for Pairdrop to implement this short-term credentials?

schlagmichdoch commented 1 year ago

If you’re sure that turns only works correctly when using short-term-credentials for the turn server we could probably implement it like stated here: https://stackoverflow.com/a/35767224

We would need another env var specifying a turn-server that needs short-term-credentials together with its secret. Currently, whenever a new client connects to the node server, it simply sends the parsed rtc_config.json. With short term credentials the node server would need to generate the rtc_config JSON on every new connection before sending it to the client. I’m not sure though how to do that so there are no breaking changes with the current implementation. Any ideas?

Maybe if the rtc_config points to a .json file it gets simply parsed like right now and if it points to a .yml file the rtc_config JSON will be generated dynamically. The admin would then specify sth like this which would get evaluated on application start:

addition-attributes:
- sdpSemantics: unified-plan
stun-servers:
- urls: stun:pairdrop.my.domain:3478
- urls: stuns:pairdrop.my.domain:5349
turn-servers:
- urls: turn:pairdrop.my.domain:3478
  username: coturnuser
  credential: verY-$ecret
- urls: turns:pairdrop.my.domain:5349  
  username: coturnuser
  credential: verY-$ecret
turn-servers-short-term-credentials:
- urls: turns:pairdrop-stc.my.domain:5349
  secret: even-more-$ecret
xundeenergie commented 1 year ago

No no... Pairdrop works now good with lt-cred-mech. I run a coturn-server on another host, because it's to much efford to recompile nginx to get stream-realip-module, which is needed for correct functionality to host pairdrop and coturn on the same host, both listening on port 443...

I did now many tests, also with my collegue, where pairdrop wouldn't work with turns on port 5349. Now with coturn on another host listening on port 443 we can connect and share files.

So i spread the information to my fediverse-followers to test my new setup. and i got positive reactions. Also from a setup, where a smartphone and a desktop are in different VPN.

So it seems now working perfectly.

The thing, i asked for short-term credentials is, that coturn can use lt-cred-mech OR use-auth-secret. Not both together. To use this coturn also for matrix and xmpp (the listening port 443 is there also a good idea) needs to configure coturn for use-auth-secret... than it would not work with pairdrop.

I've no idea, how short-term-creds work inside... and so it was only an idea of improvement.

Long story short:

Run Pairdrop on one host and coturn on another host, configured TLS to port 443 instead of standardport 5349 makes Pairdrop perfect working also in difficult environment. This is my conclusion for now.

schlagmichdoch commented 1 year ago

Run Pairdrop on one host and coturn on another host, configured TLS to port 443 instead of standardport 5349 makes Pairdrop perfect working also in difficult environment.

Glad you got it working in the end! I’ve got one more idea: If we have two domains pointing to the same host, would it be possible to listen to DomainA:443 and rewrite to node:3000 and DomainB:443 and rewrite to coturn:5347 using nginx? That way you would still need two domains but there wouldn’t be a need for a second host so it might be possible to run both via one docker compose file. What do you think?

What about the PR now? Is this still recommended and mergeable or would you rather leave the docker-compose-coturn.yml without turn over tls support? Then we may add a short paragraph about hosting turn over TLS on another host in the docs.

Regarding short-term-creds, the feature is rather well explained in the stackoverflow thread. You include a Unix timestamp into the credentials until which the credentials are valid. This way you can not take the credentials and use the turn server for another application without consent. Would be possible to implement but currently I don’t see the big benefit.

xundeenergie commented 1 year ago

It belongs to your setup.

If you run only one docker-service (docker-compose with several containers as here with pairdrop and coturn) on a vHost, then you can include a nginx with stream-realip-module enabled into the docker-compose-file. This nginx-config must include a module-configuration to separate the streams for pairdrop and coturn as i tried in my config. And nginx MUST set the clients remote-ip for pairdrop, which should stream-realip-module do.

There you need two domains: pairdrop.example.com and pairdrop.turn.example.com. The stream-configuration separates the ssl-streams fom port 443 to pairdrop -> port 3000 in pairdrop-container and coturn -> 5349 in coturn container.

I did this, and ALL devices over the internet have been visible to ALL others in pairdrop, because nginx forwards for all of them the same ip-address... because stream-realip-module is missing here (and i'm not able to compile it myself...).

So this configuration in nginx

stream {
    map $ssl_preread_server_name $name {
        <TURN-DOMAIN>           turn_server;
        default                 url_backend;
    }
    upstream url_backend {
        server 127.0.0.1:4444;
    }
    upstream turn_server {
        server <IP-ADDRESS>:5349;
    }
    server {
        listen 443;
        listen 443 udp;
        listen [::]:443;
        listen [::]:443 udp;
        ssl_preread on;
        proxy_pass $name;
        proxy_buffer_size 10m;
    }
}

does the separation of streams for coturn and pairdrop. but you need the options set_real_ip_from from the stream-realip-module. THAS is the problem.

If you find a way to provide this module in a nginx in a container, it could be a good idea, to include nginx in the docker-compose. Maybe letsencrypt would be also a good idea then... but it needs 2 domains. Yes.

If it's enough for YOUR needs as developer from pairdrop to provide coturn on port 3478 and 5349, then my PR should be good to be merged. But give me a day to check it again, if it is on my last changes from my machine.

schlagmichdoch commented 1 year ago

I need to look into it as it seems that the current turn configuration is not supported by chromium. Maybe I will need to implement TURN over TLS for it to work on Chromim base browsers.

Regarding your implementation, I just tested pairdrop.schuerz.at.

Chromium throws the following error:

Uncaught DOMException: Failed to construct 'RTCPeerConnection': 'stuns' is not one of the supported URL schemes 'stun', 'turn' or 'turns'.

While Firefox throws this warning:

WebRTC: STUNS is not yet supported.

I guess for now we can not support STUN over tls at all and I have not found any roadmap for neither of Firefox and Chromium

schlagmichdoch commented 1 year ago

As there seems to be an error for all chromium users that need a TURN server I gave this a tried to debug the TURN server and can add some things to your findings:


I quickly tried an pairdrop instance using a STUN server and your TURNS server and that seems to work! Congrats, good work! I get one error though which does not seem to be fatal:

Note: errors from onicecandidateerror above are not neccessarily fatal. For example an IPv6 DNS lookup may fail but relay candidates can still be gathered via IPv4.
The server turns:turn.schuerz.at:443?transport=tcp returned an error with code=701:
TURN host lookup received error.

You can also test it yourself here using your TURNS server: https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/ everything works (type is relay) but there is an error that tcp does not seem to work.

This is only an issue on chromium though and Firefox does not log any errors so maybe its also a chromium bug.


Regarding the stream-realip-module:

The latest nginx version debian 11 installs via apt from the Debian Main repo is nginx/1.18.0 which does not include the module nginx/1.22.1 does have the module prebuilt (according to this stackoverflow) which is available from the official nginx repository: https://debian.pkgs.org/11/nginx-arm64/nginx_1.22.0-1~bullseye_arm64.deb.html


For turn without tcp I could prevent the error with chrome by using the ip-address instead of the domain name in the rtc_config.json like so:

{
  "sdpSemantics": "unified-plan",
  "iceServers": [
    {
        "urls": "stun:192.145.47.48:3478"
    },
    {
        "urls": "turn:192.145.47.48:3478",
        "username": "coturnuser",
        "credential": "verY-$ecret"
    }
  ]
}

That gave me a hint in the direction in the right direction. After changing the following, I could finally make it work for without a second host or domain:

Missing permission for user turnserver

The default user running coturn is turnserver in group turnserver. As I directly pointed to the letsencrypt files inside the turnserver.conf tls did not work since the user (obviously) did not have permissions to read the privkey. I now have a letsencrypt renewal job configured to copy the corresponding certificate files into a folder owned by user turnserver. (See here or here)

Afterwords, coturn started normally and port 5349 was opened correctly (upd and tcp).

Missing kernel module sctp

I don't think this was not a big issue but when I started coturn with logs enabled I got two warnings:

socket: Protocol not supported

which was due to the kernel module sctp not being loaded: https://github.com/coturn/coturn/issues/492#issuecomment-1275626046

After enabling it and restarting coturn the warnings were gone.

turnserver.conf

Finally, I dug into the coturn conf docs and edited my conf accordingly. https://github.com/coturn/coturn/blob/master/examples/etc/turnserver.conf#L56

If everything is on the same device we do not need relay-ip, listening-ip or external-ip.

This is my final turnserver.conf:

# TURN server name and realm
realm=pairdrop.net
server-name=pairdrop

# Main listening port
listening-port=3478

# Further ports that are open for communication
min-port=49152
max-port=65535

# Use fingerprint in TURN message
fingerprint

# Enable verbose logging
verbose

# Specify the user for the TURN authentification
user=coturnuser:verY-$ecret

# Enable long-term credential mechanism
lt-cred-mech

# TURN over TLS (turns)
tls-listening-port=5349

# SSL certificates
cert=/etc/coturn/certs/pairdrop.net.cert
pkey=/etc/coturn/certs/pairdrop.net.key

# Security settings
no-cli
no-multicast-peers
no-software-attribute
no-tlsv1
no-tlsv1_1
no-rfc5780
no-stun-backward-compatibility
response-origin-only-with-rfc5780
check-origin-consistency

and rtc_config.json:

{
  "sdpSemantics": "unified-plan",
  "iceServers": [
    {
      "urls": "stun:pairdrop.net:3478"
    },
    {
      "urls": "turns:pairdrop.net:5349",
      "username": "coturnuser",
      "credential": "verY-$ecret"
    }
  ]
}

This does now behaving similar to your TURN server and I cannot reproduce the errors anymore. Chromium based browser are throwing an error (as with your TURN server) but everything works perfectly.

Glad we could upgrade to turn over tls, thanks! 👍

schlagmichdoch commented 1 year ago

nginx/1.22.1 does have the module prebuilt

When using the docker tag stable we're already at nginx/1.24.0 so stream-realip-mod should be available https://hub.docker.com/_/nginx

xundeenergie commented 1 year ago

I need to look into it as it seems that the current turn configuration is not supported by chromium. Maybe I will need to implement TURN over TLS for it to work on Chromim base browsers.

Regarding your implementation, I just tested pairdrop.schuerz.at.

Chromium throws the following error:

Uncaught DOMException: Failed to construct 'RTCPeerConnection': 'stuns' is not one of the supported URL schemes 'stun', 'turn' or 'turns'.

While Firefox throws this warning:

WebRTC: STUNS is not yet supported.

I guess for now we can not support STUN over tls at all and I have not found any roadmap for neither of Firefox and Chromium

Oh... ok. I thought it works... I changed the configuration to stun now.

schlagmichdoch commented 1 year ago

does the separation of streams for coturn and pairdrop. but you need the options set_real_ip_from from the stream-realip-module. THAS is the problem.

If you find a way to provide this module in a nginx in a container, it could be a good idea, to include nginx in the docker-compose. Maybe letsencrypt would be also a good idea then... but it needs 2 domains. Yes.

I upgraded nginx to the stable version nginx/1.24.0 (official guide + sources).

Therefore, the stream-realip-module is now available but I still cannot set set_real_ip_from to retrieve the real client ip properly. Currently, all nginx variables $remote_addr, realip_remote_addr, $http_x_forwarded_for are undefined or point to nginx (127.0.0.1)

The docs (http://nginx.org/en/docs/stream/ngx_stream_realip_module.html) say:

The ngx_stream_realip_module module is used to change the client address and port to the ones sent in the PROXY protocol header (1.11.4). The PROXY protocol must be previously enabled by setting the proxy_protocol parameter in the listen directive. 

But that parameter only works when there is load balancer in front of nginx that sets the headers accordingly:

The example assumes that there is a load balancer in front of NGINX to handle all incoming HTTPS traffic, for example Amazon ELB. 

(https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/#proxy-protocol-for-a-tcp-connection-to-an-upstream)

Do you have any other idea how to retrieve the IP address without adding another host with a load balancer in front of NGINX ?

xundeenergie commented 1 year ago

I will try to deploy an extra vom with nginx selfcompiled... to figure this out.

but not this weekend... i'm very interested in resolving this problem!

schlagmichdoch commented 1 year ago

@xundeenergie How should we proceed from here?

I would like to merge a working version of this PR until you figure out how to do it with a self compiled nginx version. Is the current state working and ready to be merged?

xundeenergie commented 1 year ago

no, i'm not ready. have too much to do... :disappointed:

schlagmichdoch commented 11 months ago

@xundeenergie I suspect you still haven’t had any time for this, have you?

Could you maybe resolve this into a simple working version that has HTTPS on port 443 and TURNS on port 5349?

I would love to give admins a one line setup with a turn server included

xundeenergie commented 9 months ago

Hi!

Yes... that's right... have been on holidays and had a lot to do at work.

BUT...

i did a POC for a Matrix-installation @work and found out a lot about stun/turn.

And there is one point now open for me. For turn you can configure a long-term credential mechanics (lt-cred-mech) or a shared secret.

The first (lt-cred-mech) is less secure, because there is one username/password for all users of turn, and you can find it out easily in the web-browser-console. Better is the shared-secret, which creates credential for a short livetime for each session out of the shared-secret.

But pairdrop only supports the lt-cred-mech with username/password.

The danger of lt-cred-mech is a MITM-attack and someone can use your turn-server for own purposes to transmit or spread very bad stuff and additionally the attacker can use all your bandwidth from the turn-server with much extra costs for you.

So... it would be a gread idea, if you implement shared-secret for turn(s) to Pairdrop.

Just a first entrypoint to this topic: https://github.com/coturn/coturn/blob/master/README.turnserver and the option is --use-auth-secret instead of --lt-cred-mech

schlagmichdoch commented 1 week ago

Hey @xundeenergie,

I didn't mean to fully close this issue as this is still not resolved completely. The current default files do work with TURN over TLS which is great. Thanks again for your help!

I just recognized, that pairdrop.net did not work properly on a highly restricted network that I used, which is probably due to the fact, that the turnserver (over tls) is still running on port 5349. Probably, everything would work if I put the TURN server over TLS on port 443 as well which is, what I would like to do.

Coming back to your comment over here: https://github.com/schlagmichdoch/PairDrop/issues/105#issuecomment-1527262652

I have some more questions regarding the nginx config. I

Do you think it would be possible to configure nginx in a way that default and all connections over 443 are proxied to PairDrop while sth. like turn-server.pairdrop.net over 443 is proxied to the TURN server? I do not

Current config:

turnserver.conf:

# TURN server name and realm
realm=pairdrop.net
server-name=pairdrop

#listening-ip=0.0.0.0
external-ip=192.145.47.48

# Main listening port
listening-port=3478

# Further ports that are open for communication
min-port=49152
max-port=65535

# Use fingerprint in TURN message
fingerprint

# Enable verbose logging
# verbose

# Specify the user for the TURN authentification
user=qhyDYD7PmT1a:6uX4JSBdncNLmUmoGau97Ft

# Enable long-term credential mechanism
lt-cred-mech

# TURN over TLS (turns)
tls-listening-port=5349

# SSL certificates
cert=/etc/coturn/certs/pairdrop.net.cert
pkey=/etc/coturn/certs/pairdrop.net.key

# Security settings
no-cli
no-multicast-peers
no-software-attribute
no-tlsv1
no-tlsv1_1
no-rfc5780
no-stun-backward-compatibility
response-origin-only-with-rfc5780
check-origin-consistency

nginx.conf:

server {
    # Listen default port for http
    listen 80;

    # Server name for this config
    server_name pairdrop.net;

    # Force redirect to https
    rewrite ^ https://pairdrop.net$request_uri? permanent;
}

upstream node_backend {
    zone upstreams 64K;
    server 127.0.0.1:3000 max_fails=1 fail_timeout=2s;
    keepalive 2;
}

server {
    expires epoch;

    server_name pairdrop.net;

    add_header X-XSS-Protection "1; mode=block";
    # needs to be allowed for the firefox extension to work
    add_header X-Frame-Options "ALLOW";
    add_header Referrer-Policy "strict-origin";
    add_header Link "<$scheme://$http_host>; rel=\"canonical\"";

    # Include common ssl params
    include snippets/ssl.conf;

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    http2 on;

    ssl_certificate /etc/letsencrypt/live/pairdrop.net/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/pairdrop.net/privkey.pem; # managed by Certbot

    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    location / {
        root   /var/www/PairDrop/public/;
        index  index.html;
    }

    if ( $request_uri ~ "/index.html" ) {
        rewrite ^ / permanent;
    }

    location ~ ^/(config|server)$ {
        proxy_connect_timeout 800;
        proxy_set_header Connection "upgrade";
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header X-Forwarded-for $remote_addr;
        proxy_http_version 1.1;
        proxy_pass http://node_backend/$1$is_args$args;
        proxy_next_upstream error timeout http_500;
    }

    error_page 404 https://pairdrop.net/;
    error_page 405 =200 $uri;

    access_log off;
    error_log /var/log/nginx/pairdrop_err;
}

Your config (slightly edited to apply for pairdrop domain and ip):

stream {
    map $ssl_preread_server_name $name {
        turn-server.pairdrop.net           turn_server;
        default                 node_backend;
    }
    upstream node_backend {
        zone upstreams 64K;
        server 127.0.0.1:3000 max_fails=1 fail_timeout=2s;
        keepalive 2;
    }
    upstream turn_server {
        server 192.145.47.48:5349;
    }
    server {
        listen 443;
        listen 443 udp;
        listen [::]:443;
        listen [::]:443 udp;
        ssl_preread on;
        proxy_pass $name;
        proxy_buffer_size 10m;
    }
}

Merged version

What's the use of using a stream block? Couldn't I simply put the turn server in it's separate server block that listens to the subdomain only?

If I need to use the stream block: Would I put everything from the current nginx.conf in the stream block? How would I merge these lines from the current nginx.conf:

    listen [::]:443 ssl http2; # managed by Certbot
    listen 443 ssl http2; # managed by Certbot

with your

        listen 443;
        listen 443 udp;
        listen [::]:443;
        listen [::]:443 udp;
        ssl_preread on;

Probably my path blocks /config and /server can be merged. Done.

What's the reason you use:

        proxy_buffer_size 10m;

I will play around a little and try to get it to work. I'd appreciate any help! :)

schlagmichdoch commented 1 week ago

See also: https://github.com/coturn/coturn/issues/702#issuecomment-2197407987

Reading the complete thread I could answer some of the questions myself:

I'll try to make it work in two steps:

  1. Have a setup where:
    • PairDrop runs on port 443 on the default domain
    • STUNS/TURNS runs on port 443 on a subdomain
    • STUN/TURN runs on port 80 on the same subdomain
    • (The default domain on port 80 redirects to default domain port 443)
  2. Put this into the docker-compose environment
    • Try to forward the real user IPs to prevent all clients from seeing each other mutually