From 1dcd8b02e130a30421e6a5ef53a1d36d9328da8a Mon Sep 17 00:00:00 2001
From: Jamil Bou Kheir
Date: Fri, 17 Dec 2021 09:44:59 -0800
Subject: [PATCH] Modal bug workaround; Add persistent keepalives
---
apps/fz_http/lib/fz_http/devices.ex | 27 ++++++++-
apps/fz_http/lib/fz_http/devices/device.ex | 17 +++++-
apps/fz_http/lib/fz_http/macros.ex | 3 +
apps/fz_http/lib/fz_http/settings.ex | 1 +
apps/fz_http/lib/fz_http/settings/setting.ex | 9 ++-
.../live/device_live/form_component.ex | 4 ++
.../live/device_live/form_component.html.heex | 33 +++++++++++
.../live/device_live/show.html.heex | 20 ++++++-
.../fz_http_web/live/device_live/show_live.ex | 3 +-
.../lib/fz_http_web/live/modal_component.ex | 16 ++++-
...211217003247_add_persistent_keepalives.exs | 17 ++++++
.../live/device_live/show_test.exs | 59 ++++++++++++++++++-
12 files changed, 197 insertions(+), 12 deletions(-)
create mode 100644 apps/fz_http/priv/repo/migrations/20211217003247_add_persistent_keepalives.exs
diff --git a/apps/fz_http/lib/fz_http/devices.ex b/apps/fz_http/lib/fz_http/devices.ex
index fae570914..e7c82ae7d 100644
--- a/apps/fz_http/lib/fz_http/devices.ex
+++ b/apps/fz_http/lib/fz_http/devices.ex
@@ -103,8 +103,21 @@ defmodule FzHttp.Devices do
end
end
+ def persistent_keepalives(device) do
+ if device.use_default_persistent_keepalives do
+ Settings.default_device_persistent_keepalives()
+ else
+ device.persistent_keepalives
+ end
+ end
+
def defaults(changeset) do
- ~w(use_default_allowed_ips use_default_dns_servers use_default_endpoint)a
+ ~w(
+ use_default_allowed_ips
+ use_default_dns_servers
+ use_default_endpoint
+ use_default_persistent_keepalives
+ )a
|> Enum.map(fn field -> {field, Device.field(changeset, field)} end)
|> Map.new()
end
@@ -122,6 +135,7 @@ defmodule FzHttp.Devices do
PublicKey = #{device.server_public_key}
AllowedIPs = #{allowed_ips(device)}
Endpoint = #{endpoint(device)}:#{wireguard_port}
+ #{persistent_keepalives_config(device)}
"""
end
@@ -136,6 +150,17 @@ defmodule FzHttp.Devices do
update_device(device, config_token_attrs)
end
+ defp persistent_keepalives_config(device) do
+ pk = persistent_keepalives(device)
+ pk && "PersistentKeepalives = #{pk}"
+
+ if is_nil(pk) do
+ ""
+ else
+ "PersistentKeepalives = #{pk}"
+ end
+ end
+
defp dns_servers_config(device) when is_struct(device) do
dns_servers = dns_servers(device)
diff --git a/apps/fz_http/lib/fz_http/devices/device.ex b/apps/fz_http/lib/fz_http/devices/device.ex
index 1ea9ca7f7..158a0561f 100644
--- a/apps/fz_http/lib/fz_http/devices/device.ex
+++ b/apps/fz_http/lib/fz_http/devices/device.ex
@@ -23,7 +23,9 @@ defmodule FzHttp.Devices.Device do
field :use_default_allowed_ips, :boolean, read_after_writes: true, default: true
field :use_default_dns_servers, :boolean, read_after_writes: true, default: true
field :use_default_endpoint, :boolean, read_after_writes: true, default: true
+ field :use_default_persistent_keepalives, :boolean, read_after_writes: true, default: true
field :endpoint, :string
+ field :persistent_keepalives, :integer
field :allowed_ips, :string
field :dns_servers, :string
field :private_key, FzHttp.Encrypted.Binary
@@ -62,9 +64,11 @@ defmodule FzHttp.Devices.Device do
:use_default_allowed_ips,
:use_default_dns_servers,
:use_default_endpoint,
+ :use_default_persistent_keepalives,
:allowed_ips,
:dns_servers,
:endpoint,
+ :persistent_keepalives,
:remote_ip,
:address,
:server_public_key,
@@ -86,12 +90,21 @@ defmodule FzHttp.Devices.Device do
:server_public_key,
:private_key
])
- |> validate_required_unless_default([:allowed_ips, :dns_servers, :endpoint])
- |> validate_omitted_if_default([:allowed_ips, :dns_servers, :endpoint])
+ |> validate_required_unless_default([
+ :allowed_ips,
+ :dns_servers,
+ :endpoint,
+ :persistent_keepalives
+ ])
+ |> validate_omitted_if_default([:allowed_ips, :dns_servers, :endpoint, :persistent_keepalives])
|> validate_list_of_ips_or_cidrs(:allowed_ips)
|> validate_list_of_ips(:dns_servers)
|> validate_no_duplicates(:dns_servers)
|> validate_ip(:endpoint)
+ |> validate_number(:persistent_keepalives,
+ greater_than_or_equal_to: 0,
+ less_than_or_equal_to: 120
+ )
|> unique_constraint(:address)
|> validate_number(:address, greater_than_or_equal_to: 2, less_than_or_equal_to: 254)
|> unique_constraint(:public_key)
diff --git a/apps/fz_http/lib/fz_http/macros.ex b/apps/fz_http/lib/fz_http/macros.ex
index 1e7048e4b..91c90da3a 100644
--- a/apps/fz_http/lib/fz_http/macros.ex
+++ b/apps/fz_http/lib/fz_http/macros.ex
@@ -3,6 +3,9 @@ defmodule FzHttp.Macros do
Metaprogramming macros
"""
+ @doc """
+ Defines getters for all Setting keys as functions on the Settings module.
+ """
defmacro def_settings(keys) do
quote bind_quoted: [keys: keys] do
Enum.each(keys, fn key ->
diff --git a/apps/fz_http/lib/fz_http/settings.ex b/apps/fz_http/lib/fz_http/settings.ex
index f754f2177..8c22f35df 100644
--- a/apps/fz_http/lib/fz_http/settings.ex
+++ b/apps/fz_http/lib/fz_http/settings.ex
@@ -13,6 +13,7 @@ defmodule FzHttp.Settings do
default.device.allowed_ips
default.device.dns_servers
default.device.endpoint
+ default.device.persistent_keepalives
))
@doc """
diff --git a/apps/fz_http/lib/fz_http/settings/setting.ex b/apps/fz_http/lib/fz_http/settings/setting.ex
index ae393d8fc..d8f4db7f3 100644
--- a/apps/fz_http/lib/fz_http/settings/setting.ex
+++ b/apps/fz_http/lib/fz_http/settings/setting.ex
@@ -15,6 +15,7 @@ defmodule FzHttp.Settings.Setting do
import FzHttp.SharedValidators,
only: [
+ validate_ip: 2,
validate_list_of_ips: 2,
validate_list_of_ips_or_cidrs: 2,
validate_no_duplicates: 2
@@ -57,8 +58,12 @@ defmodule FzHttp.Settings.Setting do
defp validate_kv_pair(changeset, "default.device.endpoint") do
changeset
- |> validate_list_of_ips_or_cidrs(:value)
- |> validate_no_duplicates(:value)
+ |> validate_ip(:value)
+ end
+
+ defp validate_kv_pair(changeset, "default.device.persistent_keepalives") do
+ changeset
+ |> validate_number(:value, greater_than_or_equal_to: 0, less_than_or_equal_to: 120)
end
defp validate_kv_pair(changeset, unknown_key) do
diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.ex b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.ex
index fa5163087..598c6a307 100644
--- a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.ex
+++ b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.ex
@@ -18,6 +18,10 @@ defmodule FzHttpWeb.DeviceLive.FormComponent do
|> assign(:default_device_allowed_ips, Settings.default_device_allowed_ips())
|> assign(:default_device_dns_servers, Settings.default_device_dns_servers())
|> assign(:default_device_endpoint, default_device_endpoint)
+ |> assign(
+ :default_device_persistent_keepalives,
+ Settings.default_device_persistent_keepalives()
+ )
|> assign(:changeset, changeset)}
end
diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex
index 3c4477a0e..de81b699a 100644
--- a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex
+++ b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex
@@ -92,6 +92,39 @@
+
+ <%= label f, :use_default_persistent_keepalives, "Use Default Persistent Keepalives", class: "label" %>
+
+
+ <%= radio_button f, :use_default_persistent_keepalives, true %>
+ Yes
+
+
+ <%= radio_button f, :use_default_persistent_keepalives, false %>
+ No
+
+
+
+ Default: <%= @default_device_persistent_keepalives %>
+
+
+
+
+ <%= label f, :persistent_keepalives, "Persistent Keepalives", class: "label" %>
+
+ Interval for WireGuard
+
+ persistent keepalives . A value of 0 disables this. Leave this disabled
+ unless you're experiencing NAT or firewall traversal problems.
+
+
+ <%= text_input f, :persistent_keepalives, class: "input", disabled: @use_default_persistent_keepalives %>
+
+
+ <%= error_tag f, :persistent_keepalives %>
+
+
+
<%= label f, :address, "Interface Address (last octet only)", class: "label" %>
diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex b/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex
index 87aa7a229..5685717a2 100644
--- a/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex
+++ b/apps/fz_http/lib/fz_http_web/live/device_live/show.html.heex
@@ -62,6 +62,17 @@
<%= @endpoint %>
+
+ Persistent Keepalives
+
+ <%= if @persistent_keepalives == 0 do %>
+ Every <%= @persistent_keepalives %> seconds
+ <% else %>
+ Disabled
+ <% end %>
+
+
+
Public key
<%= @device.public_key %>
@@ -87,8 +98,13 @@
-
" control dropdown is-right"}
- phx-click-away="hide_config_token">
+
" control dropdown is-right"}
+ phx-capture-click="close_dropdown"
+ phx-key="escape"
+ phx-click-away="close_dropdown"
+ phx-window-keydown="close_dropdown">
assign(:dropdown_active_class, "")}
@@ -84,6 +84,7 @@ defmodule FzHttpWeb.DeviceLive.Show do
allowed_ips: Devices.allowed_ips(device),
dns_servers: Devices.dns_servers(device),
endpoint: Devices.endpoint(device),
+ persistent_keepalives: Devices.persistent_keepalives(device),
config: Devices.as_config(device)
)
else
diff --git a/apps/fz_http/lib/fz_http_web/live/modal_component.ex b/apps/fz_http/lib/fz_http_web/live/modal_component.ex
index 3c6cd5b86..550347bbb 100644
--- a/apps/fz_http/lib/fz_http_web/live/modal_component.ex
+++ b/apps/fz_http/lib/fz_http_web/live/modal_component.ex
@@ -4,11 +4,11 @@ defmodule FzHttpWeb.ModalComponent do
"""
use FzHttpWeb, :live_component
- @impl true
+ @impl Phoenix.LiveComponent
def render(assigns) do
~H"""
%{"use_default_endpoint" => "false", "endpoint" => @wireguard_endpoint}
}
@endpoint_unchanged %{
- "device" => %{"use_default_endpoint" => "true", "dns_servers" => @wireguard_endpoint}
+ "device" => %{"use_default_endpoint" => "true", "endpoint" => @wireguard_endpoint}
+ }
+ @persistent_keepalives_change %{
+ "device" => %{
+ "use_default_persistent_keepalives" => "false",
+ "persistent_keepalives" => "120"
+ }
+ }
+ @persistent_keepalives_unchanged %{
+ "device" => %{"use_default_persistent_keepalives" => "true", "persistent_keepalives" => "5"}
}
@default_allowed_ips_change %{
"device" => %{"use_default_allowed_ips" => "false"}
@@ -36,6 +45,9 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do
@default_endpoint_change %{
"device" => %{"use_default_endpoint" => "false"}
}
+ @default_persistent_keepalives_change %{
+ "device" => %{"use_default_persistent_keepalives" => "false"}
+ }
test "shows device details", %{authed_conn: conn, device: device} do
path = Routes.device_show_path(conn, :show, device)
@@ -112,6 +124,22 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do
assert test_view =~ "must not be present"
end
+ test "prevents persistent_keepalives changes when use_default_persistent_keepalives is true",
+ %{
+ authed_conn: conn,
+ device: device
+ } do
+ path = Routes.device_show_path(conn, :edit, device)
+ {:ok, view, _html} = live(conn, path)
+
+ test_view =
+ view
+ |> form("#edit-device")
+ |> render_submit(@persistent_keepalives_unchanged)
+
+ assert test_view =~ "must not be present"
+ end
+
test "allows allowed_ips changes", %{authed_conn: conn, device: device} do
path = Routes.device_show_path(conn, :edit, device)
{:ok, view, _html} = live(conn, path)
@@ -157,6 +185,21 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do
assert html =~ "Endpoint = #{@wireguard_endpoint}:51820"
end
+ test "allows persistent_keepalives changes", %{authed_conn: conn, device: device} do
+ path = Routes.device_show_path(conn, :edit, device)
+ {:ok, view, _html} = live(conn, path)
+
+ view
+ |> form("#edit-device")
+ |> render_submit(@persistent_keepalives_change)
+
+ flash = assert_redirected(view, Routes.device_show_path(conn, :show, device))
+ assert flash["info"] == "Device updated successfully."
+
+ {:ok, _view, html} = live(conn, path)
+ assert html =~ "PersistentKeepalives = 120"
+ end
+
test "prevents empty names", %{authed_conn: conn, device: device} do
path = Routes.device_show_path(conn, :edit, device)
{:ok, view, _html} = live(conn, path)
@@ -210,6 +253,20 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do
\
"""
end
+
+ test "on use_default_persistent_keepalives change", %{authed_conn: conn, device: device} do
+ path = Routes.device_show_path(conn, :edit, device)
+ {:ok, view, _html} = live(conn, path)
+
+ test_view =
+ view
+ |> form("#edit-device")
+ |> render_change(@default_persistent_keepalives_change)
+
+ assert test_view =~ """
+ \
+ """
+ end
end
describe "delete own device" do