diff --git a/.dockerignore b/.dockerignore index 8125ff6..7451c5a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,6 +2,7 @@ !docker/coturn/alpine/ !docker/coturn/debian/ +!docker/coturn/rootfs/ !cmake/ !CMakeLists.txt diff --git a/docker/coturn/CHANGELOG.md b/docker/coturn/CHANGELOG.md index aecfe04..cb4182e 100644 --- a/docker/coturn/CHANGELOG.md +++ b/docker/coturn/CHANGELOG.md @@ -11,10 +11,15 @@ Coturn TURN server Docker image changelog - [Prometheus] support with [prometheus-client-c] 0.1.3: ([#754]) +### Improved + +- Use DNS requests to discover external IP address in `detect-external-ip` script ([#753]). + ### Fixed - Incorrect linking with [mongo-c-driver] on [Debian Linux] image. +[#753]: /../../pull/753 [#754]: /../../pull/754 diff --git a/docker/coturn/Makefile b/docker/coturn/Makefile index 99b46f2..3910411 100644 --- a/docker/coturn/Makefile +++ b/docker/coturn/Makefile @@ -176,6 +176,7 @@ docker.push: # [( [build=no] # | build=yes [DOCKERFILE=(debian|alpine)] # [ref=] )] +# [with=ipv6] test-docker-platforms = $(strip $(if $(call eq,$(platforms),),$(MAIN_PLATFORM),\ $(if $(call eq,$(platforms),@all),$(PLATFORMS),\ @@ -195,6 +196,7 @@ define test.docker.do @make docker.image DOCKERFILE=$(DOCKERFILE) \ no-cache=no tag=$(tag) platform=$(platform) ref=$(ref) ,) IMAGE=coturn/$(NAME):$(tag) PLATFORM=$(platform) \ + $(if $(call eq,$(with),ipv6),TEST_IPV6=1,) \ node_modules/.bin/bats \ --timing $(if $(call eq,$(CI),),--pretty,--formatter tap) \ tests/main.bats diff --git a/docker/coturn/README.md b/docker/coturn/README.md index 66c4a4b..32ae122 100644 --- a/docker/coturn/README.md +++ b/docker/coturn/README.md @@ -110,6 +110,14 @@ docker run -d --network=host coturn/coturn \ --relay-ip='$(detect-external-ip)' ``` +By default, [IPv4] address is discovered. In case you need an [IPv6] one, specify the `--ipv6` flag: +```bash +docker run -d --network=host coturn/coturn \ + -n --log-file=stdout \ + --external-ip='$(detect-external-ip --ipv6)' \ + --relay-ip='$(detect-external-ip --ipv6)' +``` + ### Persistence @@ -185,6 +193,8 @@ If you have any problems with or questions about this image, please contact us t [DockerHub]: https://hub.docker.com +[IPv4]: https://en.wikipedia.org/wiki/IPv4 +[IPv6]: https://en.wikipedia.org/wiki/IPv6 [RFC 5766 Section 6.2]: https://tools.ietf.org/html/rfc5766.html#section-6.2 [1]: http://alpinelinux.org diff --git a/docker/coturn/alpine/Dockerfile b/docker/coturn/alpine/Dockerfile index 5f2c513..a05916f 100644 --- a/docker/coturn/alpine/Dockerfile +++ b/docker/coturn/alpine/Dockerfile @@ -150,6 +150,7 @@ RUN mkdir -p /out/ \ && rm -f /out/etc/coturn/turnserver.conf.default # Install helper tools of Docker image. +COPY docker/coturn/rootfs/ /out/ COPY docker/coturn/alpine/rootfs/ /out/ RUN chmod +x /out/usr/local/bin/docker-entrypoint.sh \ /out/usr/local/bin/detect-external-ip.sh @@ -185,6 +186,9 @@ RUN apk update \ hiredis \ mongo-c-driver \ libmicrohttpd \ + # Install `dig` tool for `detect-external-ip.sh`. + && apk add --no-cache \ + bind-tools \ # Cleanup unnecessary stuff. && rm -rf /var/cache/apk/* diff --git a/docker/coturn/alpine/rootfs/usr/local/bin/detect-external-ip.sh b/docker/coturn/alpine/rootfs/usr/local/bin/detect-external-ip.sh deleted file mode 100644 index b4e4c57..0000000 --- a/docker/coturn/alpine/rootfs/usr/local/bin/detect-external-ip.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -if [ -z "$REAL_EXTERNAL_IP" ]; then - export REAL_EXTERNAL_IP="$(curl -4 https://icanhazip.com 2>/dev/null)" -fi - -exec echo "$REAL_EXTERNAL_IP" diff --git a/docker/coturn/debian/Dockerfile b/docker/coturn/debian/Dockerfile index f497a43..f859957 100644 --- a/docker/coturn/debian/Dockerfile +++ b/docker/coturn/debian/Dockerfile @@ -207,6 +207,7 @@ RUN mkdir -p /out/ \ && rm -f /out/etc/coturn/turnserver.conf.default # Install helper tools of Docker image. +COPY docker/coturn/rootfs/ /out/ COPY docker/coturn/debian/rootfs/ /out/ RUN chmod +x /out/usr/local/bin/docker-entrypoint.sh \ /out/usr/local/bin/detect-external-ip.sh @@ -246,6 +247,9 @@ RUN apt-get update \ libpq5 libmariadb3 libsqlite3-0 \ libhiredis0.14 \ libmicrohttpd12 \ + # Install `dig` tool for `detect-external-ip.sh`. + && apt-get install -y --no-install-recommends --no-install-suggests \ + dnsutils \ # Cleanup unnecessary stuff. && rm -rf /var/lib/apt/lists/* diff --git a/docker/coturn/debian/rootfs/usr/local/bin/detect-external-ip.sh b/docker/coturn/debian/rootfs/usr/local/bin/detect-external-ip.sh deleted file mode 100644 index b4e4c57..0000000 --- a/docker/coturn/debian/rootfs/usr/local/bin/detect-external-ip.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -if [ -z "$REAL_EXTERNAL_IP" ]; then - export REAL_EXTERNAL_IP="$(curl -4 https://icanhazip.com 2>/dev/null)" -fi - -exec echo "$REAL_EXTERNAL_IP" diff --git a/docker/coturn/rootfs/usr/local/bin/detect-external-ip.sh b/docker/coturn/rootfs/usr/local/bin/detect-external-ip.sh new file mode 100644 index 0000000..c019c2f --- /dev/null +++ b/docker/coturn/rootfs/usr/local/bin/detect-external-ip.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env sh +# shellcheck shell=dash + +#/ Use DNS to find out about the external IP of the running system. +#/ +#/ 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. +#/ +#/ Typically it is possible to make an HTTP request to a number of providers +#/ that offer the external IP in their response body (eg: ifconfig.me). However, +#/ why do a slow and heavy HTTP request, when DNS exists and is much faster? +#/ Well established providers such as OpenDNS or Google offer special hostnames +#/ that, when resolved, will actually return the IP address of the caller. +#/ +#/ https://unix.stackexchange.com/questions/22615/how-can-i-get-my-external-ip-address-in-a-shell-script/81699#81699 +#/ +#/ +#/ Arguments +#/ --------- +#/ +#/ --ipv4 +#/ +#/ Find the external IPv4 address. +#/ Optional. Default: Enabled. +#/ +#/ --ipv6 +#/ +#/ Find the external IPv6 address. +#/ 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 + + + +# Shortcut: REAL_EXTERNAL_IP +# ========================== + +if [ -n "${REAL_EXTERNAL_IP:-}" ]; then + echo "$REAL_EXTERNAL_IP" + exit 0 +fi + + + +# Parse call arguments +# ==================== + +CFG_IPV4="true" + +while [ $# -gt 0 ]; do + case "${1-}" in + --ipv4) CFG_IPV4="true" ;; + --ipv6) CFG_IPV4="false" ;; + *) + echo "Invalid argument: '${1-}'" >&2 + exit 1 + ;; + esac + shift +done + + + +# Discover the external IP address +# ================================ + +if [ "$CFG_IPV4" = "true" ]; then + COMMANDS='dig @resolver1.opendns.com myip.opendns.com A -4 +short + dig @ns1.google.com o-o.myaddr.l.google.com TXT -4 +short | tr -d \" + dig @1.1.1.1 whoami.cloudflare TXT CH -4 +short | tr -d \" + dig @ns1-1.akamaitech.net whoami.akamai.net A -4 +short' + + 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}$' + } +else + COMMANDS='dig @resolver1.opendns.com myip.opendns.com AAAA -6 +short + dig @ns1.google.com o-o.myaddr.l.google.com TXT -6 +short | tr -d \" + dig @2606:4700:4700::1111 whoami.cloudflare TXT CH -6 +short | tr -d \"' + + is_valid_ip() { + # Check if the input looks like an IPv6 address. + # It's almost impossible to check the IPv6 representation because it + # varies wildly, so just check that there are at least 2 colons. + [ "$(echo "$1" | awk -F':' '{print NF-1}')" -ge 2 ] + } +fi + +IFS="$(printf '\nx')" && IFS="${IFS%x}" +for COMMAND in $COMMANDS; do + if IP="$(eval "$COMMAND")" && is_valid_ip "$IP"; then + printf '%s' "$IP" + exit 0 + fi +done + +echo "[$0] All providers failed" >&2 +exit 1 diff --git a/docker/coturn/tests/main.bats b/docker/coturn/tests/main.bats index 73e238f..e093a1a 100644 --- a/docker/coturn/tests/main.bats +++ b/docker/coturn/tests/main.bats @@ -125,3 +125,78 @@ [ "$status" -eq 0 ] [ ! "$output" = '' ] } + + +@test "detect-external-ip is present" { + run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ + 'which detect-external-ip' + [ "$status" -eq 0 ] +} + +@test "detect-external-ip runs ok" { + run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ + 'detect-external-ip' + [ "$status" -eq 0 ] +} + +@test "detect-external-ip returns valid IPv4" { + run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ + 'detect-external-ip --ipv4' + [ "$status" -eq 0 ] + + run validate_ipv4 "$output" + [ "$status" -eq 0 ] +} + +@test "detect-external-ip returns valid IPv6" { + [ -z "$TEST_IPV6" ] && skip + + run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ + 'detect-external-ip --ipv6' + [ "$status" -eq 0 ] + + run validate_ipv6 "$output" + [ "$status" -eq 0 ] +} + +@test "detect-external-ip returns IPv4 by default" { + run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ + 'detect-external-ip --ipv4' + [ "$status" -eq 0 ] + + run validate_ipv4 "$output" + [ "$status" -eq 0 ] +} + + +# +# Helpers +# + +# Tests the IP address to be a valid IPv4 address. +function validate_ipv4() { + local ip=$1 + local stat=1 + + if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + OIFS=$IFS + IFS='.' + ip=($ip) + IFS=$OIFS + [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ + && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] + stat=$? + fi + return $stat +} + +# Tests the IP address to be a valid IPv6 address. +function validate_ipv6() { + local ip=$1 + local stat=1 + + if [[ $ip =~ ^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$ ]]; then + stat=0 + fi + return $stat +}