+ <.button style="primary" class={@class} navigate={@navigate} icon="hero-plus">
<%= render_slot(@inner_block) %>
"""
diff --git a/elixir/apps/web/lib/web/live/relay_groups/new.ex b/elixir/apps/web/lib/web/live/relay_groups/new.ex
index 1f4029e30..934a4e231 100644
--- a/elixir/apps/web/lib/web/live/relay_groups/new.ex
+++ b/elixir/apps/web/lib/web/live/relay_groups/new.ex
@@ -43,16 +43,63 @@ defmodule Web.RelayGroups.New do
Select deployment method:
- <.tabs id="deployment-instructions">
+ <.tabs id="deployment-instructions" phx-update="ignore">
<:tab id="docker-instructions" label="Docker">
- <.code_block id="code-sample-docker" class="w-full rounded-b" phx-no-format><%= docker_command(encode_group_token(@group)) %>
+
+ Copy-paste this command to your server and replace PUBLIC_IP4_ADDR
+ and PUBLIC_IP6_ADDR
+ with your public IP addresses:
+
+
+ <.code_block id="code-sample-docker" class="w-full rounded-b" phx-no-format><%= docker_command(@env) %>
<:tab id="systemd-instructions" label="Systemd">
- <.code_block id="code-sample-systemd" class="w-full rounded-b" phx-no-format><%= systemd_command(encode_group_token(@group)) %>
+
+ 1. Create a systemd unit file with the following content:
+
+
+ <.code_block id="code-sample-systemd" class="w-full" phx-no-format>sudo nano /etc/systemd/system/firezone-relay.service
+
+
+ 2. Copy-paste the following content into the file and replace
+ PUBLIC_IP4_ADDR
+ and PUBLIC_IP6_ADDR
+ with your public IP addresses::
+
+
+ <.code_block id="code-sample-systemd" class="w-full rounded-b" phx-no-format><%= systemd_command(@env) %>
+
+
+ 3. Save by pressing Ctrl+X, then Y, then Enter.
+
+
+
+ 4. Reload systemd configuration:
+
+
+ <.code_block id="code-sample-systemd" class="w-full" phx-no-format>sudo systemctl daemon-reload
+
+
+ 5. Start the service:
+
+
+ <.code_block id="code-sample-systemd" class="w-full" phx-no-format>sudo systemctl start firezone-relay
+
+
+ 6. Enable the service to start on boot:
+
+
+ <.code_block id="code-sample-systemd" class="w-full" phx-no-format>sudo systemctl enable firezone-relay
+
+
+ 7. Check the status of the service:
+
+
+ <.code_block id="code-sample-systemd" class="w-full rounded-b" phx-no-format>sudo systemctl status firezone-relay
-
+
Waiting for relay connection...
@@ -76,7 +123,8 @@ defmodule Web.RelayGroups.New do
with {:ok, group} <-
Relays.create_group(attrs, socket.assigns.subject) do
:ok = Relays.subscribe_for_relays_presence_in_group(group)
- {:noreply, assign(socket, group: group)}
+ token = encode_group_token(group)
+ {:noreply, assign(socket, group: group, env: env(token))}
else
{:error, changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
@@ -92,30 +140,99 @@ defmodule Web.RelayGroups.New do
{:noreply, socket}
end
- defp docker_command(secret) do
- """
- docker run -d \\
- --name=firezone-relay-0 \\
- --restart=always \\
- -v /dev/net/tun:/dev/net/tun \\
- -e FIREZONE_TOKEN=#{secret} \\
- us-east1-docker.pkg.dev/firezone/firezone/relay:stable
- """
+ defp version do
+ vsn =
+ Application.spec(:domain)
+ |> Keyword.fetch!(:vsn)
+ |> List.to_string()
+ |> Version.parse!()
+
+ "#{vsn.major}.#{vsn.minor}"
end
- defp systemd_command(_secret) do
- """
- [Unit]
- Description=zigbee2mqtt
- After=network.target
+ defp env(token) do
+ api_url_override =
+ if api_url = Domain.Config.get_env(:web, :api_url_override) do
+ {"FIREZONE_API_URL", api_url}
+ end
- [Service]
- ExecStart=/usr/bin/npm start
- WorkingDirectory=/opt/zigbee2mqtt
- StandardOutput=inherit
- StandardError=inherit
- Restart=always
- User=pi
+ [
+ {"FIREZONE_ID", Ecto.UUID.generate()},
+ {"FIREZONE_TOKEN", token},
+ {"PUBLIC_IP4_ADDR", "YOU_MUST_SET_THIS_VALUE"},
+ {"PUBLIC_IP6_ADDR", "YOU_MUST_SET_THIS_VALUE"},
+ api_url_override,
+ {"RUST_LOG", "warn"},
+ {"LOG_FORMAT", "google-cloud"}
+ ]
+ |> Enum.reject(&is_nil/1)
+ end
+
+ defp docker_command(env) do
+ [
+ "docker run -d",
+ "--restart=unless-stopped",
+ "--pull=always",
+ "--health-cmd=\"lsof -i UDP | grep firezone-relay\"",
+ "--name=firezone-relay",
+ "--cap-add=NET_ADMIN",
+ "--sysctl net.ipv4.ip_forward=1",
+ "--sysctl net.ipv4.conf.all.src_valid_mark=1",
+ "--sysctl net.ipv6.conf.all.disable_ipv6=0",
+ "--sysctl net.ipv6.conf.all.forwarding=1",
+ "--sysctl net.ipv6.conf.default.forwarding=1",
+ "--device=\"/dev/net/tun:/dev/net/tun\"",
+ Enum.map(env, fn {key, value} -> "--env #{key}=\"#{value}\"" end),
+ "--env FIREZONE_HOSTNAME=$(hostname)",
+ "#{Domain.Config.fetch_env!(:domain, :docker_registry)}/relay:#{version()}"
+ ]
+ |> List.flatten()
+ |> Enum.join(" \\\n ")
+ end
+
+ defp systemd_command(env) do
+ """
+ [Unit]
+ Description=Firezone Relay
+ After=network.target
+
+ [Service]
+ Type=simple
+ #{Enum.map_join(env, "\n", fn {key, value} -> "Environment=\"#{key}=#{value}\"" end)}
+ ExecStartPre=/bin/sh -c ' \\
+ remote_version=$(curl -Ls \\
+ -H "Accept: application/vnd.github+json" \\
+ -H "X-GitHub-Api-Version: 2022-11-28" \\
+ https://api.github.com/repos/firezone/firezone/releases/latest | grep -oP '"'"'(?<="tag_name": ")[^"]*'"'"'); \\
+ if [ -e /usr/local/bin/firezone-relay ]; then \\
+ current_version=$(/usr/local/bin/firezone-relay --version | awk '"'"'{print $NF}'"'"'); \\
+ else \\
+ current_version=""; \\
+ fi; \\
+ if [ ! "$current_version" = "$remote_version" ]; then \\
+ arch=$(uname -m); \\
+ case $arch in \\
+ aarch64) \\
+ bin_url="https://github.com/firezone/firezone/releases/download/latest/relay-arm64" ;; \\
+ armv7l) \\
+ bin_url="https://github.com/firezone/firezone/releases/download/latest/relay-arm" ;; \\
+ x86_64) \\
+ bin_url="https://github.com/firezone/firezone/releases/download/latest/relay-x64" ;; \\
+ *) \\
+ echo "Unsupported architecture"; \\
+ exit 1 ;; \\
+ esac; \\
+ wget -O /usr/local/bin/firezone-relay $bin_url; \\
+ chmod +x /usr/local/bin/firezone-relay; \\
+ fi \\
+ '
+ ExecStartPre=/usr/bin/chmod +x /usr/local/bin/firezone-relay
+ ExecStart=FIREZONE_HOSTNAME=$(hostname) /usr/local/bin/firezone-relay
+ Restart=always
+ RestartSec=3
+
+ [Install]
+ WantedBy=multi-user.target
"""
end
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 9d75ef2ca..e82fb0e20 100644
--- a/elixir/apps/web/lib/web/live/sites/new_token.ex
+++ b/elixir/apps/web/lib/web/live/sites/new_token.ex
@@ -4,12 +4,20 @@ defmodule Web.Sites.NewToken do
def mount(%{"id" => id}, _session, socket) do
with {:ok, group} <- Gateways.fetch_group_by_id(id, socket.assigns.subject) do
- {:ok, group} =
- Gateways.update_group(%{group | tokens: []}, %{tokens: [%{}]}, socket.assigns.subject)
+ {group, env} =
+ if connected?(socket) do
+ {:ok, group} =
+ Gateways.update_group(%{group | tokens: []}, %{tokens: [%{}]}, socket.assigns.subject)
- :ok = Gateways.subscribe_for_gateways_presence_in_group(group)
+ :ok = Gateways.subscribe_for_gateways_presence_in_group(group)
- {:ok, assign(socket, group: group)}
+ token = encode_group_token(group)
+ {group, env(token)}
+ else
+ {group, nil}
+ end
+
+ {:ok, assign(socket, group: group, env: env)}
else
{:error, _reason} -> raise Web.LiveErrors.NotFoundError
end
@@ -37,16 +45,58 @@ defmodule Web.Sites.NewToken do
Select deployment method:
- <.tabs id="deployment-instructions">
+ <.tabs :if={@env} id="deployment-instructions" phx-update="ignore">
<:tab id="docker-instructions" label="Docker">
- <.code_block id="code-sample-docker" class="w-full rounded-b" phx-no-format><%= docker_command(encode_group_token(@group)) %>
+
+ Copy-paste this command to your server:
+
+
+ <.code_block id="code-sample-docker" class="w-full rounded-b" phx-no-format><%= docker_command(@env) %>
<:tab id="systemd-instructions" label="Systemd">
- <.code_block id="code-sample-systemd" class="w-full rounded-b" phx-no-format><%= systemd_command(encode_group_token(@group)) %>
+
+ 1. Create a systemd unit file with the following content:
+
+
+ <.code_block id="code-sample-systemd" class="w-full" phx-no-format>sudo nano /etc/systemd/system/firezone-gateway.service
+
+
+ 2. Copy-paste the following content into the file:
+
+
+ <.code_block id="code-sample-systemd" class="w-full rounded-b" phx-no-format><%= systemd_command(@env) %>
+
+
+ 3. Save by pressing Ctrl+X, then Y, then Enter.
+
+
+
+ 4. Reload systemd configuration:
+
+
+ <.code_block id="code-sample-systemd" class="w-full" phx-no-format>sudo systemctl daemon-reload
+
+
+ 5. Start the service:
+
+
+ <.code_block id="code-sample-systemd" class="w-full" phx-no-format>sudo systemctl start firezone-gateway
+
+
+ 6. Enable the service to start on boot:
+
+
+ <.code_block id="code-sample-systemd" class="w-full" phx-no-format>sudo systemctl enable firezone-gateway
+
+
+ 7. Check the status of the service:
+
+
+ <.code_block id="code-sample-systemd" class="w-full rounded-b" phx-no-format>sudo systemctl status firezone-gateway
-
+
Waiting for gateway connection...
@@ -65,30 +115,45 @@ defmodule Web.Sites.NewToken do
"#{vsn.major}.#{vsn.minor}"
end
- defp docker_command(token) do
- """
- docker run -d \\
- --restart=unless-stopped \\
- --pull=always \\
- --health-cmd="ip link | grep tun-firezone" \\
- --name=firezone-gateway \\
- --cap-add=NET_ADMIN \\
- --sysctl net.ipv4.ip_forward=1 \\
- --sysctl net.ipv4.conf.all.src_valid_mark=1 \\
- --sysctl net.ipv6.conf.all.disable_ipv6=0 \\
- --sysctl net.ipv6.conf.all.forwarding=1 \\
- --sysctl net.ipv6.conf.default.forwarding=1 \\
- --device="/dev/net/tun:/dev/net/tun" \\
- --env FIREZONE_ID="#{Ecto.UUID.generate()}" \\
- --env FIREZONE_TOKEN="#{token}" \\
- --env FIREZONE_ENABLE_MASQUERADE=1 \\
- --env FIREZONE_HOSTNAME="`hostname`" \\
- --env RUST_LOG="warn" \\
- #{Domain.Config.fetch_env!(:domain, :docker_registry)}/gateway:#{version()}
- """
+ defp env(token) do
+ api_url_override =
+ if api_url = Domain.Config.get_env(:web, :api_url_override) do
+ {"FIREZONE_API_URL", api_url}
+ end
+
+ [
+ {"FIREZONE_ID", Ecto.UUID.generate()},
+ {"FIREZONE_TOKEN", token},
+ {"FIREZONE_ENABLE_MASQUERADE", "1"},
+ api_url_override,
+ {"RUST_LOG", "warn"}
+ ]
+ |> Enum.reject(&is_nil/1)
end
- defp systemd_command(token) do
+ defp docker_command(env) do
+ [
+ "docker run -d",
+ "--restart=unless-stopped",
+ "--pull=always",
+ "--health-cmd=\"ip link | grep tun-firezone\"",
+ "--name=firezone-gateway",
+ "--cap-add=NET_ADMIN",
+ "--sysctl net.ipv4.ip_forward=1",
+ "--sysctl net.ipv4.conf.all.src_valid_mark=1",
+ "--sysctl net.ipv6.conf.all.disable_ipv6=0",
+ "--sysctl net.ipv6.conf.all.forwarding=1",
+ "--sysctl net.ipv6.conf.default.forwarding=1",
+ "--device=\"/dev/net/tun:/dev/net/tun\"",
+ Enum.map(env, fn {key, value} -> "--env #{key}=\"#{value}\"" end),
+ "--env FIREZONE_HOSTNAME=$(hostname)",
+ "#{Domain.Config.fetch_env!(:domain, :docker_registry)}/gateway:#{version()}"
+ ]
+ |> List.flatten()
+ |> Enum.join(" \\\n ")
+ end
+
+ defp systemd_command(env) do
"""
[Unit]
Description=Firezone Gateway
@@ -96,34 +161,36 @@ defmodule Web.Sites.NewToken do
[Service]
Type=simple
- Environment="FIREZONE_TOKEN=#{token}"
- Environment="FIREZONE_VERSION=#{version()}"
- Environment="FIREZONE_HOSTNAME=$(hostname)"
- Environment="FIREZONE_ENABLE_MASQUERADE=1"
+ #{Enum.map_join(env, "\n", fn {key, value} -> "Environment=\"#{key}=#{value}\"" end)}
ExecStartPre=/bin/sh -c ' \\
+ remote_version=$(curl -Ls \\
+ -H "Accept: application/vnd.github+json" \\
+ -H "X-GitHub-Api-Version: 2022-11-28" \\
+ https://api.github.com/repos/firezone/firezone/releases/latest | grep -oP '"'"'(?<="tag_name": ")[^"]*'"'"'); \\
if [ -e /usr/local/bin/firezone-gateway ]; then \\
- current_version=$(/usr/local/bin/firezone-gateway --version 2>&1 | awk "{print $NF}"); \\
+ current_version=$(/usr/local/bin/firezone-gateway --version | awk '"'"'{print $NF}'"'"'); \\
else \\
current_version=""; \\
fi; \\
- if [ ! "$$current_version" = "${FIREZONE_VERSION}" ]; then \\
+ if [ ! "$current_version" = "$remote_version" ]; then \\
arch=$(uname -m); \\
- case $$arch in \\
+ case $arch in \\
aarch64) \\
- bin_url="https://github.com/firezone/firezone/releases/download/${FIREZONE_VERSION}/gateway-aarch64-unknown-linux-musl-${FIREZONE_VERSION}" ;; \\
+ bin_url="https://github.com/firezone/firezone/releases/download/latest/gateway-arm64" ;; \\
armv7l) \\
- bin_url="https://github.com/firezone/firezone/releases/download/${FIREZONE_VERSION}/gateway-armv7-unknown-linux-musleabihf-${FIREZONE_VERSION}" ;; \\
+ bin_url="https://github.com/firezone/firezone/releases/download/latest/gateway-arm" ;; \\
x86_64) \\
- bin_url="https://github.com/firezone/firezone/releases/download/${FIREZONE_VERSION}/gateway-x86_64-unknown-linux-musl-${FIREZONE_VERSION}" ;; \\
+ bin_url="https://github.com/firezone/firezone/releases/download/latest/gateway-x64" ;; \\
*) \\
echo "Unsupported architecture"; \\
exit 1 ;; \\
esac; \\
- wget -O /usr/local/bin/firezone-gateway $$bin_url; \\
+ wget -O /usr/local/bin/firezone-gateway $bin_url; \\
+ chmod +x /usr/local/bin/firezone-gateway; \\
fi \\
'
ExecStartPre=/usr/bin/chmod +x /usr/local/bin/firezone-gateway
- ExecStart=/usr/local/bin/firezone-gateway
+ ExecStart=FIREZONE_HOSTNAME=$(hostname) /usr/local/bin/firezone-gateway
Restart=always
RestartSec=3
diff --git a/elixir/apps/web/test/web/live/relay_groups/new_test.exs b/elixir/apps/web/test/web/live/relay_groups/new_test.exs
index 5d37bd97e..9105b6511 100644
--- a/elixir/apps/web/test/web/live/relay_groups/new_test.exs
+++ b/elixir/apps/web/test/web/live/relay_groups/new_test.exs
@@ -123,7 +123,7 @@ defmodule Web.Live.RelayGroups.NewTest do
assert html =~ "docker run"
assert html =~ "Waiting for relay connection..."
- token = Regex.run(~r/FIREZONE_TOKEN=([^ ]+)/, html) |> List.last()
+ token = Regex.run(~r/FIREZONE_TOKEN="([^ ]+)"/, html) |> List.last()
assert {:ok, _token} = Domain.Relays.authorize_relay(token)
group = Repo.get_by(Domain.Relays.Group, name: attrs.name) |> Repo.preload(:tokens)
diff --git a/elixir/config/config.exs b/elixir/config/config.exs
index 69b22c6b9..f232996d9 100644
--- a/elixir/config/config.exs
+++ b/elixir/config/config.exs
@@ -130,6 +130,8 @@ config :web, Web.Plugs.SecureHeaders,
"connect-src 'self' https://firezone.statuspage.io"
]
+config :web, api_url_override: "ws://localhost:13001/"
+
###############################
##### API #####################
###############################
diff --git a/elixir/config/dev.exs b/elixir/config/dev.exs
index aa9ede7f1..4c2aae7d8 100644
--- a/elixir/config/dev.exs
+++ b/elixir/config/dev.exs
@@ -64,6 +64,10 @@ config :web, Web.Plugs.SecureHeaders,
"connect-src 'self' data: https://firezone.statuspage.io"
]
+# Note: on Linux you may need to add `--add-host=host.docker.internal:host-gateway`
+# to the `docker run` command. Works on Docker v20.10 and above.
+config :web, api_url_override: "ws://host.docker.internal:13001/"
+
###############################
##### API #####################
###############################
diff --git a/elixir/config/runtime.exs b/elixir/config/runtime.exs
index bed93d42d..a3959f16c 100644
--- a/elixir/config/runtime.exs
+++ b/elixir/config/runtime.exs
@@ -107,6 +107,8 @@ if config_env() == :prod do
cookie_signing_salt: compile_config!(:cookie_signing_salt),
cookie_encryption_salt: compile_config!(:cookie_encryption_salt)
+ config :web, api_url_override: compile_config!(:api_url_override)
+
###############################
##### API #####################
###############################
diff --git a/terraform/environments/staging/bi.tf b/terraform/environments/staging/bi.tf
index d17552b8a..fba515552 100644
--- a/terraform/environments/staging/bi.tf
+++ b/terraform/environments/staging/bi.tf
@@ -14,6 +14,12 @@ resource "random_password" "metabase_db_password" {
min_special = 1
}
+# This user can also be used to connect to the Firezone database,
+# but following SQL should be run manually using the Cloud SQL Proxy:
+#
+# GRANT SELECT ON ALL TABLES IN SCHEMA public TO metabase;
+# GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO metabase;
+#
resource "google_sql_user" "metabase" {
project = module.google-cloud-project.project.project_id
@@ -85,6 +91,10 @@ module "metabase" {
name = "MB_ANON_TRACKING_ENABLED"
value = "false"
},
+ # {
+ # name = "MB_JETTY_PORT"
+ # value = "80"
+ # }
]
health_check = {
diff --git a/terraform/environments/staging/main.tf b/terraform/environments/staging/main.tf
index 229b899b2..9d92f6c59 100644
--- a/terraform/environments/staging/main.tf
+++ b/terraform/environments/staging/main.tf
@@ -485,7 +485,11 @@ module "web" {
{
name = "PHOENIX_HTTP_WEB_PORT"
value = "8080"
- }
+ },
+ {
+ name = "API_URL_OVERRIDE"
+ value = "wss://api.${local.tld}"
+ },
], local.shared_application_environment_variables)
application_labels = {