From d47a1c9ad7be4fe3f897c92e38828f1c530f1d1d Mon Sep 17 00:00:00 2001 From: cruizba Date: Mon, 12 Jul 2021 15:55:31 +0200 Subject: [PATCH] Add additional parameter 'COTURN_INTERNAL_RELAY=' to use Public or Internal IP. Defaults to 'false', which respect previous OpenVidu default deployment configuration --- .../docker-compose.yml | 2 + .../openvidu-server-pro/docker-compose.yml | 2 + .../docker/openvidu-coturn/Dockerfile | 9 +- .../openvidu-coturn/detect-external-ip.sh | 9 +- .../openvidu-coturn/discover-internal-ip.sh | 105 ++++++++++++++++++ .../server/config/OpenviduConfig.java | 10 ++ .../kurento/endpoint/MediaEndpoint.java | 14 ++- ...itional-spring-configuration-metadata.json | 12 +- .../src/main/resources/application.properties | 1 + 9 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 openvidu-server/docker/openvidu-coturn/discover-internal-ip.sh diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml index c514d14e..4a298799 100644 --- a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml +++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml @@ -45,6 +45,7 @@ services: - COTURN_REDIS_IP=127.0.0.1 - COTURN_REDIS_PASSWORD=${OPENVIDU_SECRET} - COTURN_IP=${COTURN_IP:-auto-ipv4} + - COTURN_INTERNAL_RELAY=${COTURN_INTERNAL_RELAY:-false} - OPENVIDU_PRO_CLUSTER=true - OPENVIDU_PRO_KIBANA_HOST=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana} - OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200} @@ -93,6 +94,7 @@ services: - REDIS_IP=127.0.0.1 - DB_NAME=0 - DB_PASSWORD=${OPENVIDU_SECRET} + - COTURN_INTERNAL_RELAY=${COTURN_INTERNAL_RELAY:-false} command: - --log-file=stdout - --external-ip=$$(detect-external-ip) diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.yml b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.yml index 9d7f5ad3..80760be3 100644 --- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.yml +++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.yml @@ -42,6 +42,7 @@ services: - COTURN_REDIS_IP=127.0.0.1 - COTURN_REDIS_PASSWORD=${OPENVIDU_SECRET} - COTURN_IP=${COTURN_IP:-auto-ipv4} + - COTURN_INTERNAL_RELAY=${COTURN_INTERNAL_RELAY:-false} - OPENVIDU_PRO_CLUSTER=true - OPENVIDU_PRO_KIBANA_HOST=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana} - OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200} @@ -69,6 +70,7 @@ services: - REDIS_IP=127.0.0.1 - DB_NAME=0 - DB_PASSWORD=${OPENVIDU_SECRET} + - COTURN_INTERNAL_RELAY=${COTURN_INTERNAL_RELAY:-false} command: - --log-file=stdout - --external-ip=$$(detect-external-ip) diff --git a/openvidu-server/docker/openvidu-coturn/Dockerfile b/openvidu-server/docker/openvidu-coturn/Dockerfile index 878c412d..c77b4e0c 100644 --- a/openvidu-server/docker/openvidu-coturn/Dockerfile +++ b/openvidu-server/docker/openvidu-coturn/Dockerfile @@ -2,16 +2,19 @@ FROM coturn/coturn:4.5.2-alpine USER root -RUN apk add --no-cache bind-tools +RUN apk add --no-cache bind-tools grep # Override detect-external-ip.sh script COPY ./detect-external-ip.sh /usr/local/bin/detect-external-ip.sh COPY ./docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +COPY ./discover-internal-ip.sh /usr/local/bin/discover-internal-ip.sh -RUN chmod +x /usr/local/bin/detect-external-ip.sh /usr/local/bin/docker-entrypoint.sh && \ +RUN chmod +x /usr/local/bin/detect-external-ip.sh \ + /usr/local/bin/docker-entrypoint.sh \ + /usr/local/bin/discover-internal-ip.sh && \ chown -R nobody:nogroup /var/lib/coturn/ && \ touch /turnserver.conf && chown nobody:nogroup /turnserver.conf USER nobody:nogroup ENTRYPOINT ["docker-entrypoint.sh"] -CMD ["--log-file=stdout", "--external-ip=$(detect-external-ip)"] \ No newline at end of file +CMD ["--log-file=stdout", "--external-ip=$(detect-external-ip)"] diff --git a/openvidu-server/docker/openvidu-coturn/detect-external-ip.sh b/openvidu-server/docker/openvidu-coturn/detect-external-ip.sh index ce1feec7..fccbeae7 100644 --- a/openvidu-server/docker/openvidu-coturn/detect-external-ip.sh +++ b/openvidu-server/docker/openvidu-coturn/detect-external-ip.sh @@ -52,7 +52,12 @@ if [ -n "${REAL_EXTERNAL_IP:-}" ]; then exit 0 fi - +# Shortcut to use internal IP as external IP: COTURN_INTERNAL_RELAY +# ========================== +if [ -n "${COTURN_INTERNAL_RELAY}" ] && [ "${COTURN_INTERNAL_RELAY}" = "true" ]; then + discover-internal-ip.sh + exit 0 +fi # Parse call arguments # ==================== @@ -112,4 +117,4 @@ if [ $? -eq 100 ]; then else echo "[$0] All providers failed" >&2 exit 1 -fi \ No newline at end of file +fi diff --git a/openvidu-server/docker/openvidu-coturn/discover-internal-ip.sh b/openvidu-server/docker/openvidu-coturn/discover-internal-ip.sh new file mode 100644 index 00000000..c010dba2 --- /dev/null +++ b/openvidu-server/docker/openvidu-coturn/discover-internal-ip.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env sh +# shellcheck shell=dash + +#/ Use the routing table to find the default internal IP for outgoing packets. +#/ +#/ This script is useful when running from a machine that sits behind a NAT. +#/ Due to how NAT works, machines behind it belong to an internal or private +#/ subnet, with a different address space than the external or public side. +#/ +#/ Any given machine might have multiple network interfaces, but the most +#/ common scenario is that only one is actually used for communications with the +#/ outside world, handling traffic that comes from the external NAT side. +#/ +#/ This script queries the system's IP routing tables for a dummy external IP, +#/ which has the effect of providing us with the IP of the network interface +#/ that would have been used for communicating with that address. +#/ +#/ Arguments +#/ --------- +#/ +#/ --default +#/ +#/ Find the internal IP address of the default IP route gateway. +#/ Optional. Default: Enabled. +#/ +#/ --name +#/ +#/ Find the internal IP address of the specified IP route gateway. +#/ Optional. Default: Disabled. + + + +# Shell setup +# =========== + +# Shell options for strict error checking. +for OPTION in errexit errtrace pipefail nounset; do + set -o | grep -wq "$OPTION" && set -o "$OPTION" +done + +# Trace all commands (to stderr). +#set -o xtrace + + + +# Parse arguments +# =============== + +CFG_DEFAULT="true" +CFG_NAME="" + +while [ $# -gt 0 ]; do + case "${1-}" in + --default) + CFG_DEFAULT="true" + ;; + --name) + if [ -n "${2-}" ]; then + CFG_NAME="$2" + shift + else + echo "[$0] ERROR: --name expects " >&2 + exit 1 + fi + ;; + *) + echo "[$0] Invalid argument: '${1-}'" >&2 + exit 1 + ;; + esac + shift +done + +# Ensure coherent settings... + +if [ -n "$CFG_NAME" ]; then + CFG_DEFAULT="false" +fi + + + +# Discover internal IP address +# ============================ + +if [ "$CFG_DEFAULT" = "true" ]; then + # Get main local IP address from the default external route (Internet gateway). + # Uses "1.0.0.0" as the target address, but any other external IP would work. + COMMAND='ip -4 -oneline route get 1.0.0.0 | grep -Po "src \K(\d\.?)+"' +else + COMMAND="ip -4 -oneline address show dev '$CFG_NAME' | grep -Po 'inet \K(\d\.?)+'" +fi + +is_valid_ip() { + # Check if the input looks like an IPv4 address. + # Doesn't check if the actual values are valid; assumes they are. + echo "$1" | grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$' +} + +if IP="$(eval "$COMMAND")" && is_valid_ip "$IP"; then + printf '%s' "$IP" + exit 0 +fi + +echo "[$0] Discovery failed" >&2 +exit 1 diff --git a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java index 736e8d3d..60477f4a 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java +++ b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java @@ -153,6 +153,9 @@ public class OpenviduConfig { private String coturnRedisIp; + // If true, coturn relay ips will come with the private IP of the machine + private boolean coturnInternalRelay; + private boolean openviduWebhookEnabled; private String openviduWebhookEndpoint; @@ -213,6 +216,10 @@ public class OpenviduConfig { return this.coturnRedisPassword; } + public boolean isCoturnUsingInternalRelay() { + return this.coturnInternalRelay; + } + public List getKmsUris() { return kmsUrisList; } @@ -522,6 +529,9 @@ public class OpenviduConfig { coturnRedisConnectTimeout = getValue("COTURN_REDIS_CONNECT_TIMEOUT"); + // If true, coturn is using private IPs as relay IPs to enable relay connections pass through internal network + coturnInternalRelay = asBoolean("COTURN_INTERNAL_RELAY"); + openviduSecret = asNonEmptyAlphanumericString("OPENVIDU_SECRET", "Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens (\"-\") and underscores (\"_\")"); diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java index daa9f050..dafb9ef8 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java @@ -605,11 +605,19 @@ public abstract class MediaEndpoint { // Resend old public IP next to the new one if (candidateParser.isType(IceCandidateType.srflx)) { - // Send candidate with private ip IceCandidate candidateMinPriority = new IceCandidate(candidate.getCandidate(), candidate.getSdpMid(), candidate.getSdpMLineIndex()); - candidateParser.setIp(originalIp); - candidateParser.setMinPriority(); // Set min priority for original public IP + if (openviduConfig.isCoturnUsingInternalRelay()) { + // If coturn is using internal relay, there should be candidates with the private IP + // to relay on the internal network + candidateParser.setIp(kurentoPrivateIp); // Send candidate with private ip + } else { + // If coturn is configured using public IP as relay, candidates with the original IP + // and the new one should be sent + // to relay using the public internet + candidateParser.setIp(originalIp); // Send candidate with original IP + } + candidateParser.setMinPriority(); // Set min priority for this candidate candidateMinPriority.setCandidate(candidateParser.toString()); sendCandidate(senderPublicId, candidateMinPriority); } diff --git a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 04a0f0f6..6ba031d8 100644 --- a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -205,6 +205,16 @@ "type": "java.lang.String", "description": "Which kind of certificate shall be used by OpenVidu in production mode [selfsigned,letsencrypt,owncert]" }, + { + "name": "MEDIA_NODES_PUBLIC_IPS", + "type": "java.lang.String", + "description": "Additional IP to be returned for each media node in ICE candidates. The format consists on a list of pairs with private ips and public ips [::...]" + }, + { + "name": "COTURN_INTERNAL_RELAY", + "type": "java.lang.String", + "description": "If true, coturn is returning the private IP on relayed candidates. This can be useful to know which candidates must be sent when MEDIA_NODES_PUBLIC_IPS is defined" + }, { "name": "jsonRpcClientWebSocket.reconnectionDelay", "type": "java.lang.Integer", @@ -248,4 +258,4 @@ "defaultValue": 9223372036854775807 } ] -} \ No newline at end of file +} diff --git a/openvidu-server/src/main/resources/application.properties b/openvidu-server/src/main/resources/application.properties index 3e3280db..0166449e 100644 --- a/openvidu-server/src/main/resources/application.properties +++ b/openvidu-server/src/main/resources/application.properties @@ -52,4 +52,5 @@ COTURN_REDIS_IP=127.0.0.1 COTURN_REDIS_DBNAME=0 COTURN_REDIS_PASSWORD=turn COTURN_REDIS_CONNECT_TIMEOUT=30 +COTURN_INTERNAL_RELAY=false MEDIA_NODES_PUBLIC_IPS=[]