From 94527f9fa1cb297b5b357ee19c0539406a03fe9d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 7 Aug 2024 04:00:50 +0100 Subject: [PATCH] fix(gateway): always masquerade for docker-deployed gateways (#6169) Without masquerading, packets sent by the gateway through the TUN interface use the wrong source address (the TUN device's address) instead of the gateway's actual network interface. We set this env variable in all our uses of the gateway, thus we might as well remove it and always perform unconditionally. --------- Signed-off-by: Thomas Eizinger Co-authored-by: Reactor Scram --- docker-compose.yml | 2 +- .../apps/web/lib/web/live/sites/new_token.ex | 2 +- rust/Dockerfile | 49 +++++++++++++------ rust/docker-init-gateway.sh | 19 +++++++ rust/docker-init-relay.sh | 24 +++++++++ rust/docker-init.sh | 29 ----------- terraform/modules/aws/gateway/main.tf | 4 -- .../app/kb/automate/docker-compose/readme.mdx | 4 -- website/src/components/Changelog/Gateway.tsx | 8 +++ 9 files changed, 87 insertions(+), 54 deletions(-) create mode 100755 rust/docker-init-gateway.sh create mode 100755 rust/docker-init-relay.sh diff --git a/docker-compose.yml b/docker-compose.yml index 7d59f80c8..8ad5d6652 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -344,7 +344,7 @@ services: environment: FIREZONE_TOKEN: ".SFMyNTY.g2gDaANtAAAAJGM4OWJjYzhjLTkzOTItNGRhZS1hNDBkLTg4OGFlZjZkMjhlMG0AAAAkMjI3NDU2MGItZTk3Yi00NWU0LThiMzQtNjc5Yzc2MTdlOThkbQAAADhPMDJMN1VTMkozVklOT01QUjlKNklMODhRSVFQNlVPOEFRVk82VTVJUEwwVkpDMjJKR0gwPT09PW4GAI0Qi02QAWIAAVGA.emd2izXZdGngSMDruiTNgwRYbNtsVLYf_fouNyoKv8Q" RUST_LOG: ${RUST_LOG:-phoenix_channel=trace,firezone_gateway=trace,wire=trace,connlib_gateway_shared=trace,firezone_tunnel=trace,connlib_shared=trace,phoenix_channel=debug,boringtun=debug,snownet=debug,str0m=debug,info} - FIREZONE_ENABLE_MASQUERADE: 1 + FIREZONE_ENABLE_MASQUERADE: 1 # FIXME: NOOP in latest version. Remove after next release. FIREZONE_API_URL: ws://api:8081 FIREZONE_ID: 4694E56C-7643-4A15-9DF3-638E5B05F570 build: diff --git a/elixir/apps/web/lib/web/live/sites/new_token.ex b/elixir/apps/web/lib/web/live/sites/new_token.ex index 8d1e6b4fc..80267353c 100644 --- a/elixir/apps/web/lib/web/live/sites/new_token.ex +++ b/elixir/apps/web/lib/web/live/sites/new_token.ex @@ -310,7 +310,7 @@ defmodule Web.Sites.NewToken do "--sysctl net.ipv6.conf.all.forwarding=1", "--sysctl net.ipv6.conf.default.forwarding=1", "--device=\"/dev/net/tun:/dev/net/tun\"", - Enum.map(env ++ [{"FIREZONE_ENABLE_MASQUERADE", "1"}], fn {key, value} -> + Enum.map(env, fn {key, value} -> "--env #{key}=\"#{value}\"" end), "--env FIREZONE_NAME=$(hostname)", diff --git a/rust/Dockerfile b/rust/Dockerfile index 73fcd5716..f3eaad45a 100644 --- a/rust/Dockerfile +++ b/rust/Dockerfile @@ -3,9 +3,11 @@ ARG RUST_VERSION="1.80" ARG ALPINE_VERSION="3.20" ARG CARGO_CHEF_VERSION="0.1.67" +ARG PACKAGE + # This image is used to prepare Cargo Chef which is used to cache dependencies # Keep the Rust version synced with `rust-toolchain.toml` -FROM rust:${RUST_VERSION}-alpine${ALPINE_VERSION} as chef +FROM rust:${RUST_VERSION}-alpine${ALPINE_VERSION} AS chef ARG CARGO_CHEF_VERSION RUN set -xe \ @@ -23,29 +25,27 @@ WORKDIR /build # Create a cache recipe for dependencies, which allows # to leverage Docker layer caching in a later build stage -FROM chef as planner +FROM chef AS planner COPY . . RUN cargo chef prepare --recipe-path recipe.json # Build dependencies and application application -FROM chef as builder +FROM chef AS builder COPY --from=planner /build/recipe.json . -ARG PACKAGE RUN set -xe \ && cargo chef cook --recipe-path recipe.json --bin ${PACKAGE} COPY . . ARG TARGET -ARG PACKAGE RUN cargo build -p ${PACKAGE} $([ -n "${TARGET}" ] && "--target ${TARGET}") -# Image which is used to run the application binary -FROM alpine:${ALPINE_VERSION} AS runtime +# Base image which is used to run the application binary +FROM alpine:${ALPINE_VERSION} AS runtime_base # Important! Update this no-op ENV variable when this Dockerfile # is updated with the current date. It will force refresh of all @@ -59,28 +59,47 @@ ENV REFRESHED_AT=2023-10-23 \ WORKDIR /bin -## curl is needed by the entrypoint script +# Gateway specific runtime base image +FROM runtime_base AS runtime_firezone-gateway +## iptables are needed only by gateway for masquerading +RUN apk add --no-cache iptables ip6tables +COPY ./docker-init-gateway.sh ./docker-init.sh + +# Relay specific runtime base image +FROM runtime_base AS runtime_firezone-relay +## curl is needed by the entrypoint script for the relay RUN set -xe \ && apk add --no-cache curl +COPY ./docker-init-relay.sh ./docker-init.sh -COPY ./docker-init.sh . +# Headless-client specific runtime base image +FROM runtime_base AS runtime_firezone-headless-client +COPY ./docker-init.sh ./docker-init.sh -## iptables are needed only by gateway for masquerading -ARG PACKAGE +# HTTP test server specific runtime base image +FROM runtime_base AS runtime_http-test-server +COPY ./docker-init.sh ./docker-init.sh + +# snownet-tests specific runtime base image +FROM runtime_base AS runtime_snownet-tests RUN set -xe \ - && \[ "${PACKAGE}" = "firezone-gateway" ] && apk add --no-cache iptables ip6tables || true + && apk add --no-cache curl +COPY ./docker-init.sh ./docker-init.sh +# Funnel package specific base image back into `runtime` +FROM runtime_${PACKAGE} AS runtime + +ARG PACKAGE ENTRYPOINT ["docker-init.sh"] - ENV PACKAGE=${PACKAGE} CMD $PACKAGE # used as a base for dev and test -FROM runtime as test +FROM runtime AS test RUN set -xe \ - && apk add --no-cache iperf3 bind-tools iproute2 jq procps + && apk add --no-cache curl iperf3 bind-tools iproute2 jq procps # used for local development FROM test AS dev diff --git a/rust/docker-init-gateway.sh b/rust/docker-init-gateway.sh new file mode 100755 index 000000000..36687fed8 --- /dev/null +++ b/rust/docker-init-gateway.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if [ -f "${FIREZONE_TOKEN}" ]; then + FIREZONE_TOKEN="$(cat "${FIREZONE_TOKEN}")" + export FIREZONE_TOKEN +fi + +IFACE="tun-firezone" +# Enable masquerading for our TUN interface +iptables -C FORWARD -i $IFACE -j ACCEPT >/dev/null 2>&1 || iptables -A FORWARD -i $IFACE -j ACCEPT +iptables -C FORWARD -o $IFACE -j ACCEPT >/dev/null 2>&1 || iptables -A FORWARD -o $IFACE -j ACCEPT +iptables -t nat -C POSTROUTING -o e+ -j MASQUERADE >/dev/null 2>&1 || iptables -t nat -A POSTROUTING -o e+ -j MASQUERADE +iptables -t nat -C POSTROUTING -o w+ -j MASQUERADE >/dev/null 2>&1 || iptables -t nat -A POSTROUTING -o w+ -j MASQUERADE +ip6tables -C FORWARD -i $IFACE -j ACCEPT >/dev/null 2>&1 || ip6tables -A FORWARD -i $IFACE -j ACCEPT +ip6tables -C FORWARD -o $IFACE -j ACCEPT >/dev/null 2>&1 || ip6tables -A FORWARD -o $IFACE -j ACCEPT +ip6tables -t nat -C POSTROUTING -o e+ -j MASQUERADE >/dev/null 2>&1 || ip6tables -t nat -A POSTROUTING -o e+ -j MASQUERADE +ip6tables -t nat -C POSTROUTING -o w+ -j MASQUERADE >/dev/null 2>&1 || ip6tables -t nat -A POSTROUTING -o w+ -j MASQUERADE + +exec "$@" diff --git a/rust/docker-init-relay.sh b/rust/docker-init-relay.sh new file mode 100755 index 000000000..ffe2cd62b --- /dev/null +++ b/rust/docker-init-relay.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +if [ -f "${FIREZONE_TOKEN}" ]; then + FIREZONE_TOKEN="$(cat "${FIREZONE_TOKEN}")" + export FIREZONE_TOKEN +fi + +if [ "${LISTEN_ADDRESS_DISCOVERY_METHOD}" = "gce_metadata" ]; then + echo "Using GCE metadata to discover listen address" + + if [ "${PUBLIC_IP4_ADDR}" = "" ]; then + public_ip4=$(curl "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip" -H "Metadata-Flavor: Google" -s) + export PUBLIC_IP4_ADDR="${public_ip4}" + echo "Discovered PUBLIC_IP4_ADDR: ${PUBLIC_IP4_ADDR}" + fi + + if [ "${PUBLIC_IP6_ADDR}" = "" ]; then + public_ip6=$(curl "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ipv6s" -H "Metadata-Flavor: Google" -s) + export PUBLIC_IP6_ADDR="${public_ip6}" + echo "Discovered PUBLIC_IP6_ADDR: ${PUBLIC_IP6_ADDR}" + fi +fi + +exec "$@" diff --git a/rust/docker-init.sh b/rust/docker-init.sh index 816c10921..6209cf404 100755 --- a/rust/docker-init.sh +++ b/rust/docker-init.sh @@ -5,33 +5,4 @@ if [ -f "${FIREZONE_TOKEN}" ]; then export FIREZONE_TOKEN fi -if [ "${FIREZONE_ENABLE_MASQUERADE}" = "1" ]; then - IFACE="tun-firezone" - # Enable masquerading for ethernet and wireless interfaces - iptables -C FORWARD -i $IFACE -j ACCEPT >/dev/null 2>&1 || iptables -A FORWARD -i $IFACE -j ACCEPT - iptables -C FORWARD -o $IFACE -j ACCEPT >/dev/null 2>&1 || iptables -A FORWARD -o $IFACE -j ACCEPT - iptables -t nat -C POSTROUTING -o e+ -j MASQUERADE >/dev/null 2>&1 || iptables -t nat -A POSTROUTING -o e+ -j MASQUERADE - iptables -t nat -C POSTROUTING -o w+ -j MASQUERADE >/dev/null 2>&1 || iptables -t nat -A POSTROUTING -o w+ -j MASQUERADE - ip6tables -C FORWARD -i $IFACE -j ACCEPT >/dev/null 2>&1 || ip6tables -A FORWARD -i $IFACE -j ACCEPT - ip6tables -C FORWARD -o $IFACE -j ACCEPT >/dev/null 2>&1 || ip6tables -A FORWARD -o $IFACE -j ACCEPT - ip6tables -t nat -C POSTROUTING -o e+ -j MASQUERADE >/dev/null 2>&1 || ip6tables -t nat -A POSTROUTING -o e+ -j MASQUERADE - ip6tables -t nat -C POSTROUTING -o w+ -j MASQUERADE >/dev/null 2>&1 || ip6tables -t nat -A POSTROUTING -o w+ -j MASQUERADE -fi - -if [ "${LISTEN_ADDRESS_DISCOVERY_METHOD}" = "gce_metadata" ]; then - echo "Using GCE metadata to discover listen address" - - if [ "${PUBLIC_IP4_ADDR}" = "" ]; then - public_ip4=$(curl "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip" -H "Metadata-Flavor: Google" -s) - export PUBLIC_IP4_ADDR="${public_ip4}" - echo "Discovered PUBLIC_IP4_ADDR: ${PUBLIC_IP4_ADDR}" - fi - - if [ "${PUBLIC_IP6_ADDR}" = "" ]; then - public_ip6=$(curl "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ipv6s" -H "Metadata-Flavor: Google" -s) - export PUBLIC_IP6_ADDR="${public_ip6}" - echo "Discovered PUBLIC_IP6_ADDR: ${PUBLIC_IP6_ADDR}" - fi -fi - exec "$@" diff --git a/terraform/modules/aws/gateway/main.tf b/terraform/modules/aws/gateway/main.tf index 1e3e25307..29f39a695 100644 --- a/terraform/modules/aws/gateway/main.tf +++ b/terraform/modules/aws/gateway/main.tf @@ -18,10 +18,6 @@ locals { { name = "FIREZONE_API_URL" value = var.api_url - }, - { - name = "FIREZONE_ENABLE_MASQUERADE" - value = "1" } ], var.application_environment_variables) } diff --git a/website/src/app/kb/automate/docker-compose/readme.mdx b/website/src/app/kb/automate/docker-compose/readme.mdx index acbe32932..9c7601a9e 100644 --- a/website/src/app/kb/automate/docker-compose/readme.mdx +++ b/website/src/app/kb/automate/docker-compose/readme.mdx @@ -34,10 +34,6 @@ services: # See https://docs.rs/env_logger/latest/env_logger/ for more information. - RUST_LOG=str0m=warn,info - # Enable or disable masquerading. Default enabled. Disabling this can prevent - # the Gateway from reaching other hosts in your subnet or the internet. - # - FIREZONE_ENABLE_MASQUERADE=1 - # Human-friendly name to use for this Gateway in the admin portal. # $(hostname) is used by default if not set. # - FIREZONE_NAME= diff --git a/website/src/components/Changelog/Gateway.tsx b/website/src/components/Changelog/Gateway.tsx index 845819664..798dca263 100644 --- a/website/src/components/Changelog/Gateway.tsx +++ b/website/src/components/Changelog/Gateway.tsx @@ -8,6 +8,14 @@ export default function Gateway() { return ( + +
    +
  • + Removes `FIREZONE_ENABLE_MASQUERADE` env variable. + Masquerading is now always enabled unconditionally. +
  • +
+