diff --git a/apps/fz_common/lib/fz_net.ex b/apps/fz_common/lib/fz_net.ex index 45839887b..fa8c57aa8 100644 --- a/apps/fz_common/lib/fz_net.ex +++ b/apps/fz_common/lib/fz_net.ex @@ -65,4 +65,16 @@ defmodule FzCommon.FzNet do def valid_hostname?(hostname) when is_binary(hostname) do String.match?(hostname, @host_regex) end + + def to_complete_url(str) when is_binary(str) do + case URI.new(str) do + {:ok, %{host: nil, scheme: nil}} -> + {:ok, "https://" <> str} + + {:ok, _} -> + {:ok, str} + + err -> err + end + end end diff --git a/apps/fz_common/test/fz_net_test.exs b/apps/fz_common/test/fz_net_test.exs index 28fbdf750..bd46bd072 100644 --- a/apps/fz_common/test/fz_net_test.exs +++ b/apps/fz_common/test/fz_net_test.exs @@ -110,4 +110,32 @@ defmodule FzCommon.FzNetTest do assert "fd00:3::1" == FzNet.standardized_inet("fd00:3:0000::1") end end + + describe "to_complete_url/1" do + @tag cases: [ + {"foobar", "https://foobar"}, + {"google.com", "https://google.com"}, + {"127.0.0.1", "https://127.0.0.1"}, + {"8.8.8.8", "https://8.8.8.8"}, + {"https://[fd00::1]", "https://[fd00::1]"}, + {"http://foobar", "http://foobar"}, + {"https://foobar", "https://foobar"} + ] + test "parses valid string URIs", %{cases: cases} do + for {subject, expected} <- cases do + assert {:ok, ^expected} = FzNet.to_complete_url(subject) + end + end + + @tag cases: [ + "<", + "{", + "[" + ] + test "returns {:error, _} for invalid URIs", %{cases: cases} do + for subject <- cases do + assert {:error, _} = FzNet.to_complete_url(subject) + end + end + end end diff --git a/config/runtime.exs b/config/runtime.exs index 37d0459c9..5d05b657e 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -5,10 +5,18 @@ import Config -alias FzCommon.{CLI, FzInteger, FzString, FzKernelVersion} +alias FzCommon.{CLI, FzInteger, FzString, FzKernelVersion, FzNet} + +# external_url is important, so fail fast here if we can't parse +{:ok, external_url} = + if config_env() == :prod do + System.fetch_env!("EXTERNAL_URL") + |> FzNet.to_complete_url() + else + System.get_env("EXTERNAL_URL", "https://localhost") + |> FzNet.to_complete_url() + end -# external_url is important -external_url = System.get_env("EXTERNAL_URL", "https://localhost") config :fz_http, :external_url, external_url %{host: host, path: path, port: port, scheme: scheme} = URI.parse(external_url) diff --git a/docker-compose.desktop.yml b/docker-compose.desktop.yml new file mode 100644 index 000000000..0004389e4 --- /dev/null +++ b/docker-compose.desktop.yml @@ -0,0 +1,90 @@ +# Example compose file for a running a local Firezone instance on +# macOS or Windows. +# +# Note: This file is meant to serve as a template. Please modify it +# according to your needs. Read more about Docker Compose: +# +# https://docs.docker.com/compose/compose-file/ +# +# +x-deploy: &default-deploy + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + window: 120s + update_config: + order: start-first + +version: '3.7' + +services: + caddy: + image: caddy:2 + volumes: + - ${FZ_INSTALL_DIR:-.}/caddy:/data/caddy + ports: + - 80:80 + - 443:443 + # See Caddy's documentation for customizing the Caddyfile + # https://caddyserver.com/docs/quick-starts/reverse-proxy + command: + - /bin/sh + - -c + - | + cat < /etc/caddy/Caddyfile && caddy run --config /etc/caddy/Caddyfile + + https:// { + log + reverse_proxy * firezone:13000 + tls internal { + on_demand + } + } + EOF + deploy: + <<: *default-deploy + + firezone: + image: firezone/firezone + ports: + - 51820:51820/udp + env_file: + # This should contain a list of env vars for configuring Firezone. + # See https://docs.firezone.dev/reference/env-vars for more info. + - ${FZ_INSTALL_DIR:-.}/.env + volumes: + # IMPORTANT: Persists WireGuard private key and other data. If + # /var/firezone/private_key exists when Firezone starts, it is + # used as the WireGuard private. Otherwise, one is generated. + - ${FZ_INSTALL_DIR:-.}/firezone:/var/firezone + cap_add: + # Needed for WireGuard and firewall support. + - NET_ADMIN + - SYS_MODULE + sysctls: + # Needed for masquerading and NAT. + - net.ipv6.conf.all.disable_ipv6=0 + - net.ipv4.ip_forward=1 + - net.ipv6.conf.all.forwarding=1 + depends_on: + - postgres + deploy: + <<: *default-deploy + + postgres: + image: postgres:15 + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + POSTGRES_DB: ${DATABASE_NAME:-firezone} + POSTGRES_USER: ${DATABASE_USER:-postgres} + POSTGRES_PASSWORD: ${DATABASE_PASSWORD:?err} + deploy: + <<: *default-deploy + update_config: + order: stop-first + +# Postgres needs a named volume to prevent perms issues on non-linux platforms +volumes: + postgres-data: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index e02574d63..25183a12d 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,4 +1,4 @@ -# Example compose file for production deployment. +# Example compose file for production deployment on Linux. # # Note: This file is meant to serve as a template. Please modify it # according to your needs. Read more about Docker Compose: @@ -22,12 +22,10 @@ services: image: caddy:2 volumes: - ${FZ_INSTALL_DIR:-.}/caddy:/data/caddy - ports: - - 80:80 - - 443:443 - # See Caddy's documentation for customizing this line - # https://caddyserver.com/docs/quick-starts/reverse-proxy - command: caddy reverse-proxy ${CADDY_OPTS} --to firezone:13000 --from ${EXTERNAL_URL:?err} + # See Caddy's documentation for customizing this line + # https://caddyserver.com/docs/quick-starts/reverse-proxy + command: caddy reverse-proxy ${CADDY_OPTS:-} --to 172.25.0.100:13000 --from ${EXTERNAL_URL:?err} + network_mode: "host" deploy: <<: *default-deploy @@ -55,6 +53,9 @@ services: - net.ipv6.conf.all.forwarding=1 depends_on: - postgres + networks: + firezone-network: + ipv4_address: 172.25.0.100 deploy: <<: *default-deploy @@ -66,6 +67,8 @@ services: POSTGRES_DB: ${DATABASE_NAME:-firezone} POSTGRES_USER: ${DATABASE_USER:-postgres} POSTGRES_PASSWORD: ${DATABASE_PASSWORD:?err} + networks: + - firezone-network deploy: <<: *default-deploy update_config: @@ -74,3 +77,10 @@ services: # Postgres needs a named volume to prevent perms issues on non-linux platforms volumes: postgres-data: + +networks: + firezone-network: + driver: bridge + ipam: + config: + - subnet: 172.25.0.0/16 diff --git a/docs/docs/deploy/docker/README.mdx b/docs/docs/deploy/docker/README.mdx index d02ffca36..c2c51158a 100644 --- a/docs/docs/deploy/docker/README.mdx +++ b/docs/docs/deploy/docker/README.mdx @@ -62,9 +62,14 @@ installation process, follow the steps below to install manually. 1. Ensure you're on a supported platform and have a recent version of [docker-compose](https://docs.docker.com/compose/install/) installed. 1. Download the docker compose template to a local working directory: + **For Linux**: ```shell curl -fsSL https://raw.githubusercontent.com/firezone/firezone/master/docker-compose.prod.yml -o docker-compose.yml ``` + **For macOS, Windows (non-production only)**: + ```shell + curl -fsSL https://raw.githubusercontent.com/firezone/firezone/master/docker-compose.desktop.yml -o docker-compose.yml + ``` 1. Generate required secrets: ```shell docker run --rm firezone/firezone bin/gen-env > .env diff --git a/docs/docs/deploy/docker/supported-platforms.mdx b/docs/docs/deploy/docker/supported-platforms.mdx index 454c1912d..7e8c7748d 100644 --- a/docs/docs/deploy/docker/supported-platforms.mdx +++ b/docs/docs/deploy/docker/supported-platforms.mdx @@ -6,17 +6,28 @@ sidebar_position: 1 Firezone currently supports the following platforms for Docker-based deployments. -| OS | Architecture(s) | Runtime | Status | Notes | -| --- | --- | --- | --- | --- | -| Linux | `amd64` `arm64` | Docker Server | Fully-supported | `wireguard` module needed for kernels < `5.6` | -| Linux | `amd64` `arm64` | Docker Desktop | Fully-supported | Not recommended for production deployments. | -| macOS | `amd64` `arm64` | Docker Desktop | Fully-supported | Not recommended for production deployments. | -| Windows | `amd64` `arm64` | Docker Desktop | Fully-supported | Not recommended for production deployments. | +| OS | Architecture(s) | Runtime | Status | Notes | +| --- | --- | --- | --- | --- | +| Linux | `amd64` `arm64` | Docker Server | **Fully-supported** | `wireguard` kernel module needed for kernels < `5.6`. | +| Linux | `amd64` `arm64` | Docker Desktop | Works, but unsupported. | Not recommended for production deployments. See [caveats](#docker-desktop-caveats). | +| macOS | `amd64` `arm64` | Docker Desktop | Works. but unsupported. | Not recommended for production deployments. See [caveats](#non-linux-platform-caveats). | +| Windows | `amd64` `arm64` | Docker Desktop | **Untested** | Not recommended for production deployments. See [caveats](#non-linux-platform-caveats). | + +## Docker Desktop Caveats -:::info Docker Desktop [rewrites the source address ](https://www.docker.com/blog/how-docker-desktop-networking-works-under-the-hood/) for packets flowing out of container networks under some conditions. This can cause routing loops and other hard to debug connectivity issues with Firezone. We recommend **only** using Docker Server for Linux for production deployments. -::: + +## Non-Linux Platform Caveats + +Only Docker for Linux supports the host networking mode, so macOS and Windows +platforms will be able unable to properly attribute client source address +for HTTP requests. This means any IP-based throttling or logging in your +chosen proxy (`caddy` by default) will be ineffective, since the source +IP will always be the Docker-side host IP (typically `172.X.0.1`). + +Egress rules operate on the tunneled client IP address and aren't affected +by this limitation. diff --git a/docs/docs/reference/env-vars.mdx b/docs/docs/reference/env-vars.mdx index 31c9e16b4..984a388d4 100644 --- a/docs/docs/reference/env-vars.mdx +++ b/docs/docs/reference/env-vars.mdx @@ -10,7 +10,7 @@ For Docker-based deployments, deployment-related or infrastructure-related config of Firezone is done through environment variables passed to the Firezone image upon launch. -We recommend setting these in your Docker ENV file (`/data/firezone/firezone/.env` by +We recommend setting these in your Docker ENV file (`$HOME/.firezone/.env` by default). Required fields in **bold**. | Name | Description | Format | Default | diff --git a/docs/docs/reference/file-and-directory-locations.mdx b/docs/docs/reference/file-and-directory-locations.mdx index 68657a8c4..947da3f98 100644 --- a/docs/docs/reference/file-and-directory-locations.mdx +++ b/docs/docs/reference/file-and-directory-locations.mdx @@ -10,18 +10,18 @@ your installation. -| path | description | -| --- | --- | -| `/.env` | Firezone secrets used for encryption, cookies, and sessions. **Losing this file will result in irrecoverable data loss**. | -| `/docker-compose.yml` | Docker Compose file used to manage Firezone services. | -| `/data/firezone/firezone` | Top-level directory containing Firezone-related persisted data | -| `/data/firezone/postgres` | Postgres DB files. | -| `/data/firezone/caddy` | Caddy persisted files. | +| Default path | Description | +| --- | --- | +| `$HOME/.firezone/.env` | Firezone secrets used for encryption, cookies, and sessions. **Losing this file will result in irrecoverable data loss**. | +| `$HOME/.firezone/docker-compose.yml` | Docker Compose file used to manage Firezone services. | +| `$HOME/.firezone/firezone` | Top-level directory containing Firezone-related persisted data | +| `$HOME/.firezone/caddy` | Caddy persisted files. | +| Default Docker volume location. | Postgres DB files. | -| path | description | +| Path | Description | | --- | --- | | `/var/opt/firezone` | Top-level directory containing data and generated configuration for Firezone bundled services. | | `/opt/firezone` | Top-level directory containing built libraries, binaries and runtime files needed by Firezone. | diff --git a/docs/docs/reference/reverse-proxy-templates/traefik.mdx b/docs/docs/reference/reverse-proxy-templates/traefik.mdx index 1493b5cb4..2c75494d1 100644 --- a/docs/docs/reference/reverse-proxy-templates/traefik.mdx +++ b/docs/docs/reference/reverse-proxy-templates/traefik.mdx @@ -34,47 +34,14 @@ x-deploy: &default-deploy order: start-first networks: - app: + firezone-network: enable_ipv6: true ipam: config: - - subnet: 172.28.0.0/16 + - subnet: 172.25.0.0/16 - subnet: 2001:3990:3990::/64 services: - firezone: - image: firezone/firezone - ports: - - 51820:51820/udp - volumes: - # IMPORTANT: Persists WireGuard private key and other data. If - # /var/firezone/private_key exists when Firezone starts, it is - # used as the WireGuard private. Otherwise, one is generated. - - /data/firezone/firezone:/var/firezone - environment: - EXTERNAL_URL: ${EXTERNAL_URL:?err} - ADMIN_EMAIL: ${ADMIN_EMAIL:?err} - DEFAULT_ADMIN_PASSWORD: ${DEFAULT_ADMIN_PASSWORD:?err} - GUARDIAN_SECRET_KEY: ${GUARDIAN_SECRET_KEY:?err} - SECRET_KEY_BASE: ${SECRET_KEY_BASE:?err} - LIVE_VIEW_SIGNING_SALT: ${LIVE_VIEW_SIGNING_SALT:?err} - COOKIE_SIGNING_SALT: ${COOKIE_SIGNING_SALT:?err} - COOKIE_ENCRYPTION_SALT: ${COOKIE_ENCRYPTION_SALT:?err} - DATABASE_ENCRYPTION_KEY: ${DATABASE_ENCRYPTION_KEY:?err} - - # Ensure this includes the traefik service IP. - EXTERAL_TRUSTED_PROXIES: [''] - networks: - - app - cap_add: - - NET_ADMIN - - SYS_MODULE - sysctls: - - net.ipv6.conf.all.disable_ipv6=0 - - net.ipv4.ip_forward=1 - - net.ipv6.conf.all.forwarding=1 - depends_on: - - postgres traefik: deploy: <<: *default-deploy @@ -98,22 +65,65 @@ services: - ./rules.yml:/rules.yml # make the IP of this service deterministic networks: - app: - ipv4_address: 172.28.0.99 + firezone-network: + ipv4_address: 172.25.0.99 ipv6_address: 2001:3990:3990::99 - postgres: - image: postgres:13 + + firezone: + image: firezone/firezone + ports: + - 51820:51820/udp + env_file: + # This should contain a list of env vars for configuring Firezone. + # See https://docs.firezone.dev/reference/env-vars for more info. + - ${FZ_INSTALL_DIR:-.}/.env volumes: - - /data/firezone/postgres:/var/lib/postgresql/data + # IMPORTANT: Persists WireGuard private key and other data. If + # /var/firezone/private_key exists when Firezone starts, it is + # used as the WireGuard private. Otherwise, one is generated. + - ${FZ_INSTALL_DIR:-.}/firezone:/var/firezone + cap_add: + # Needed for WireGuard and firewall support. + - NET_ADMIN + - SYS_MODULE + sysctls: + # Needed for masquerading and NAT. + - net.ipv6.conf.all.disable_ipv6=0 + - net.ipv4.ip_forward=1 + - net.ipv6.conf.all.forwarding=1 + depends_on: + - postgres + networks: + firezone-network: + ipv4_address: 172.25.0.100 + deploy: + <<: *default-deploy + + postgres: + image: postgres:15 + volumes: + - postgres-data:/var/lib/postgresql/data environment: - # same value as ## DB section above POSTGRES_DB: ${DATABASE_NAME:-firezone} POSTGRES_USER: ${DATABASE_USER:-postgres} POSTGRES_PASSWORD: ${DATABASE_PASSWORD:?err} + networks: + - firezone-network deploy: <<: *default-deploy update_config: order: stop-first + +# Postgres needs a named volume to prevent perms issues on non-linux platforms +volumes: + postgres-data: + +networks: + firezone-network: + driver: bridge + ipam: + config: + - subnet: 172.25.0.0/16 ``` ### `rules.yml` diff --git a/scripts/install.sh b/scripts/install.sh index cc3cc3baf..630725079 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -113,7 +113,16 @@ firezoneSetup() { export FZ_INSTALL_DIR=$installDir if ! test -f $installDir/docker-compose.yml; then - curl -fsSL https://raw.githubusercontent.com/firezone/firezone/master/docker-compose.prod.yml -o $installDir/docker-compose.yml + os_type="$(uname -s)" + case "${os_type}" in + Linux*) + file=docker-compose.prod.yml + ;; + *) + file=docker-compose.desktop.yml + ;; + esac + curl -fsSL https://raw.githubusercontent.com/firezone/firezone/master/$file -o $installDir/docker-compose.yml fi db_pass=$(od -vN "8" -An -tx1 /dev/urandom | tr -d " \n" ; echo) docker run --rm firezone/firezone bin/gen-env > "$installDir/.env"