mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
Add diagnostic pinger; default settings
Also don't display DNSservers when empty. Fixes firezone/backlog#135 Fixes firezone/backlog#123 Fixes firezone/backlog#130 Refs #333
This commit is contained in:
44
.github/workflows/ci.yml
vendored
44
.github/workflows/ci.yml
vendored
@@ -11,6 +11,50 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
static-analysis:
|
||||
runs-on: ubuntu-18.04
|
||||
env:
|
||||
MIX_ENV: dev
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: '24.1'
|
||||
elixir-version: '1.12.3'
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
deps
|
||||
_build
|
||||
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-mix-
|
||||
- name: Install Dependencies
|
||||
run: mix deps.get --only dev
|
||||
# Don't cache PLTs based on mix.lock hash, as Dialyzer can incrementally update even old ones
|
||||
# Cache key based on Elixir & Erlang version (also usefull when running in matrix)
|
||||
- name: Restore PLT cache
|
||||
uses: actions/cache@v2
|
||||
id: plt_cache
|
||||
with:
|
||||
key: |
|
||||
${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt
|
||||
path: |
|
||||
priv/plts
|
||||
# Create PLTs if no cache was found
|
||||
- name: Create PLTs
|
||||
if: steps.plt_cache.outputs.cache-hit != 'true'
|
||||
run: mix dialyzer --plt
|
||||
- name: Run format check
|
||||
run: mix format --check-formatted
|
||||
- name: Run linter
|
||||
run: mix credo --strict
|
||||
- name: Run dialyzer
|
||||
run: mix dialyzer --format dialyxir
|
||||
|
||||
unit-test:
|
||||
runs-on: ubuntu-18.04
|
||||
env:
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -33,6 +33,9 @@ npm-debug.log
|
||||
# this depending on your deployment strategy.
|
||||
/priv/static/
|
||||
|
||||
# Dialyxir output
|
||||
/priv/plts/
|
||||
|
||||
# ElixirLS generates an .elixir_ls folder for user settings
|
||||
.elixir_ls
|
||||
|
||||
@@ -51,7 +54,6 @@ npm-debug.log
|
||||
/*.deb
|
||||
/*.rpm
|
||||
|
||||
pkg/debian/opt
|
||||
|
||||
# Test screenshots
|
||||
apps/fz_http/screenshots
|
||||
|
||||
@@ -13,6 +13,12 @@ repos:
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: \.exs*$
|
||||
- id: mix-analysis
|
||||
name: 'elixir: mix dialyzer'
|
||||
entry: mix dialyzer --format dialyxir
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: \.exs*$
|
||||
- id: mix-compile
|
||||
name: 'elixir: mix compile'
|
||||
entry: mix compile --force --warnings-as-errors
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# These should match the versions used in the build
|
||||
# These are used for the dev environment.
|
||||
# This should match the versions used in the built product.
|
||||
nodejs 14.18.1
|
||||
elixir 1.12.3-otp-24
|
||||
erlang 24.1.4
|
||||
|
||||
9
apps/fz_common/lib/fz_integer.ex
Normal file
9
apps/fz_common/lib/fz_integer.ex
Normal file
@@ -0,0 +1,9 @@
|
||||
defmodule FzCommon.FzInteger do
|
||||
@moduledoc """
|
||||
Utility functions for working with Integers.
|
||||
"""
|
||||
|
||||
def clamp(num, min, _max) when is_integer(num) and num < min, do: min
|
||||
def clamp(num, _min, max) when is_integer(num) and num > max, do: max
|
||||
def clamp(num, _min, _max) when is_integer(num), do: num
|
||||
end
|
||||
21
apps/fz_common/lib/fz_string.ex
Normal file
21
apps/fz_common/lib/fz_string.ex
Normal file
@@ -0,0 +1,21 @@
|
||||
defmodule FzCommon.FzString do
|
||||
@moduledoc """
|
||||
Utility functions for working with Strings.
|
||||
"""
|
||||
|
||||
def to_boolean(str) when is_binary(str) do
|
||||
as_bool(String.downcase(str))
|
||||
end
|
||||
|
||||
defp as_bool("true") do
|
||||
true
|
||||
end
|
||||
|
||||
defp as_bool("false") do
|
||||
false
|
||||
end
|
||||
|
||||
defp as_bool(unknown) do
|
||||
raise "Unknown boolean: string #{unknown} not one of ['true', 'false']."
|
||||
end
|
||||
end
|
||||
@@ -3,7 +3,7 @@ defmodule FzCommon.FzCryptoTest do
|
||||
|
||||
alias FzCommon.FzCrypto
|
||||
|
||||
describe "rand_string" do
|
||||
describe "rand_string/1" do
|
||||
test "it returns a string of default length" do
|
||||
assert 16 == String.length(FzCrypto.rand_string())
|
||||
end
|
||||
@@ -14,4 +14,27 @@ defmodule FzCommon.FzCryptoTest do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "rand_token/1" do
|
||||
test "returns a token of default length" do
|
||||
# 8 bytes is 12 chars in Base64
|
||||
assert 12 == String.length(FzCrypto.rand_token())
|
||||
end
|
||||
|
||||
test "returns a token of length 4 when bytes is 1" do
|
||||
assert 4 == String.length(FzCrypto.rand_token(1))
|
||||
end
|
||||
|
||||
test "returns a token of length 4 when bytes is 3" do
|
||||
assert 4 == String.length(FzCrypto.rand_token(3))
|
||||
end
|
||||
|
||||
test "returns a token of length 40_000 when bytes is 32_768" do
|
||||
assert 44 == String.length(FzCrypto.rand_token(32))
|
||||
end
|
||||
|
||||
test "returns a token of length 44 when bytes is 32" do
|
||||
assert 43_692 == String.length(FzCrypto.rand_token(32_768))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
31
apps/fz_common/test/fz_integer_text.exs
Normal file
31
apps/fz_common/test/fz_integer_text.exs
Normal file
@@ -0,0 +1,31 @@
|
||||
defmodule FzCommon.FzIntegerTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias FzCommon.FzInteger
|
||||
|
||||
describe "clamp/3" do
|
||||
test "clamps to min" do
|
||||
min = 1
|
||||
max = 5
|
||||
num = 0
|
||||
|
||||
assert 1 == FzInteger.clamp(num, min, max)
|
||||
end
|
||||
|
||||
test "clamps to max" do
|
||||
min = 1
|
||||
max = 5
|
||||
num = 7
|
||||
|
||||
assert 5 == FzInteger.clamp(num, min, max)
|
||||
end
|
||||
|
||||
test "returns num if in range" do
|
||||
min = 1
|
||||
max = 5
|
||||
num = 3
|
||||
|
||||
assert 3 == FzInteger.clamp(num, min, max)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -18,4 +18,18 @@ defmodule FzCommon.FzMapTest do
|
||||
assert FzMap.compact(@data, "") == %{foo: "bar"}
|
||||
end
|
||||
end
|
||||
|
||||
describe "stringify_keys/1" do
|
||||
@data %{foo: "bar", bar: "", map: %{foo: "bar"}}
|
||||
|
||||
test "stringifies the keys" do
|
||||
assert FzMap.stringify_keys(@data) == %{
|
||||
"foo" => "bar",
|
||||
"bar" => "",
|
||||
"map" => %{
|
||||
foo: "bar"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
23
apps/fz_common/test/fz_string_test.exs
Normal file
23
apps/fz_common/test/fz_string_test.exs
Normal file
@@ -0,0 +1,23 @@
|
||||
defmodule FzCommon.FzStringTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias FzCommon.FzString
|
||||
|
||||
describe "to_boolean/1" do
|
||||
test "converts to true" do
|
||||
assert true == FzString.to_boolean("True")
|
||||
end
|
||||
|
||||
test "converts to false" do
|
||||
assert false == FzString.to_boolean("False")
|
||||
end
|
||||
|
||||
test "raises exception on unknowns" do
|
||||
message = "Unknown boolean: string foobar not one of ['true', 'false']."
|
||||
|
||||
assert_raise RuntimeError, message, fn ->
|
||||
FzString.to_boolean("foobar")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
2
apps/fz_http/.gitignore
vendored
2
apps/fz_http/.gitignore
vendored
@@ -30,6 +30,8 @@ npm-debug.log
|
||||
|
||||
# The directory NPM downloads your dependencies sources to.
|
||||
/assets/node_modules/
|
||||
/assets/lib/node_modules/
|
||||
/assets/bin/
|
||||
|
||||
# Since we are building assets from assets/,
|
||||
# we ignore priv/static. You may want to comment
|
||||
|
||||
@@ -6,8 +6,16 @@
|
||||
@import "./email.scss";
|
||||
@import "./tables.scss";
|
||||
|
||||
/* Font Awesome */
|
||||
$fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
|
||||
@import "~@fortawesome/fontawesome-free/scss/fontawesome.scss";
|
||||
@import "~@fortawesome/fontawesome-free/scss/solid.scss";
|
||||
@import "~@fortawesome/fontawesome-free/scss/regular.scss";
|
||||
@import "~@fortawesome/fontawesome-free/scss/brands.scss";
|
||||
|
||||
/* Material Design Icons */
|
||||
$mdi-font-path: "~@mdi/font/fonts";
|
||||
@import "~@mdi/font/scss/materialdesignicons.scss";
|
||||
|
||||
/* Bulma Tooltip */
|
||||
@import "~@creativebulma/bulma-tooltip/src/sass/index.sass";
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// its own CSS file.
|
||||
import css from "../css/app.scss"
|
||||
|
||||
import "@fontsource/fira-sans"
|
||||
|
||||
// webpack automatically bundles all modules in your
|
||||
// entry points. Those entry points can be configured
|
||||
// in "webpack.config.js".
|
||||
|
||||
948
apps/fz_http/assets/package-lock.json
generated
948
apps/fz_http/assets/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -21,12 +21,15 @@
|
||||
"phoenix": "file:../../../deps/phoenix",
|
||||
"phoenix_html": "file:../../../deps/phoenix_html",
|
||||
"phoenix_live_view": "file:../../../deps/phoenix_live_view",
|
||||
"qrcode": "^1.4.4"
|
||||
"qrcode": "^1.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/preset-env": "^7.16.0",
|
||||
"@creativebulma/bulma-tooltip": "^1.2.0",
|
||||
"@fontsource/fira-sans": "^4.5.0",
|
||||
"@fortawesome/fontawesome-free": "^5.15.3",
|
||||
"@mdi/font": "^6.5.95",
|
||||
"admin-one-bulma-dashboard": "file:local_modules/admin-one-bulma-dashboard",
|
||||
"autoprefixer": "^9.8.8",
|
||||
"babel-loader": "^8.2.3",
|
||||
|
||||
@@ -6,28 +6,10 @@ defmodule FzHttp.Application do
|
||||
use Application
|
||||
|
||||
def start(_type, _args) do
|
||||
children =
|
||||
case Application.get_env(:fz_http, :minimal) do
|
||||
true ->
|
||||
[
|
||||
FzHttp.Repo,
|
||||
FzHttp.Vault
|
||||
]
|
||||
|
||||
_ ->
|
||||
[
|
||||
FzHttp.Server,
|
||||
FzHttp.Repo,
|
||||
FzHttp.Vault,
|
||||
{Phoenix.PubSub, name: FzHttp.PubSub},
|
||||
FzHttpWeb.Endpoint
|
||||
]
|
||||
end
|
||||
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
# for other strategies and supported options
|
||||
opts = [strategy: :one_for_one, name: FzHttp.Supervisor]
|
||||
Supervisor.start_link(children, opts)
|
||||
Supervisor.start_link(children(), opts)
|
||||
end
|
||||
|
||||
# Tell Phoenix to update the endpoint configuration
|
||||
@@ -36,4 +18,27 @@ defmodule FzHttp.Application do
|
||||
FzHttpWeb.Endpoint.config_change(changed, removed)
|
||||
:ok
|
||||
end
|
||||
|
||||
defp children, do: children(Application.fetch_env!(:fz_http, :supervision_tree_mode))
|
||||
|
||||
defp children(:full) do
|
||||
[
|
||||
FzHttp.Server,
|
||||
FzHttp.Repo,
|
||||
FzHttp.Vault,
|
||||
FzHttpWeb.Endpoint,
|
||||
{Phoenix.PubSub, name: FzHttp.PubSub},
|
||||
FzHttp.ConnectivityCheckService
|
||||
]
|
||||
end
|
||||
|
||||
defp children(:test) do
|
||||
[
|
||||
FzHttp.Server,
|
||||
FzHttp.Repo,
|
||||
FzHttp.Vault,
|
||||
FzHttpWeb.Endpoint,
|
||||
{Phoenix.PubSub, name: FzHttp.PubSub}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
78
apps/fz_http/lib/fz_http/connectivity_check_service.ex
Normal file
78
apps/fz_http/lib/fz_http/connectivity_check_service.ex
Normal file
@@ -0,0 +1,78 @@
|
||||
defmodule FzHttp.ConnectivityCheckService do
|
||||
@moduledoc """
|
||||
A simple GenServer to periodically check for WAN connectivity by issuing
|
||||
POSTs to https://ping[-dev].firez.one/{version}.
|
||||
"""
|
||||
use GenServer
|
||||
|
||||
require Logger
|
||||
|
||||
alias FzHttp.ConnectivityChecks
|
||||
|
||||
def start_link(_) do
|
||||
http_client().start()
|
||||
GenServer.start_link(__MODULE__, %{})
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def init(state) do
|
||||
if enabled?() do
|
||||
send(self(), :perform)
|
||||
:timer.send_interval(interval(), :perform)
|
||||
end
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_info(:perform, _state) do
|
||||
# XXX: Consider passing state here to implement exponential backoff in the
|
||||
# case of errors.
|
||||
{:noreply, post_request()}
|
||||
end
|
||||
|
||||
def post_request, do: post_request(url())
|
||||
|
||||
def post_request(request_url) do
|
||||
body = ""
|
||||
|
||||
case http_client().post(request_url, body) do
|
||||
{:ok, response} ->
|
||||
ConnectivityChecks.create_connectivity_check(%{
|
||||
response_body: response.body,
|
||||
response_code: response.status_code,
|
||||
response_headers: Map.new(response.headers),
|
||||
url: request_url
|
||||
})
|
||||
|
||||
response
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("""
|
||||
An unexpected error occurred while performing a Firezone connectivity check to #{request_url}. Reason: #{error.reason}
|
||||
""")
|
||||
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
defp url do
|
||||
Application.fetch_env!(:fz_http, :connectivity_checks_url) <> version()
|
||||
end
|
||||
|
||||
defp http_client do
|
||||
Application.fetch_env!(:fz_http, :http_client)
|
||||
end
|
||||
|
||||
defp version do
|
||||
Application.spec(:fz_http, :vsn) |> to_string()
|
||||
end
|
||||
|
||||
defp interval do
|
||||
Application.fetch_env!(:fz_http, :connectivity_checks_interval) * 1_000
|
||||
end
|
||||
|
||||
defp enabled? do
|
||||
Application.fetch_env!(:fz_http, :connectivity_checks_enabled)
|
||||
end
|
||||
end
|
||||
138
apps/fz_http/lib/fz_http/connectivity_checks.ex
Normal file
138
apps/fz_http/lib/fz_http/connectivity_checks.ex
Normal file
@@ -0,0 +1,138 @@
|
||||
defmodule FzHttp.ConnectivityChecks do
|
||||
@moduledoc """
|
||||
The ConnectivityChecks context.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias FzHttp.Repo
|
||||
|
||||
alias FzHttp.ConnectivityChecks.ConnectivityCheck
|
||||
|
||||
@doc """
|
||||
Returns the list of connectivity_checks.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_connectivity_checks()
|
||||
[%ConnectivityCheck{}, ...]
|
||||
|
||||
"""
|
||||
def list_connectivity_checks do
|
||||
Repo.all(ConnectivityCheck)
|
||||
end
|
||||
|
||||
def list_connectivity_checks(limit: limit) when is_integer(limit) do
|
||||
Repo.all(
|
||||
from(
|
||||
c in ConnectivityCheck,
|
||||
limit: ^limit,
|
||||
order_by: [desc: :inserted_at]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single connectivity_check.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the ConnectivityCheck does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_connectivity_check!(123)
|
||||
%ConnectivityCheck{}
|
||||
|
||||
iex> get_connectivity_check!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_connectivity_check!(id), do: Repo.get!(ConnectivityCheck, id)
|
||||
|
||||
@doc """
|
||||
Creates a connectivity_check.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> create_connectivity_check(%{field: value})
|
||||
{:ok, %ConnectivityCheck{}}
|
||||
|
||||
iex> create_connectivity_check(%{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_connectivity_check(attrs \\ %{}) do
|
||||
%ConnectivityCheck{}
|
||||
|> ConnectivityCheck.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a connectivity_check.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_connectivity_check(connectivity_check, %{field: new_value})
|
||||
{:ok, %ConnectivityCheck{}}
|
||||
|
||||
iex> update_connectivity_check(connectivity_check, %{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def update_connectivity_check(%ConnectivityCheck{} = connectivity_check, attrs) do
|
||||
connectivity_check
|
||||
|> ConnectivityCheck.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a connectivity_check.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> delete_connectivity_check(connectivity_check)
|
||||
{:ok, %ConnectivityCheck{}}
|
||||
|
||||
iex> delete_connectivity_check(connectivity_check)
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def delete_connectivity_check(%ConnectivityCheck{} = connectivity_check) do
|
||||
Repo.delete(connectivity_check)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking connectivity_check changes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> change_connectivity_check(connectivity_check)
|
||||
%Ecto.Changeset{data: %ConnectivityCheck{}}
|
||||
|
||||
"""
|
||||
def change_connectivity_check(%ConnectivityCheck{} = connectivity_check, attrs \\ %{}) do
|
||||
ConnectivityCheck.changeset(connectivity_check, attrs)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the latest connectivity_check.
|
||||
"""
|
||||
def latest_connectivity_check do
|
||||
Repo.one(
|
||||
from(
|
||||
c in ConnectivityCheck,
|
||||
limit: 1,
|
||||
order_by: [desc: :inserted_at]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the latest connectivity_check's response_body which should contain the resolved public
|
||||
IP.
|
||||
"""
|
||||
def endpoint do
|
||||
case latest_connectivity_check() do
|
||||
nil -> nil
|
||||
connectivity_check -> connectivity_check.response_body
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
defmodule FzHttp.ConnectivityChecks.ConnectivityCheck do
|
||||
@moduledoc """
|
||||
Manages the connectivity_checks table
|
||||
"""
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@url_regex ~r<\Ahttps://ping(?:-dev)?\.firez\.one/\d+\.\d+\.\d+(?:\+git\.\d+\.[0-9a-fA-F]{7,})?\z>
|
||||
|
||||
schema "connectivity_checks" do
|
||||
field :response_body, :string
|
||||
field :response_code, :integer
|
||||
field :response_headers, :map
|
||||
field :url, :string
|
||||
|
||||
timestamps(type: :utc_datetime_usec)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(connectivity_check, attrs) do
|
||||
connectivity_check
|
||||
|> cast(attrs, [:url, :response_body, :response_code, :response_headers])
|
||||
|> validate_required([:url, :response_code])
|
||||
|> validate_format(:url, @url_regex)
|
||||
|> validate_number(:response_code, greater_than_or_equal_to: 100, less_than: 600)
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,7 @@ defmodule FzHttp.Devices do
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias FzCommon.NameGenerator
|
||||
alias FzHttp.{Devices.Device, Repo, Users.User}
|
||||
alias FzHttp.{ConnectivityChecks, Devices.Device, Repo, Settings, Users.User}
|
||||
|
||||
@ipv4_prefix "10.3.2."
|
||||
@ipv6_prefix "fd00:3:2::"
|
||||
@@ -38,8 +38,8 @@ defmodule FzHttp.Devices do
|
||||
Repo.delete(device)
|
||||
end
|
||||
|
||||
def change_device(%Device{} = device) do
|
||||
Device.update_changeset(device, %{})
|
||||
def change_device(%Device{} = device, attrs \\ %{}) do
|
||||
Device.update_changeset(device, attrs)
|
||||
end
|
||||
|
||||
def rand_name do
|
||||
@@ -62,4 +62,34 @@ defmodule FzHttp.Devices do
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def allowed_ips(device) do
|
||||
if device.use_default_allowed_ips do
|
||||
Settings.default_device_allowed_ips()
|
||||
else
|
||||
device.allowed_ips
|
||||
end
|
||||
end
|
||||
|
||||
def dns_servers(device) do
|
||||
if device.use_default_dns_servers do
|
||||
Settings.default_device_dns_servers()
|
||||
else
|
||||
device.dns_servers
|
||||
end
|
||||
end
|
||||
|
||||
def endpoint(device) do
|
||||
if device.use_default_endpoint do
|
||||
Settings.default_device_endpoint() || ConnectivityChecks.endpoint()
|
||||
else
|
||||
device.endpoint
|
||||
end
|
||||
end
|
||||
|
||||
def defaults(changeset) do
|
||||
~w(use_default_allowed_ips use_default_dns_servers use_default_endpoint)a
|
||||
|> Enum.map(fn field -> {field, Device.field(changeset, field)} end)
|
||||
|> Map.new()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,10 +6,13 @@ defmodule FzHttp.Devices.Device do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
import FzCommon.FzNet,
|
||||
import FzHttp.SharedValidators,
|
||||
only: [
|
||||
valid_ip?: 1,
|
||||
valid_cidr?: 1
|
||||
validate_ip: 2,
|
||||
validate_omitted: 2,
|
||||
validate_list_of_ips: 2,
|
||||
validate_no_duplicates: 2,
|
||||
validate_list_of_ips_or_cidrs: 2
|
||||
]
|
||||
|
||||
alias FzHttp.Users.User
|
||||
@@ -17,8 +20,12 @@ defmodule FzHttp.Devices.Device do
|
||||
schema "devices" do
|
||||
field :name, :string
|
||||
field :public_key, :string
|
||||
field :allowed_ips, :string, read_after_writes: true
|
||||
field :dns_servers, :string, read_after_writes: true
|
||||
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 :endpoint, :string
|
||||
field :allowed_ips, :string
|
||||
field :dns_servers, :string
|
||||
field :private_key, FzHttp.Encrypted.Binary
|
||||
field :server_public_key, :string
|
||||
field :remote_ip, EctoNetwork.INET
|
||||
@@ -32,25 +39,30 @@ defmodule FzHttp.Devices.Device do
|
||||
|
||||
def create_changeset(device, attrs) do
|
||||
device
|
||||
|> cast(attrs, [
|
||||
:allowed_ips,
|
||||
:dns_servers,
|
||||
:remote_ip,
|
||||
:address,
|
||||
:server_public_key,
|
||||
:private_key,
|
||||
:user_id,
|
||||
:name,
|
||||
:public_key
|
||||
])
|
||||
|> shared_cast(attrs)
|
||||
|> shared_changeset()
|
||||
end
|
||||
|
||||
def update_changeset(device, attrs) do
|
||||
device
|
||||
|> shared_cast(attrs)
|
||||
|> shared_changeset()
|
||||
|> validate_required(:address)
|
||||
end
|
||||
|
||||
def field(changeset, field) do
|
||||
get_field(changeset, field)
|
||||
end
|
||||
|
||||
defp shared_cast(device, attrs) do
|
||||
device
|
||||
|> cast(attrs, [
|
||||
:use_default_allowed_ips,
|
||||
:use_default_dns_servers,
|
||||
:use_default_endpoint,
|
||||
:allowed_ips,
|
||||
:dns_servers,
|
||||
:endpoint,
|
||||
:remote_ip,
|
||||
:address,
|
||||
:server_public_key,
|
||||
@@ -59,8 +71,6 @@ defmodule FzHttp.Devices.Device do
|
||||
:name,
|
||||
:public_key
|
||||
])
|
||||
|> shared_changeset()
|
||||
|> validate_required(:address)
|
||||
end
|
||||
|
||||
defp shared_changeset(changeset) do
|
||||
@@ -72,9 +82,12 @@ 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_list_of_ips_or_cidrs(:allowed_ips)
|
||||
|> validate_list_of_ips(:dns_servers)
|
||||
|> validate_no_duplicates(:dns_servers)
|
||||
|> validate_ip(:endpoint)
|
||||
|> unique_constraint(:address)
|
||||
|> validate_number(:address, greater_than_or_equal_to: 2, less_than_or_equal_to: 254)
|
||||
|> unique_constraint(:public_key)
|
||||
@@ -82,66 +95,25 @@ defmodule FzHttp.Devices.Device do
|
||||
|> unique_constraint([:user_id, :name])
|
||||
end
|
||||
|
||||
defp validate_no_duplicates(changeset, field) when is_atom(field) do
|
||||
validate_change(changeset, field, fn _current_field, value ->
|
||||
try do
|
||||
trimmed = Enum.map(String.split(value, ","), fn el -> String.trim(el) end)
|
||||
dupes = Enum.uniq(trimmed -- Enum.uniq(trimmed))
|
||||
defp validate_omitted_if_default(changeset, fields) when is_list(fields) do
|
||||
fields_to_validate =
|
||||
defaulted_fields(changeset, fields)
|
||||
|> Enum.map(fn field ->
|
||||
String.trim(Atom.to_string(field), "use_default_") |> String.to_atom()
|
||||
end)
|
||||
|
||||
if length(dupes) > 0 do
|
||||
throw(dupes)
|
||||
end
|
||||
|
||||
[]
|
||||
catch
|
||||
dupes ->
|
||||
[
|
||||
{field,
|
||||
"is invalid: duplicate DNS servers are not allowed: #{Enum.join(dupes, ", ")}"}
|
||||
]
|
||||
end
|
||||
end)
|
||||
validate_omitted(changeset, fields_to_validate)
|
||||
end
|
||||
|
||||
defp validate_list_of_ips(changeset, field) when is_atom(field) do
|
||||
validate_change(changeset, field, fn _current_field, value ->
|
||||
try do
|
||||
for ip <- String.split(value, ",") do
|
||||
unless valid_ip?(String.trim(ip)) do
|
||||
throw(ip)
|
||||
end
|
||||
end
|
||||
|
||||
[]
|
||||
catch
|
||||
ip ->
|
||||
[{field, "is invalid: #{String.trim(ip)} is not a valid IPv4 / IPv6 address"}]
|
||||
end
|
||||
end)
|
||||
defp validate_required_unless_default(changeset, fields) when is_list(fields) do
|
||||
fields_as_atoms = Enum.map(fields, fn field -> String.to_atom("use_default_#{field}") end)
|
||||
fields_to_validate = fields_as_atoms -- defaulted_fields(changeset, fields)
|
||||
validate_required(changeset, fields_to_validate)
|
||||
end
|
||||
|
||||
defp validate_list_of_ips_or_cidrs(changeset, field) when is_atom(field) do
|
||||
validate_change(changeset, field, fn _current_field, value ->
|
||||
try do
|
||||
for ip_or_cidr <- String.split(value, ",") do
|
||||
trimmed_ip_or_cidr = String.trim(ip_or_cidr)
|
||||
|
||||
unless valid_ip?(trimmed_ip_or_cidr) or valid_cidr?(trimmed_ip_or_cidr) do
|
||||
throw(ip_or_cidr)
|
||||
end
|
||||
end
|
||||
|
||||
[]
|
||||
catch
|
||||
ip_or_cidr ->
|
||||
[
|
||||
{field,
|
||||
"""
|
||||
is invalid: #{String.trim(ip_or_cidr)} is not a valid IPv4 / IPv6 address or \
|
||||
CIDR range\
|
||||
"""}
|
||||
]
|
||||
end
|
||||
end)
|
||||
defp defaulted_fields(changeset, fields) do
|
||||
fields
|
||||
|> Enum.map(fn field -> String.to_atom("use_default_#{field}") end)
|
||||
|> Enum.filter(fn field -> get_field(changeset, field) end)
|
||||
end
|
||||
end
|
||||
|
||||
17
apps/fz_http/lib/fz_http/macros.ex
Normal file
17
apps/fz_http/lib/fz_http/macros.ex
Normal file
@@ -0,0 +1,17 @@
|
||||
defmodule FzHttp.Macros do
|
||||
@moduledoc """
|
||||
Metaprogramming macros
|
||||
"""
|
||||
|
||||
defmacro def_settings(keys) do
|
||||
quote bind_quoted: [keys: keys] do
|
||||
Enum.each(keys, fn key ->
|
||||
fun_name = key |> String.replace(".", "_") |> String.to_atom() |> Macro.var(__MODULE__)
|
||||
|
||||
def unquote(fun_name) do
|
||||
get_setting!(key: unquote(key)).value
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
32
apps/fz_http/lib/fz_http/mock_http_client.ex
Normal file
32
apps/fz_http/lib/fz_http/mock_http_client.ex
Normal file
@@ -0,0 +1,32 @@
|
||||
defmodule FzHttp.MockHttpClient do
|
||||
@moduledoc """
|
||||
Mocks http requests in place of HTTPoison
|
||||
"""
|
||||
|
||||
@success_response {
|
||||
:ok,
|
||||
%{
|
||||
headers: [
|
||||
{"content-length", 9},
|
||||
{"date", "Tue, 07 Dec 2021 19:57:02 GMT"}
|
||||
],
|
||||
status_code: 200,
|
||||
body: "127.0.0.1"
|
||||
}
|
||||
}
|
||||
@error_sentinel "invalid-url"
|
||||
@error_response {:error, %{reason: :nxdomain}}
|
||||
|
||||
def start, do: nil
|
||||
|
||||
@doc """
|
||||
Simulates a POST. Include @error_sentinel in the request URL to simulate an error.
|
||||
"""
|
||||
def post(url, _body) do
|
||||
if String.contains?(url, @error_sentinel) do
|
||||
@error_response
|
||||
else
|
||||
@success_response
|
||||
end
|
||||
end
|
||||
end
|
||||
94
apps/fz_http/lib/fz_http/settings.ex
Normal file
94
apps/fz_http/lib/fz_http/settings.ex
Normal file
@@ -0,0 +1,94 @@
|
||||
defmodule FzHttp.Settings do
|
||||
@moduledoc """
|
||||
The Settings context.
|
||||
"""
|
||||
|
||||
import FzHttp.Macros
|
||||
import Ecto.Query, warn: false
|
||||
alias FzHttp.Repo
|
||||
|
||||
alias FzHttp.Settings.Setting
|
||||
|
||||
def_settings(~w(
|
||||
default.device.allowed_ips
|
||||
default.device.dns_servers
|
||||
default.device.endpoint
|
||||
))
|
||||
|
||||
@doc """
|
||||
Returns the list of settings.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_settings()
|
||||
[%Setting{}, ...]
|
||||
|
||||
"""
|
||||
def list_settings do
|
||||
Repo.all(Setting)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single setting by its ID.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the Setting does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_setting!(123)
|
||||
%Setting{}
|
||||
|
||||
iex> get_setting!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_setting!(key: key) do
|
||||
Repo.one!(from s in Setting, where: s.key == ^key)
|
||||
end
|
||||
|
||||
def get_setting!(id), do: Repo.get!(Setting, id)
|
||||
|
||||
@doc """
|
||||
Updates a setting.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_setting(setting, %{field: new_value})
|
||||
{:ok, %Setting{}}
|
||||
|
||||
iex> update_setting(setting, %{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def update_setting(%Setting{} = setting, attrs) do
|
||||
setting
|
||||
|> Setting.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def update_setting(key, value) when is_binary(key) do
|
||||
get_setting!(key: key)
|
||||
|> update_setting(%{value: value})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking setting changes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> change_setting(setting)
|
||||
%Ecto.Changeset{data: %Setting{}}
|
||||
|
||||
"""
|
||||
def change_setting(%Setting{} = setting, attrs \\ %{}) do
|
||||
Setting.changeset(setting, attrs)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a list of all the settings beginning with the specified key prefix.
|
||||
"""
|
||||
def to_list(prefix \\ "") do
|
||||
starts_with = prefix <> "%"
|
||||
Repo.all(from s in Setting, where: ilike(s.key, ^starts_with))
|
||||
end
|
||||
end
|
||||
69
apps/fz_http/lib/fz_http/settings/setting.ex
Normal file
69
apps/fz_http/lib/fz_http/settings/setting.ex
Normal file
@@ -0,0 +1,69 @@
|
||||
defmodule FzHttp.Settings.Setting do
|
||||
@moduledoc """
|
||||
Represents Firezone runtime configuration settings.
|
||||
|
||||
Each record in the table has a unique key corresponding to a configuration setting.
|
||||
|
||||
Settings values can be changed at application runtime on the fly.
|
||||
Settings cannot be created or destroyed by the running application.
|
||||
|
||||
Settings are created / destroyed in migrations.
|
||||
"""
|
||||
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
import FzHttp.SharedValidators,
|
||||
only: [
|
||||
validate_list_of_ips: 2,
|
||||
validate_list_of_ips_or_cidrs: 2,
|
||||
validate_no_duplicates: 2
|
||||
]
|
||||
|
||||
schema "settings" do
|
||||
field :key, :string
|
||||
field :value, :string
|
||||
|
||||
timestamps(type: :utc_datetime_usec)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(setting, attrs) do
|
||||
setting
|
||||
|> cast(attrs, [:key, :value])
|
||||
|> validate_required([:key])
|
||||
|> validate_setting()
|
||||
end
|
||||
|
||||
defp validate_setting(%{data: %{key: key}, changes: %{value: _value}} = changeset) do
|
||||
changeset
|
||||
|> validate_kv_pair(key)
|
||||
end
|
||||
|
||||
defp validate_setting(changeset), do: changeset
|
||||
|
||||
defp validate_kv_pair(changeset, "default.device.dns_servers") do
|
||||
changeset
|
||||
|> validate_list_of_ips(:value)
|
||||
|> validate_no_duplicates(:value)
|
||||
end
|
||||
|
||||
defp validate_kv_pair(changeset, "default.device.allowed_ips") do
|
||||
changeset
|
||||
|> validate_required(:value)
|
||||
|> validate_list_of_ips_or_cidrs(:value)
|
||||
|> validate_no_duplicates(:value)
|
||||
end
|
||||
|
||||
defp validate_kv_pair(changeset, "default.device.endpoint") do
|
||||
changeset
|
||||
|> validate_list_of_ips_or_cidrs(:value)
|
||||
|> validate_no_duplicates(:value)
|
||||
end
|
||||
|
||||
defp validate_kv_pair(changeset, unknown_key) do
|
||||
validate_change(changeset, :key, fn _current_field, _value ->
|
||||
[{:key, "is invalid: #{unknown_key} is not a valid setting"}]
|
||||
end)
|
||||
end
|
||||
end
|
||||
109
apps/fz_http/lib/fz_http/shared_validators.ex
Normal file
109
apps/fz_http/lib/fz_http/shared_validators.ex
Normal file
@@ -0,0 +1,109 @@
|
||||
defmodule FzHttp.SharedValidators do
|
||||
@moduledoc """
|
||||
Shared validators to use between schemas.
|
||||
"""
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
import FzCommon.FzNet,
|
||||
only: [
|
||||
valid_ip?: 1,
|
||||
valid_cidr?: 1
|
||||
]
|
||||
|
||||
def validate_no_duplicates(changeset, field) when is_atom(field) do
|
||||
validate_change(changeset, field, fn _current_field, value ->
|
||||
try do
|
||||
trimmed = Enum.map(String.split(value, ","), fn el -> String.trim(el) end)
|
||||
dupes = Enum.uniq(trimmed -- Enum.uniq(trimmed))
|
||||
|
||||
if length(dupes) > 0 do
|
||||
throw(dupes)
|
||||
end
|
||||
|
||||
[]
|
||||
catch
|
||||
dupes ->
|
||||
[
|
||||
{field,
|
||||
"is invalid: duplicate DNS servers are not allowed: #{Enum.join(dupes, ", ")}"}
|
||||
]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def validate_ip(changeset, field) when is_atom(field) do
|
||||
validate_change(changeset, field, fn _current_field, value ->
|
||||
try do
|
||||
for ip <- String.split(value, ",") do
|
||||
unless valid_ip?(String.trim(ip)) do
|
||||
throw(ip)
|
||||
end
|
||||
end
|
||||
|
||||
[]
|
||||
catch
|
||||
ip ->
|
||||
[{field, "is invalid: #{String.trim(ip)} is not a valid IPv4 / IPv6 address"}]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def validate_list_of_ips(changeset, field) when is_atom(field) do
|
||||
validate_change(changeset, field, fn _current_field, value ->
|
||||
try do
|
||||
for ip <- String.split(value, ",") do
|
||||
unless valid_ip?(String.trim(ip)) do
|
||||
throw(ip)
|
||||
end
|
||||
end
|
||||
|
||||
[]
|
||||
catch
|
||||
ip ->
|
||||
[{field, "is invalid: #{String.trim(ip)} is not a valid IPv4 / IPv6 address"}]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def validate_list_of_ips_or_cidrs(changeset, field) when is_atom(field) do
|
||||
validate_change(changeset, field, fn _current_field, value ->
|
||||
try do
|
||||
for ip_or_cidr <- String.split(value, ",") do
|
||||
trimmed_ip_or_cidr = String.trim(ip_or_cidr)
|
||||
|
||||
unless valid_ip?(trimmed_ip_or_cidr) or valid_cidr?(trimmed_ip_or_cidr) do
|
||||
throw(ip_or_cidr)
|
||||
end
|
||||
end
|
||||
|
||||
[]
|
||||
catch
|
||||
ip_or_cidr ->
|
||||
[
|
||||
{field,
|
||||
"""
|
||||
is invalid: #{String.trim(ip_or_cidr)} is not a valid IPv4 / IPv6 address or \
|
||||
CIDR range\
|
||||
"""}
|
||||
]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def validate_omitted(changeset, fields) when is_list(fields) do
|
||||
Enum.reduce(fields, changeset, fn field, accumulated_changeset ->
|
||||
validate_omitted(accumulated_changeset, field)
|
||||
end)
|
||||
end
|
||||
|
||||
def validate_omitted(changeset, field) when is_atom(field) do
|
||||
validate_change(changeset, field, fn _current_field, value ->
|
||||
if is_nil(value) do
|
||||
[]
|
||||
else
|
||||
[{field, "must not be present"}]
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
@@ -10,7 +10,6 @@ defmodule FzHttp.Users.User do
|
||||
import Ecto.Changeset
|
||||
import FzHttp.Users.PasswordHelpers
|
||||
|
||||
alias FzCommon.FzMap
|
||||
alias FzHttp.Devices.Device
|
||||
|
||||
schema "users" do
|
||||
@@ -62,7 +61,7 @@ defmodule FzHttp.Users.User do
|
||||
|> validate_required([:sign_in_token, :sign_in_token_created_at])
|
||||
end
|
||||
|
||||
# Password updated with user logged in
|
||||
# If password isn't being changed, remove it from list of attributes to validate
|
||||
def update_changeset(
|
||||
user,
|
||||
%{
|
||||
@@ -71,16 +70,24 @@ defmodule FzHttp.Users.User do
|
||||
"current_password" => nil
|
||||
} = attrs
|
||||
) do
|
||||
update_changeset(user, FzMap.compact(attrs))
|
||||
update_changeset(
|
||||
user,
|
||||
Map.drop(attrs, ["password", "password_confirmation", "current_password"])
|
||||
)
|
||||
end
|
||||
|
||||
# If password isn't being changed, remove it from list of attributes to validate
|
||||
def update_changeset(
|
||||
user,
|
||||
%{"password" => "", "password_confirmation" => "", "current_password" => ""} = attrs
|
||||
) do
|
||||
update_changeset(user, FzMap.compact(attrs, ""))
|
||||
update_changeset(
|
||||
user,
|
||||
Map.drop(attrs, ["password", "password_confirmation", "current_password"])
|
||||
)
|
||||
end
|
||||
|
||||
# Password and other fields are being changed
|
||||
def update_changeset(
|
||||
user,
|
||||
%{
|
||||
@@ -109,7 +116,7 @@ defmodule FzHttp.Users.User do
|
||||
"password_confirmation" => ""
|
||||
} = attrs
|
||||
) do
|
||||
update_changeset(user, FzMap.compact(attrs, ""))
|
||||
update_changeset(user, Map.drop(attrs, ["password", "password_confirmation"]))
|
||||
end
|
||||
|
||||
# Password updated from token or admin
|
||||
|
||||
@@ -6,7 +6,7 @@ defmodule FzHttpWeb.AccountLive.Show do
|
||||
|
||||
alias FzHttp.Users
|
||||
|
||||
@impl true
|
||||
@impl Phoenix.LiveView
|
||||
def mount(params, session, socket) do
|
||||
{:ok,
|
||||
socket
|
||||
@@ -14,7 +14,7 @@ defmodule FzHttpWeb.AccountLive.Show do
|
||||
|> assign(:page_title, "Account")}
|
||||
end
|
||||
|
||||
@impl true
|
||||
@impl Phoenix.LiveView
|
||||
def handle_params(_params, _url, socket) do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<%= render FzHttpWeb.SharedView, "heading.html", page_title: @page_title %>
|
||||
|
||||
<section class="section is-main-section">
|
||||
<%= render FzHttpWeb.SharedView, "flash.html", assigns %>
|
||||
|
||||
<div class="content">
|
||||
<p>
|
||||
Firezone periodically checks for WAN connectivity to the Internet and logs
|
||||
the result here. This is used to determine the public IP address of this
|
||||
server for populating the default endpoint field in device configurations.
|
||||
</p>
|
||||
<table class="table is-bordered is-hoverable is-striped is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Checked At</th>
|
||||
<th>Resolved IP</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%= for connectivity_check <- @connectivity_checks do %>
|
||||
<tr>
|
||||
<td id={"connectivity_check-#{connectivity_check.id}"}
|
||||
phx-hook="FormatTimestamp"
|
||||
data-timestamp={connectivity_check.inserted_at}>
|
||||
…
|
||||
</td>
|
||||
<td><%= connectivity_check.response_body %></td>
|
||||
<td>
|
||||
<span
|
||||
data-tooltip={"HTTP Response Code: #{connectivity_check.response_code}"}
|
||||
class={connectivity_check_span_class(connectivity_check.response_code)}>
|
||||
<i class={connectivity_check_icon_class(connectivity_check.response_code)}></i>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,20 @@
|
||||
defmodule FzHttpWeb.ConnectivityCheckLive.Index do
|
||||
@moduledoc """
|
||||
Manages the connectivity_checks view.
|
||||
"""
|
||||
use FzHttpWeb, :live_view
|
||||
|
||||
alias FzHttp.ConnectivityChecks
|
||||
|
||||
@impl Phoenix.LiveView
|
||||
def mount(params, session, socket) do
|
||||
{:ok,
|
||||
socket
|
||||
|> assign_defaults(params, session, &load_data/2)
|
||||
|> assign(:page_title, "Connectivity Checks")}
|
||||
end
|
||||
|
||||
defp load_data(_params, socket) do
|
||||
assign(socket, :connectivity_checks, ConnectivityChecks.list_connectivity_checks(limit: 20))
|
||||
end
|
||||
end
|
||||
@@ -4,17 +4,32 @@ defmodule FzHttpWeb.DeviceLive.FormComponent do
|
||||
"""
|
||||
use FzHttpWeb, :live_component
|
||||
|
||||
alias FzHttp.Devices
|
||||
alias FzHttp.{ConnectivityChecks, Devices, Settings}
|
||||
|
||||
def update(assigns, socket) do
|
||||
changeset = Devices.change_device(assigns.device)
|
||||
device = assigns.device
|
||||
changeset = Devices.change_device(device)
|
||||
default_device_endpoint = Settings.default_device_endpoint() || ConnectivityChecks.endpoint()
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(assigns)
|
||||
|> assign(Devices.defaults(changeset))
|
||||
|> 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(:changeset, changeset)}
|
||||
end
|
||||
|
||||
def handle_event("change", %{"device" => device_params}, socket) do
|
||||
changeset = Devices.change_device(socket.assigns.device, device_params)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:changeset, changeset)
|
||||
|> assign(Devices.defaults(changeset))}
|
||||
end
|
||||
|
||||
def handle_event("save", %{"device" => device_params}, socket) do
|
||||
device = socket.assigns.device
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<div>
|
||||
<.form let={f} for={@changeset} id="edit-device" phx-target={@myself} phx-submit="save">
|
||||
<.form let={f} for={@changeset} id="edit-device" phx-change="change" phx-target={@myself} phx-submit="save">
|
||||
<div class="field">
|
||||
<%= label f, :name, class: "label" %>
|
||||
|
||||
<div class="control">
|
||||
<%= text_input f, :name, class: "input" %>
|
||||
</div>
|
||||
@@ -12,10 +11,26 @@
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= label f, :allowed_ips, "Allowed IPs", class: "label" %>
|
||||
|
||||
<%= label f, :use_default_allowed_ips, "Use Default Allowed IPs", class: "label" %>
|
||||
<div class="control">
|
||||
<%= text_input f, :allowed_ips, class: "input" %>
|
||||
<label class="radio">
|
||||
<%= radio_button f, :use_default_allowed_ips, true %>
|
||||
Yes
|
||||
</label>
|
||||
<label class="radio">
|
||||
<%= radio_button f, :use_default_allowed_ips, false %>
|
||||
No
|
||||
</label>
|
||||
</div>
|
||||
<p class="help">
|
||||
Default: <%= @default_device_allowed_ips %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= label f, :allowed_ips, "Allowed IPs", class: "label" %>
|
||||
<div class="control">
|
||||
<%= text_input f, :allowed_ips, class: "input", disabled: @use_default_allowed_ips %>
|
||||
</div>
|
||||
<p class="help is-danger">
|
||||
<%= error_tag f, :allowed_ips %>
|
||||
@@ -23,16 +38,60 @@
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= label f, :dns_servers, "DNS Servers", class: "label" %>
|
||||
|
||||
<%= label f, :use_default_dns_servers, "Use Default DNS Servers", class: "label" %>
|
||||
<div class="control">
|
||||
<%= text_input f, :dns_servers, class: "input" %>
|
||||
<label class="radio">
|
||||
<%= radio_button f, :use_default_dns_servers, true %>
|
||||
Yes
|
||||
</label>
|
||||
<label class="radio">
|
||||
<%= radio_button f, :use_default_dns_servers, false %>
|
||||
No
|
||||
</label>
|
||||
</div>
|
||||
<p class="help">
|
||||
Default: <%= @default_device_dns_servers %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= label f, :dns_servers, "DNS Servers", class: "label" %>
|
||||
<div class="control">
|
||||
<%= text_input f, :dns_servers, class: "input", disabled: @use_default_dns_servers %>
|
||||
</div>
|
||||
<p class="help is-danger">
|
||||
<%= error_tag f, :dns_servers %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= label f, :use_default_endpoint, "Use Default Endpoint", class: "label" %>
|
||||
<div class="control">
|
||||
<label class="radio">
|
||||
<%= radio_button f, :use_default_endpoint, true %>
|
||||
Yes
|
||||
</label>
|
||||
<label class="radio">
|
||||
<%= radio_button f, :use_default_endpoint, false %>
|
||||
No
|
||||
</label>
|
||||
</div>
|
||||
<p class="help">
|
||||
Default: <%= @default_device_endpoint %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= label f, :endpoint, "Server Endpoint", class: "label" %>
|
||||
<p>The IP of the server this device should connect to.</p>
|
||||
<div class="control">
|
||||
<%= text_input f, :endpoint, class: "input", disabled: @use_default_endpoint %>
|
||||
</div>
|
||||
<p class="help is-danger">
|
||||
<%= error_tag f, :endpoint %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= label f, :address, "Interface Address (last octet only)", class: "label" %>
|
||||
|
||||
|
||||
@@ -34,10 +34,13 @@
|
||||
</dd>
|
||||
|
||||
<dt><strong>Allowed IPs</strong></dt>
|
||||
<dd><%= @device.allowed_ips %></dd>
|
||||
<dd><%= @allowed_ips %></dd>
|
||||
|
||||
<dt><strong>DNS Servers</strong></dt>
|
||||
<dd><%= @device.dns_servers %></dd>
|
||||
<dd><%= @dns_servers || "None" %></dd>
|
||||
|
||||
<dt><strong>Endpoint</strong></dt>
|
||||
<dd><%= @endpoint %></dd>
|
||||
|
||||
<dt><strong>Public key</strong></dt>
|
||||
<dd class="code"><%= @device.public_key %></dd>
|
||||
@@ -88,12 +91,12 @@
|
||||
[Interface]
|
||||
PrivateKey = <%= @device.private_key %>
|
||||
Address = <%= FzHttp.Devices.ipv4_address(@device) %>/32, <%= FzHttp.Devices.ipv6_address(@device) %>/128
|
||||
DNS = <%= @device.dns_servers %>
|
||||
<%= @dns_servers %>
|
||||
|
||||
[Peer]
|
||||
PublicKey = <%= @device.server_public_key %>
|
||||
AllowedIPs = <%= @device.allowed_ips %>
|
||||
Endpoint = <%= @wireguard_endpoint %>:<%= @wireguard_port %></code></pre>
|
||||
AllowedIPs = <%= @allowed_ips %>
|
||||
Endpoint = <%= @endpoint %>:<%= @wireguard_port %></code></pre>
|
||||
<hr>
|
||||
<h6 class="is-6 title">
|
||||
Or scan the QR code with your mobile phone:
|
||||
|
||||
@@ -51,11 +51,34 @@ defmodule FzHttpWeb.DeviceLive.Show do
|
||||
device: device,
|
||||
user: Users.get_user!(device.user_id),
|
||||
page_title: device.name,
|
||||
wireguard_endpoint: Application.fetch_env!(:fz_vpn, :wireguard_endpoint),
|
||||
allowed_ips: Devices.allowed_ips(device),
|
||||
dns_servers: dns_servers(device),
|
||||
endpoint: Devices.endpoint(device),
|
||||
wireguard_port: Application.fetch_env!(:fz_vpn, :wireguard_port)
|
||||
)
|
||||
else
|
||||
not_authorized(socket)
|
||||
end
|
||||
end
|
||||
|
||||
defp dns_servers(device) when is_struct(device) do
|
||||
dns_servers = Devices.dns_servers(device)
|
||||
|
||||
if dns_servers_empty?(dns_servers) do
|
||||
""
|
||||
else
|
||||
"DNS = #{dns_servers}"
|
||||
end
|
||||
end
|
||||
|
||||
defp dns_servers_empty?(nil), do: true
|
||||
|
||||
defp dns_servers_empty?(dns_servers) when is_binary(dns_servers) do
|
||||
len =
|
||||
dns_servers
|
||||
|> String.trim()
|
||||
|> String.length()
|
||||
|
||||
len == 0
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<%= render FzHttpWeb.SharedView, "heading.html", page_title: @page_title %>
|
||||
|
||||
<section class="section is-main-section">
|
||||
<%= render FzHttpWeb.SharedView, "flash.html", assigns %>
|
||||
|
||||
<div class="tile is-ancestor is-flex-wrap-wrap">
|
||||
<div class="tile is-parent">
|
||||
<div class="card tile is-child">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Device</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<%= live_component(
|
||||
@socket,
|
||||
FzHttpWeb.SettingLive.FormComponent,
|
||||
label_text: "Allowed IPs",
|
||||
placeholder: nil,
|
||||
changeset: @changesets["default.device.allowed_ips"],
|
||||
help_text: @help_texts.allowed_ips,
|
||||
id: :allowed_ips_form_component) %>
|
||||
|
||||
<hr>
|
||||
|
||||
<%= live_component(
|
||||
@socket,
|
||||
FzHttpWeb.SettingLive.FormComponent,
|
||||
label_text: "DNS Servers",
|
||||
placeholder: nil,
|
||||
changeset: @changesets["default.device.dns_servers"],
|
||||
help_text: @help_texts.dns_servers,
|
||||
id: :dns_servers_form_component) %>
|
||||
|
||||
<hr>
|
||||
|
||||
<%= live_component(
|
||||
@socket,
|
||||
FzHttpWeb.SettingLive.FormComponent,
|
||||
label_text: "Endpoint",
|
||||
placeholder: @endpoint_placeholder,
|
||||
changeset: @changesets["default.device.endpoint"],
|
||||
help_text: @help_texts.endpoint,
|
||||
id: :endpoint_form_component) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,46 @@
|
||||
defmodule FzHttpWeb.SettingLive.Default do
|
||||
@moduledoc """
|
||||
Manages the defaults view.
|
||||
"""
|
||||
use FzHttpWeb, :live_view
|
||||
|
||||
alias FzHttp.{ConnectivityChecks, Settings}
|
||||
|
||||
@help_texts %{
|
||||
allowed_ips: """
|
||||
Configures the default AllowedIPs setting for devices.
|
||||
AllowedIPs determines which destination IPs get routed through
|
||||
Firezone. Specify a comma-separated list of IPs or CIDRs here to achieve split tunneling, or use
|
||||
<code>0.0.0.0/0, ::/0</code> to route all device traffic through this Firezone server.
|
||||
""",
|
||||
dns_servers: """
|
||||
Comma-separated list of DNS servers to use for devices.
|
||||
Leaving this blank will omit the <code>DNS</code> section in
|
||||
generated device configs.
|
||||
""",
|
||||
endpoint: """
|
||||
IPv4 or IPv6 address that devices will be configured to connect
|
||||
to. Defaults to this server's public IP if not set.
|
||||
"""
|
||||
}
|
||||
|
||||
@impl Phoenix.LiveView
|
||||
def mount(params, session, socket) do
|
||||
{:ok,
|
||||
socket
|
||||
|> assign_defaults(params, session)
|
||||
|> assign(:help_texts, @help_texts)
|
||||
|> assign(:changesets, load_changesets())
|
||||
|> assign(:endpoint_placeholder, endpoint_placeholder())
|
||||
|> assign(:page_title, "Default Settings")}
|
||||
end
|
||||
|
||||
defp endpoint_placeholder do
|
||||
ConnectivityChecks.endpoint()
|
||||
end
|
||||
|
||||
defp load_changesets do
|
||||
Settings.to_list("default.")
|
||||
|> Map.new(fn setting -> {setting.key, Settings.change_setting(setting)} end)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,62 @@
|
||||
defmodule FzHttpWeb.SettingLive.FormComponent do
|
||||
@moduledoc """
|
||||
Handles updating setting values, one at a time
|
||||
"""
|
||||
use FzHttpWeb, :live_component
|
||||
|
||||
alias FzHttp.Settings
|
||||
|
||||
@impl Phoenix.LiveComponent
|
||||
def update(assigns, socket) do
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:input_class, "input")
|
||||
|> assign(:form_changed, false)
|
||||
|> assign(:input_icon, "")
|
||||
|> assign(assigns)}
|
||||
end
|
||||
|
||||
@impl Phoenix.LiveComponent
|
||||
def handle_event("save", %{"setting" => %{"value" => value}}, socket) do
|
||||
key = socket.assigns.changeset.data.key
|
||||
|
||||
case Settings.update_setting(key, value) do
|
||||
{:ok, setting} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:input_class, input_class(false))
|
||||
|> assign(:input_icon, input_icon(false))
|
||||
|> assign(:changeset, Settings.change_setting(setting, %{}))}
|
||||
|
||||
{:error, changeset} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:input_class, input_class(true))
|
||||
|> assign(:input_icon, input_icon(true))
|
||||
|> assign(:changeset, changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Phoenix.LiveComponent
|
||||
def handle_event("change", _params, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:form_changed, true)}
|
||||
end
|
||||
|
||||
defp input_icon(false) do
|
||||
"mdi mdi-check-circle"
|
||||
end
|
||||
|
||||
defp input_icon(true) do
|
||||
"mdi mdi-alert-circle"
|
||||
end
|
||||
|
||||
defp input_class(false) do
|
||||
"input is-success"
|
||||
end
|
||||
|
||||
defp input_class(true) do
|
||||
"input is-danger"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
<div>
|
||||
<.form let={f} for={@changeset} id={@id} phx-target={@myself} phx-change="change" phx-submit="save">
|
||||
<div class="field">
|
||||
<%= label f, :value, @label_text, class: "label" %>
|
||||
<div class="field has-addons">
|
||||
<div class="control has-icons-right is-expanded">
|
||||
<%= text_input f, :value, placeholder: @placeholder, class: @input_class %>
|
||||
<span class="icon is-small is-right">
|
||||
<i class={@input_icon}></i>
|
||||
</span>
|
||||
<div class="help is-danger">
|
||||
<%= error_tag f, :value %>
|
||||
</div>
|
||||
<div class="help">
|
||||
<%= raw @help_text %>
|
||||
</div>
|
||||
</div>
|
||||
<%= if @form_changed do %>
|
||||
<div class="control">
|
||||
<%= submit "Save", class: "button is-primary" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</.form>
|
||||
</div>
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule FzHttpWeb.LiveHelpers do
|
||||
@moduledoc """
|
||||
Helpers available to all LiveViews.
|
||||
XXX: Consider splitting these up using one of the techniques at
|
||||
https://bernheisel.com/blog/phoenix-liveview-and-views
|
||||
"""
|
||||
import Phoenix.LiveView
|
||||
import Phoenix.LiveView.Helpers
|
||||
@@ -43,4 +45,33 @@ defmodule FzHttpWeb.LiveHelpers do
|
||||
modal_opts = [id: :modal, return_to: path, component: component, opts: opts]
|
||||
live_component(FzHttpWeb.ModalComponent, modal_opts)
|
||||
end
|
||||
|
||||
def connectivity_check_span_class(response_code) do
|
||||
if http_success?(status_digit(response_code)) do
|
||||
"icon has-text-success"
|
||||
else
|
||||
"icon has-text-danger"
|
||||
end
|
||||
end
|
||||
|
||||
def connectivity_check_icon_class(response_code) do
|
||||
if http_success?(status_digit(response_code)) do
|
||||
"mdi mdi-check-circle"
|
||||
else
|
||||
"mdi mdi-alert-circle"
|
||||
end
|
||||
end
|
||||
|
||||
defp status_digit(response_code) when is_integer(response_code) do
|
||||
[status_digit | _tail] = Integer.digits(response_code)
|
||||
status_digit
|
||||
end
|
||||
|
||||
defp http_success?(2) do
|
||||
true
|
||||
end
|
||||
|
||||
defp http_success?(_) do
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,9 +24,6 @@ defmodule FzHttpWeb.Router do
|
||||
get "/", DeviceController, :index
|
||||
resources "/session", SessionController, only: [:new, :create, :delete], singleton: true
|
||||
|
||||
live "/account", AccountLive.Show, :show
|
||||
live "/account/edit", AccountLive.Show, :edit
|
||||
|
||||
live "/users", UserLive.Index, :index
|
||||
live "/users/new", UserLive.Index, :new
|
||||
live "/users/:id", UserLive.Show, :show
|
||||
@@ -38,6 +35,13 @@ defmodule FzHttpWeb.Router do
|
||||
live "/devices/:id", DeviceLive.Show, :show
|
||||
live "/devices/:id/edit", DeviceLive.Show, :edit
|
||||
|
||||
live "/settings/default", SettingLive.Default, :default
|
||||
|
||||
live "/account", AccountLive.Show, :show
|
||||
live "/account/edit", AccountLive.Show, :edit
|
||||
|
||||
live "/connectivity_checks", ConnectivityCheckLive.Index, :index
|
||||
|
||||
get "/sign_in/:token", SessionController, :create
|
||||
delete "/user", UserController, :delete
|
||||
end
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
<meta name="msapplication-config" content="/browserconfig.xml" />
|
||||
<meta name="msapplication-TileColor" content="331700">
|
||||
<meta name="theme-color" content="331700">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css?family=Fira%20Sans" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
@@ -44,8 +40,8 @@
|
||||
</a>
|
||||
<div class="navbar-dropdown">
|
||||
<%= link(to: Routes.account_show_path(@conn, :show), class: "navbar-item") do %>
|
||||
<span class="icon"><i class="mdi mdi-settings"></i></span>
|
||||
<span>Settings</span>
|
||||
<span class="icon"><i class="mdi mdi-account"></i></span>
|
||||
<span>Account Settings</span>
|
||||
<% end %>
|
||||
<hr class="navbar-divider">
|
||||
<%= link(to: Routes.session_path(@conn, :delete), method: :delete, class: "navbar-item") do %>
|
||||
@@ -78,7 +74,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<%= link(to: Routes.device_index_path(@conn, :index), class: nav_class(@conn.request_path, "devices")) do %>
|
||||
<span class="icon"><i class="mdi mdi-laptop-windows"></i></span>
|
||||
<span class="icon"><i class="mdi mdi-laptop"></i></span>
|
||||
<span class="menu-item-label">Devices</span>
|
||||
<% end %>
|
||||
</li>
|
||||
@@ -91,6 +87,12 @@
|
||||
</ul>
|
||||
<p class="menu-label">Settings</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<%= link(to: Routes.setting_default_path(@conn, :default), class: nav_class(@conn.request_path, "defaults")) do %>
|
||||
<span class="icon"><i class="mdi mdi-cog"></i></span>
|
||||
<span class="menu-item-label">Defaults</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link(to: Routes.account_show_path(@conn, :show), class: nav_class(@conn.request_path, "account")) do %>
|
||||
<span class="icon"><i class="mdi mdi-account"></i></span>
|
||||
@@ -98,6 +100,15 @@
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="menu-label">Diagnostics</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<%= link(to: Routes.connectivity_check_index_path(@conn, :index), class: nav_class(@conn.request_path, "connectivity_checks")) do %>
|
||||
<span class="icon"><i class="mdi mdi-access-point"></i></span>
|
||||
<span class="menu-item-label">Connectivity Checks</span>
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -109,7 +120,7 @@
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<%= link(to: "mailto:" <> feedback_recipient()) do %>
|
||||
Leave us feedback!
|
||||
Click here to leave feedback
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,7 +140,5 @@
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.materialdesignicons.com/4.9.95/css/materialdesignicons.min.css">
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
defmodule FzHttpWeb.AdminView do
|
||||
use FzHttpWeb, :view
|
||||
end
|
||||
@@ -58,6 +58,7 @@ defmodule FzHttp.MixProject do
|
||||
{:cloak_ecto, "~> 1.2"},
|
||||
{:excoveralls, "~> 0.14", only: :test},
|
||||
{:floki, ">= 0.0.0", only: :test},
|
||||
{:httpoison, "~> 1.8"},
|
||||
{:argon2_elixir, "~> 2.0"},
|
||||
{:phoenix_pubsub, "~> 2.0"},
|
||||
{:phoenix_ecto, "~> 4.4"},
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
defmodule FzHttp.Repo.Migrations.CreateConnectivityChecks do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:connectivity_checks) do
|
||||
add :url, :string
|
||||
add :response_body, :string
|
||||
add :response_code, :integer
|
||||
add :response_headers, :map
|
||||
|
||||
timestamps(type: :utc_datetime_usec)
|
||||
end
|
||||
|
||||
create index(:connectivity_checks, :inserted_at)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
defmodule FzHttp.Repo.Migrations.CreateSettings do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:settings) do
|
||||
add :key, :string
|
||||
add :value, :string
|
||||
|
||||
timestamps(type: :utc_datetime_usec)
|
||||
end
|
||||
|
||||
create unique_index(:settings, :key)
|
||||
|
||||
flush()
|
||||
|
||||
now = DateTime.utc_now()
|
||||
|
||||
execute """
|
||||
INSERT INTO settings (key, value, inserted_at, updated_at) VALUES \
|
||||
('default.device.dns_servers', '1.1.1.1, 1.0.0.1', '#{now}', '#{now}'),
|
||||
('default.device.allowed_ips', '0.0.0.0/0, ::/0', '#{now}', '#{now}'),
|
||||
('default.device.endpoint', null, '#{now}', '#{now}')
|
||||
"""
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
defmodule FzHttp.Repo.Migrations.ChangeDeviceColumnDefaults do
|
||||
@moduledoc """
|
||||
Removes the device defaults in favor of using values from the
|
||||
settings table.
|
||||
"""
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table("devices") do
|
||||
add :use_default_endpoint, :boolean, default: true, null: false
|
||||
add :use_default_allowed_ips, :boolean, default: true, null: false
|
||||
add :use_default_dns_servers, :boolean, default: true, null: false
|
||||
add :endpoint, :string, default: nil
|
||||
modify :allowed_ips, :string, default: nil
|
||||
modify :dns_servers, :string, default: nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -10,7 +10,7 @@
|
||||
# We recommend using the bang functions (`insert!`, `update!`
|
||||
# and so on) as they will fail if something goes wrong.
|
||||
|
||||
alias FzHttp.{Devices, Rules, Users}
|
||||
alias FzHttp.{Devices, ConnectivityChecks, Rules, Users}
|
||||
|
||||
{:ok, user} =
|
||||
Users.create_user(%{
|
||||
@@ -34,3 +34,19 @@ alias FzHttp.{Devices, Rules, Users}
|
||||
device_id: device.id,
|
||||
destination: %Postgrex.INET{address: {0, 0, 0, 0}, netmask: 0}
|
||||
})
|
||||
|
||||
{:ok, _connectivity_check} =
|
||||
ConnectivityChecks.create_connectivity_check(%{
|
||||
response_headers: %{"Content-Type" => "text/plain"},
|
||||
response_body: "127.0.0.1",
|
||||
response_code: 200,
|
||||
url: "https://ping-dev.firez.one/0.1.19"
|
||||
})
|
||||
|
||||
{:ok, _connectivity_check} =
|
||||
ConnectivityChecks.create_connectivity_check(%{
|
||||
response_headers: %{"Content-Type" => "text/plain"},
|
||||
response_body: "127.0.0.1",
|
||||
response_code: 400,
|
||||
url: "https://ping-dev.firez.one/0.20.0"
|
||||
})
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
defmodule FzHttp.ConnectivityCheckServiceTest do
|
||||
@moduledoc """
|
||||
Tests the ConnectivityCheckService module.
|
||||
"""
|
||||
alias Ecto.Adapters.SQL.Sandbox
|
||||
alias FzHttp.{ConnectivityChecks, ConnectivityCheckService, Repo}
|
||||
use FzHttp.DataCase, async: true
|
||||
|
||||
describe "post_request/0 valid url" do
|
||||
@expected_check %{
|
||||
response_code: 200,
|
||||
response_headers: %{"content-length" => 9, "date" => "Tue, 07 Dec 2021 19:57:02 GMT"},
|
||||
response_body: "127.0.0.1"
|
||||
}
|
||||
|
||||
test "inserts connectivity check" do
|
||||
ConnectivityCheckService.post_request()
|
||||
assert [@expected_check] = ConnectivityChecks.list_connectivity_checks()
|
||||
end
|
||||
end
|
||||
|
||||
describe "post_request/0 error" do
|
||||
@expected_response %{reason: :nxdomain}
|
||||
@url "invalid-url"
|
||||
|
||||
test "returns error reason" do
|
||||
assert @expected_response = ConnectivityCheckService.post_request(@url)
|
||||
assert ConnectivityChecks.list_connectivity_checks() == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "handle_info/2" do
|
||||
@expected_response %{
|
||||
headers: [
|
||||
{"content-length", 9},
|
||||
{"date", "Tue, 07 Dec 2021 19:57:02 GMT"}
|
||||
],
|
||||
status_code: 200,
|
||||
body: "127.0.0.1"
|
||||
}
|
||||
|
||||
setup _tags do
|
||||
%{test_pid: start_supervised!(ConnectivityCheckService)}
|
||||
end
|
||||
|
||||
test ":perform", %{test_pid: test_pid} do
|
||||
Sandbox.allow(Repo, self(), test_pid)
|
||||
assert @expected_response == :sys.get_state(test_pid)
|
||||
end
|
||||
end
|
||||
end
|
||||
103
apps/fz_http/test/fz_http/connectivity_checks_test.exs
Normal file
103
apps/fz_http/test/fz_http/connectivity_checks_test.exs
Normal file
@@ -0,0 +1,103 @@
|
||||
defmodule FzHttp.ConnectivityChecksTest do
|
||||
use FzHttp.DataCase, async: true
|
||||
|
||||
alias FzHttp.ConnectivityChecks
|
||||
|
||||
describe "connectivity_checks" do
|
||||
alias FzHttp.ConnectivityChecks.ConnectivityCheck
|
||||
|
||||
import FzHttp.ConnectivityChecksFixtures
|
||||
|
||||
@invalid_attrs %{response_body: nil, response_code: nil, response_headers: nil, url: nil}
|
||||
|
||||
test "list_connectivity_checks/0 returns all connectivity_checks" do
|
||||
connectivity_check = connectivity_check_fixture()
|
||||
assert ConnectivityChecks.list_connectivity_checks() == [connectivity_check]
|
||||
end
|
||||
|
||||
test "list_connectivity_checks/1 applies limit" do
|
||||
connectivity_check_fixture()
|
||||
connectivity_check = connectivity_check_fixture()
|
||||
assert ConnectivityChecks.list_connectivity_checks(limit: 1) == [connectivity_check]
|
||||
end
|
||||
|
||||
test "endpoint/0 returns latest check's response body" do
|
||||
connectivity_check = connectivity_check_fixture()
|
||||
assert ConnectivityChecks.endpoint() == connectivity_check.response_body
|
||||
end
|
||||
|
||||
test "get_connectivity_check!/1 returns the connectivity_check with given id" do
|
||||
connectivity_check = connectivity_check_fixture()
|
||||
|
||||
assert ConnectivityChecks.get_connectivity_check!(connectivity_check.id) ==
|
||||
connectivity_check
|
||||
end
|
||||
|
||||
test "create_connectivity_check/1 with valid data creates a connectivity_check" do
|
||||
valid_attrs = %{
|
||||
response_body: "some response_body",
|
||||
response_code: 500,
|
||||
response_headers: %{"updated_response" => "headers"},
|
||||
url: "https://ping-dev.firez.one/1.1.1"
|
||||
}
|
||||
|
||||
assert {:ok, %ConnectivityCheck{} = connectivity_check} =
|
||||
ConnectivityChecks.create_connectivity_check(valid_attrs)
|
||||
|
||||
assert connectivity_check.response_body == "some response_body"
|
||||
assert connectivity_check.response_code == 500
|
||||
assert connectivity_check.response_headers == %{"updated_response" => "headers"}
|
||||
assert connectivity_check.url == "https://ping-dev.firez.one/1.1.1"
|
||||
end
|
||||
|
||||
test "create_connectivity_check/1 with invalid data returns error changeset" do
|
||||
assert {:error, %Ecto.Changeset{}} =
|
||||
ConnectivityChecks.create_connectivity_check(@invalid_attrs)
|
||||
end
|
||||
|
||||
test "update_connectivity_check/2 with valid data updates the connectivity_check" do
|
||||
connectivity_check = connectivity_check_fixture()
|
||||
|
||||
update_attrs = %{
|
||||
response_body: "some updated response_body",
|
||||
response_code: 500,
|
||||
response_headers: %{"updated" => "response headers"},
|
||||
url: "https://ping.firez.one/6.6.6"
|
||||
}
|
||||
|
||||
assert {:ok, %ConnectivityCheck{} = connectivity_check} =
|
||||
ConnectivityChecks.update_connectivity_check(connectivity_check, update_attrs)
|
||||
|
||||
assert connectivity_check.response_body == "some updated response_body"
|
||||
assert connectivity_check.response_code == 500
|
||||
assert connectivity_check.response_headers == %{"updated" => "response headers"}
|
||||
assert connectivity_check.url == "https://ping.firez.one/6.6.6"
|
||||
end
|
||||
|
||||
test "update_connectivity_check/2 with invalid data returns error changeset" do
|
||||
connectivity_check = connectivity_check_fixture()
|
||||
|
||||
assert {:error, %Ecto.Changeset{}} =
|
||||
ConnectivityChecks.update_connectivity_check(connectivity_check, @invalid_attrs)
|
||||
|
||||
assert connectivity_check ==
|
||||
ConnectivityChecks.get_connectivity_check!(connectivity_check.id)
|
||||
end
|
||||
|
||||
test "delete_connectivity_check/1 deletes the connectivity_check" do
|
||||
connectivity_check = connectivity_check_fixture()
|
||||
|
||||
assert {:ok, %ConnectivityCheck{}} =
|
||||
ConnectivityChecks.delete_connectivity_check(connectivity_check)
|
||||
|
||||
assert_raise Ecto.NoResultsError, fn ->
|
||||
ConnectivityChecks.get_connectivity_check!(connectivity_check.id)
|
||||
end
|
||||
end
|
||||
|
||||
test "change_connectivity_check/1 returns a connectivity_check changeset" do
|
||||
connectivity_check = connectivity_check_fixture()
|
||||
assert %Ecto.Changeset{} = ConnectivityChecks.change_connectivity_check(connectivity_check)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -52,10 +52,12 @@ defmodule FzHttp.DevicesTest do
|
||||
|
||||
@attrs %{
|
||||
name: "Go hard or go home.",
|
||||
allowed_ips: "0.0.0.0"
|
||||
allowed_ips: "0.0.0.0",
|
||||
use_default_allowed_ips: false
|
||||
}
|
||||
|
||||
@valid_dns_servers_attrs %{
|
||||
use_default_dns_servers: false,
|
||||
dns_servers: "1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001"
|
||||
}
|
||||
|
||||
@@ -68,6 +70,7 @@ defmodule FzHttp.DevicesTest do
|
||||
}
|
||||
|
||||
@valid_allowed_ips_attrs %{
|
||||
use_default_allowed_ips: false,
|
||||
allowed_ips: "0.0.0.0/0, ::/0, ::0/0, 192.168.1.0/24"
|
||||
}
|
||||
|
||||
|
||||
73
apps/fz_http/test/fz_http/settings_test.exs
Normal file
73
apps/fz_http/test/fz_http/settings_test.exs
Normal file
@@ -0,0 +1,73 @@
|
||||
defmodule FzHttp.SettingsTest do
|
||||
use FzHttp.DataCase
|
||||
|
||||
alias FzHttp.Settings
|
||||
|
||||
@setting_keys ~w(
|
||||
default.device.dns_servers
|
||||
default.device.allowed_ips
|
||||
default.device.endpoint
|
||||
)
|
||||
|
||||
describe "settings" do
|
||||
alias FzHttp.Settings.Setting
|
||||
|
||||
import FzHttp.SettingsFixtures
|
||||
|
||||
@valid_settings %{
|
||||
"default.device.dns_servers" => "8.8.8.8",
|
||||
"default.device.allowed_ips" => "::/0",
|
||||
"default.device.endpoint" => "172.10.10.10"
|
||||
}
|
||||
@invalid_settings %{
|
||||
"default.device.dns_servers" => "foobar",
|
||||
"default.device.allowed_ips" => nil,
|
||||
"default.device.endpoint" => "foobar"
|
||||
}
|
||||
|
||||
test "get_setting!/1 returns the setting with given id" do
|
||||
setting = setting_fixture()
|
||||
assert Settings.get_setting!(setting.id) == setting
|
||||
end
|
||||
|
||||
test "get_setting!/1 returns the setting with the given key" do
|
||||
for key <- @setting_keys do
|
||||
setting = Settings.get_setting!(key: key)
|
||||
assert setting.key == key
|
||||
end
|
||||
end
|
||||
|
||||
test "update_setting/2 with valid data updates the setting via provided setting" do
|
||||
for key <- @setting_keys do
|
||||
value = @valid_settings[key]
|
||||
setting = Settings.get_setting!(key: key)
|
||||
assert {:ok, %Setting{} = setting} = Settings.update_setting(setting, %{value: value})
|
||||
assert setting.key == key
|
||||
assert setting.value == value
|
||||
end
|
||||
end
|
||||
|
||||
test "update_setting/2 with valid data updates the setting via key, value" do
|
||||
for key <- @setting_keys do
|
||||
value = @valid_settings[key]
|
||||
assert {:ok, %Setting{} = setting} = Settings.update_setting(key, value)
|
||||
assert setting.key == key
|
||||
assert setting.value == value
|
||||
end
|
||||
end
|
||||
|
||||
test "update_setting/2 with invalid data returns error changeset" do
|
||||
for key <- @setting_keys do
|
||||
value = @invalid_settings[key]
|
||||
assert {:error, %Ecto.Changeset{}} = Settings.update_setting(key, value)
|
||||
setting = Settings.get_setting!(key: key)
|
||||
refute setting.value == value
|
||||
end
|
||||
end
|
||||
|
||||
test "change_setting/1 returns a setting changeset" do
|
||||
setting = setting_fixture()
|
||||
assert %Ecto.Changeset{} = Settings.change_setting(setting)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -45,6 +45,26 @@ defmodule FzHttpWeb.AccountLive.ShowTest do
|
||||
assert flash["info"] == "Account updated successfully."
|
||||
end
|
||||
|
||||
test "doesn't allow empty email", %{authed_conn: conn} do
|
||||
path = Routes.account_show_path(conn, :edit)
|
||||
{:ok, view, _html} = live(conn, path)
|
||||
|
||||
test_view =
|
||||
view
|
||||
|> element("#account-edit")
|
||||
|> render_submit(%{
|
||||
"user" => %{
|
||||
"email" => "",
|
||||
"current_password" => "",
|
||||
"password" => "",
|
||||
"password_confirmation" => ""
|
||||
}
|
||||
})
|
||||
|
||||
refute_redirected(view, Routes.account_show_path(conn, :show))
|
||||
assert test_view =~ "can't be blank"
|
||||
end
|
||||
|
||||
test "renders validation errors", %{authed_conn: conn} do
|
||||
path = Routes.account_show_path(conn, :edit)
|
||||
{:ok, view, _html} = live(conn, path)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
defmodule FzHttpWeb.ConnectivityCheckLive.IndexTest do
|
||||
use FzHttpWeb.ConnCase, async: true
|
||||
|
||||
describe "authenticated/connectivity_checks list" do
|
||||
setup :create_connectivity_checks
|
||||
|
||||
test "show connectivity checks", %{
|
||||
authed_conn: conn,
|
||||
connectivity_checks: connectivity_checks
|
||||
} do
|
||||
path = Routes.connectivity_check_index_path(conn, :index)
|
||||
{:ok, _view, html} = live(conn, path)
|
||||
|
||||
for connectivity_check <- connectivity_checks do
|
||||
assert html =~ DateTime.to_iso8601(connectivity_check.inserted_at)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "unauthenticated/connectivity_checks list" do
|
||||
test "mount redirects to session path", %{unauthed_conn: conn} do
|
||||
path = Routes.connectivity_check_index_path(conn, :index)
|
||||
expected_path = Routes.session_path(conn, :new)
|
||||
assert {:error, {:redirect, %{to: ^expected_path}}} = live(conn, path)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -6,8 +6,36 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do
|
||||
|
||||
@valid_params %{"device" => %{"name" => "new_name"}}
|
||||
@invalid_params %{"device" => %{"name" => ""}}
|
||||
@allowed_ips "2.2.2.2"
|
||||
@allowed_ips_change %{
|
||||
"device" => %{"use_default_allowed_ips" => "false", "allowed_ips" => @allowed_ips}
|
||||
}
|
||||
@allowed_ips_unchanged %{
|
||||
"device" => %{"use_default_allowed_ips" => "true", "allowed_ips" => @allowed_ips}
|
||||
}
|
||||
@dns_servers "8.8.8.8, 8.8.4.4"
|
||||
@dns_servers_change %{"device" => %{"dns_servers" => @dns_servers}}
|
||||
@dns_servers_change %{
|
||||
"device" => %{"use_default_dns_servers" => "false", "dns_servers" => @dns_servers}
|
||||
}
|
||||
@dns_servers_unchanged %{
|
||||
"device" => %{"use_default_dns_servers" => "true", "dns_servers" => @dns_servers}
|
||||
}
|
||||
@wireguard_endpoint "6.6.6.6"
|
||||
@endpoint_change %{
|
||||
"device" => %{"use_default_endpoint" => "false", "endpoint" => @wireguard_endpoint}
|
||||
}
|
||||
@endpoint_unchanged %{
|
||||
"device" => %{"use_default_endpoint" => "true", "dns_servers" => @wireguard_endpoint}
|
||||
}
|
||||
@default_allowed_ips_change %{
|
||||
"device" => %{"use_default_allowed_ips" => "false"}
|
||||
}
|
||||
@default_dns_servers_change %{
|
||||
"device" => %{"use_default_dns_servers" => "false"}
|
||||
}
|
||||
@default_endpoint_change %{
|
||||
"device" => %{"use_default_endpoint" => "false"}
|
||||
}
|
||||
|
||||
test "shows device details", %{authed_conn: conn, device: device} do
|
||||
path = Routes.device_show_path(conn, :show, device)
|
||||
@@ -39,6 +67,66 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do
|
||||
assert flash["info"] == "Device updated successfully."
|
||||
end
|
||||
|
||||
test "prevents allowed_ips changes when use_default_allowed_ips 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(@allowed_ips_unchanged)
|
||||
|
||||
assert test_view =~ "must not be present"
|
||||
end
|
||||
|
||||
test "prevents dns_servers changes when use_default_dns_servers 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(@dns_servers_unchanged)
|
||||
|
||||
assert test_view =~ "must not be present"
|
||||
end
|
||||
|
||||
test "prevents endpoint changes when use_default_endpoint 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(@endpoint_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)
|
||||
|
||||
view
|
||||
|> form("#edit-device")
|
||||
|> render_submit(@allowed_ips_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 =~ "AllowedIPs = #{@allowed_ips}"
|
||||
end
|
||||
|
||||
test "allows dns_servers changes", %{authed_conn: conn, device: device} do
|
||||
path = Routes.device_show_path(conn, :edit, device)
|
||||
{:ok, view, _html} = live(conn, path)
|
||||
@@ -54,6 +142,21 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do
|
||||
assert html =~ "DNS = #{@dns_servers}"
|
||||
end
|
||||
|
||||
test "allows endpoint 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(@endpoint_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 =~ "Endpoint = #{@wireguard_endpoint}:51820"
|
||||
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)
|
||||
@@ -65,6 +168,48 @@ defmodule FzHttpWeb.DeviceLive.ShowTest do
|
||||
|
||||
assert test_view =~ "can't be blank"
|
||||
end
|
||||
|
||||
test "on use_default_allowed_ips 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_allowed_ips_change)
|
||||
|
||||
assert test_view =~ """
|
||||
<input class="input" id="edit-device_allowed_ips" name="device[allowed_ips]" type="text"/>\
|
||||
"""
|
||||
end
|
||||
|
||||
test "on use_default_dns_servers 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_dns_servers_change)
|
||||
|
||||
assert test_view =~ """
|
||||
<input class="input" id="edit-device_dns_servers" name="device[dns_servers]" type="text"/>\
|
||||
"""
|
||||
end
|
||||
|
||||
test "on use_default_endpoint 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_endpoint_change)
|
||||
|
||||
assert test_view =~ """
|
||||
<input class="input" id="edit-device_endpoint" name="device[endpoint]" type="text"/>\
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete own device" do
|
||||
|
||||
169
apps/fz_http/test/fz_http_web/live/setting_live/default_test.exs
Normal file
169
apps/fz_http/test/fz_http_web/live/setting_live/default_test.exs
Normal file
@@ -0,0 +1,169 @@
|
||||
defmodule FzHttpWeb.SettingLive.DefaultTest do
|
||||
use FzHttpWeb.ConnCase, async: true
|
||||
|
||||
alias FzHttp.Settings
|
||||
|
||||
describe "authenticated/settings default" do
|
||||
@valid_allowed_ips %{
|
||||
"setting" => %{"value" => "1.1.1.1"}
|
||||
}
|
||||
@valid_dns_servers %{
|
||||
"setting" => %{"value" => "1.1.1.1"}
|
||||
}
|
||||
@valid_endpoint %{
|
||||
"setting" => %{"value" => "1.1.1.1"}
|
||||
}
|
||||
|
||||
@invalid_allowed_ips %{
|
||||
"setting" => %{"value" => "foobar"}
|
||||
}
|
||||
@invalid_dns_servers %{
|
||||
"setting" => %{"value" => "foobar"}
|
||||
}
|
||||
@invalid_endpoint %{
|
||||
"setting" => %{"value" => "foobar"}
|
||||
}
|
||||
|
||||
setup %{authed_conn: conn} do
|
||||
path = Routes.setting_default_path(conn, :default)
|
||||
{:ok, view, html} = live(conn, path)
|
||||
|
||||
%{html: html, view: view}
|
||||
end
|
||||
|
||||
test "renders current settings", %{html: html} do
|
||||
assert html =~ Settings.default_device_allowed_ips()
|
||||
assert html =~ Settings.default_device_dns_servers()
|
||||
|
||||
assert html =~ """
|
||||
id="endpoint_form_component"\
|
||||
"""
|
||||
end
|
||||
|
||||
test "hides Save button by default", %{html: html} do
|
||||
refute html =~ """
|
||||
<button class="button is-primary" type="submit">Save</button>\
|
||||
"""
|
||||
end
|
||||
|
||||
test "shows Save button after allowed_ips form is changed", %{view: view} do
|
||||
test_view =
|
||||
view
|
||||
|> element("#allowed_ips_form_component")
|
||||
|> render_change(@valid_allowed_ips)
|
||||
|
||||
assert test_view =~ """
|
||||
<button class="button is-primary" type="submit">Save</button>\
|
||||
"""
|
||||
end
|
||||
|
||||
test "shows Save button after dns_servers form is changed", %{view: view} do
|
||||
test_view =
|
||||
view
|
||||
|> element("#dns_servers_form_component")
|
||||
|> render_change(@valid_dns_servers)
|
||||
|
||||
assert test_view =~ """
|
||||
<button class="button is-primary" type="submit">Save</button>\
|
||||
"""
|
||||
end
|
||||
|
||||
test "shows Save button after endpoint form is changed", %{view: view} do
|
||||
test_view =
|
||||
view
|
||||
|> element("#endpoint_form_component")
|
||||
|> render_change(@valid_endpoint)
|
||||
|
||||
assert test_view =~ """
|
||||
<button class="button is-primary" type="submit">Save</button>\
|
||||
"""
|
||||
end
|
||||
|
||||
test "updates default allowed_ips", %{view: view} do
|
||||
test_view =
|
||||
view
|
||||
|> element("#allowed_ips_form_component")
|
||||
|> render_submit(@valid_allowed_ips)
|
||||
|
||||
refute test_view =~ "is invalid"
|
||||
|
||||
assert test_view =~ """
|
||||
<input class="input is-success" id="allowed_ips_form_component_value" name="setting[value]" type="text" value="1.1.1.1"/>\
|
||||
"""
|
||||
end
|
||||
|
||||
test "updates default dns_servers", %{view: view} do
|
||||
test_view =
|
||||
view
|
||||
|> element("#dns_servers_form_component")
|
||||
|> render_submit(@valid_dns_servers)
|
||||
|
||||
refute test_view =~ "is invalid"
|
||||
|
||||
assert test_view =~ """
|
||||
<input class="input is-success" id="dns_servers_form_component_value" name="setting[value]" type="text" value="1.1.1.1"/>\
|
||||
"""
|
||||
end
|
||||
|
||||
test "updates default endpoint", %{view: view} do
|
||||
test_view =
|
||||
view
|
||||
|> element("#endpoint_form_component")
|
||||
|> render_submit(@valid_endpoint)
|
||||
|
||||
refute test_view =~ "is invalid"
|
||||
|
||||
assert test_view =~ """
|
||||
<input class="input is-success" id="endpoint_form_component_value" name="setting[value]" type="text" value="1.1.1.1"/>\
|
||||
"""
|
||||
end
|
||||
|
||||
test "prevents invalid allowed_ips", %{view: view} do
|
||||
test_view =
|
||||
view
|
||||
|> element("#allowed_ips_form_component")
|
||||
|> render_submit(@invalid_allowed_ips)
|
||||
|
||||
assert test_view =~ "is invalid"
|
||||
|
||||
refute test_view =~ """
|
||||
<input id="allowed_ips_form_component" class="input is-success"\
|
||||
"""
|
||||
end
|
||||
|
||||
test "prevents invalid dns_servers", %{view: view} do
|
||||
test_view =
|
||||
view
|
||||
|> element("#dns_servers_form_component")
|
||||
|> render_submit(@invalid_dns_servers)
|
||||
|
||||
assert test_view =~ "is invalid"
|
||||
|
||||
refute test_view =~ """
|
||||
<input id="dns_servers_form_component" class="input is-success"\
|
||||
"""
|
||||
end
|
||||
|
||||
test "prevents invalid endpoint", %{view: view} do
|
||||
test_view =
|
||||
view
|
||||
|> element("#endpoint_form_component")
|
||||
|> render_submit(@invalid_endpoint)
|
||||
|
||||
assert test_view =~ "is invalid"
|
||||
|
||||
refute test_view =~ """
|
||||
<input id="endpoint_form_component" class="input is-success"\
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
describe "unauthenticated/settings default" do
|
||||
@tag :unauthed
|
||||
test "mount redirects to session path", %{unauthed_conn: conn} do
|
||||
path = Routes.setting_default_path(conn, :default)
|
||||
expected_path = Routes.session_path(conn, :new)
|
||||
assert {:error, {:redirect, %{to: ^expected_path}}} = live(conn, path)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -19,7 +19,7 @@ defmodule FzHttpWeb.ConnCase do
|
||||
|
||||
alias Ecto.Adapters.SQL.Sandbox
|
||||
|
||||
alias FzHttp.Fixtures
|
||||
alias FzHttp.SessionsFixtures
|
||||
|
||||
using do
|
||||
quote do
|
||||
@@ -40,7 +40,7 @@ defmodule FzHttpWeb.ConnCase do
|
||||
end
|
||||
|
||||
def authed_conn do
|
||||
session = Fixtures.session()
|
||||
session = SessionsFixtures.session()
|
||||
|
||||
{session.id,
|
||||
new_conn()
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
defmodule FzHttp.Fixtures do
|
||||
@moduledoc """
|
||||
Convenience helpers for inserting records
|
||||
"""
|
||||
alias FzHttp.{Devices, Repo, Rules, Sessions, Users, Users.User}
|
||||
|
||||
# return user specified by email, or generate a new otherwise
|
||||
def user(attrs \\ %{}) do
|
||||
email = Map.get(attrs, :email, "test-#{counter()}@test")
|
||||
|
||||
case Repo.get_by(User, email: email) do
|
||||
nil ->
|
||||
{:ok, user} =
|
||||
%{email: email, password: "testtest", password_confirmation: "testtest"}
|
||||
|> Map.merge(attrs)
|
||||
|> Users.create_user()
|
||||
|
||||
user
|
||||
|
||||
%User{} = user ->
|
||||
user
|
||||
end
|
||||
end
|
||||
|
||||
def device(attrs \\ %{}) do
|
||||
# don't create a user if user_id is passed
|
||||
user_id = Map.get_lazy(attrs, :user_id, fn -> user().id end)
|
||||
|
||||
default_attrs = %{
|
||||
user_id: user_id,
|
||||
public_key: "test-pubkey",
|
||||
name: "factory",
|
||||
private_key: "test-privkey",
|
||||
server_public_key: "test-server-pubkey"
|
||||
}
|
||||
|
||||
{:ok, device} = Devices.create_device(Map.merge(default_attrs, attrs))
|
||||
device
|
||||
end
|
||||
|
||||
def rule4(attrs \\ %{}) do
|
||||
rule(attrs)
|
||||
end
|
||||
|
||||
def rule6(attrs \\ %{}) do
|
||||
rule(Map.merge(attrs, %{destination: "::/0"}))
|
||||
end
|
||||
|
||||
def rule(attrs \\ %{}) do
|
||||
default_attrs = %{
|
||||
destination: "10.10.10.0/24"
|
||||
}
|
||||
|
||||
{:ok, rule} = Rules.create_rule(Map.merge(default_attrs, attrs))
|
||||
rule
|
||||
end
|
||||
|
||||
def session(_attrs \\ %{}) do
|
||||
email = user().email
|
||||
record = Sessions.get_session!(email: email)
|
||||
create_params = %{email: email, password: "testtest"}
|
||||
{:ok, session} = Sessions.create_session(record, create_params)
|
||||
session
|
||||
end
|
||||
|
||||
defp counter do
|
||||
System.unique_integer([:positive])
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
defmodule FzHttp.ConnectivityChecksFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `FzHttp.ConnectivityChecks` context.
|
||||
"""
|
||||
|
||||
alias FzHttp.ConnectivityChecks
|
||||
|
||||
@doc """
|
||||
Generate a connectivity_check.
|
||||
"""
|
||||
def connectivity_check_fixture(attrs \\ %{}) do
|
||||
{:ok, connectivity_check} =
|
||||
attrs
|
||||
|> Enum.into(%{
|
||||
response_body: "some response_body",
|
||||
response_code: 142,
|
||||
response_headers: %{"Content-Type" => "text/plain"},
|
||||
url: "https://ping.firez.one/0.0.0+git.0.deadbeef0"
|
||||
})
|
||||
|> ConnectivityChecks.create_connectivity_check()
|
||||
|
||||
connectivity_check
|
||||
end
|
||||
end
|
||||
27
apps/fz_http/test/support/fixtures/devices_fixtures.ex
Normal file
27
apps/fz_http/test/support/fixtures/devices_fixtures.ex
Normal file
@@ -0,0 +1,27 @@
|
||||
defmodule FzHttp.DevicesFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `FzHttp.Devices` context.
|
||||
"""
|
||||
|
||||
alias FzHttp.{Devices, UsersFixtures}
|
||||
|
||||
@doc """
|
||||
Generate a device.
|
||||
"""
|
||||
def device(attrs \\ %{}) do
|
||||
# Don't create a user if user_id is passed
|
||||
user_id = Map.get_lazy(attrs, :user_id, fn -> UsersFixtures.user().id end)
|
||||
|
||||
default_attrs = %{
|
||||
user_id: user_id,
|
||||
public_key: "test-pubkey",
|
||||
name: "factory",
|
||||
private_key: "test-privkey",
|
||||
server_public_key: "test-server-pubkey"
|
||||
}
|
||||
|
||||
{:ok, device} = Devices.create_device(Map.merge(default_attrs, attrs))
|
||||
device
|
||||
end
|
||||
end
|
||||
25
apps/fz_http/test/support/fixtures/rules_fixtures.ex
Normal file
25
apps/fz_http/test/support/fixtures/rules_fixtures.ex
Normal file
@@ -0,0 +1,25 @@
|
||||
defmodule FzHttp.RulesFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `FzHttp.Rules` context.
|
||||
"""
|
||||
|
||||
alias FzHttp.Rules
|
||||
|
||||
def rule4(attrs \\ %{}) do
|
||||
rule(attrs)
|
||||
end
|
||||
|
||||
def rule6(attrs \\ %{}) do
|
||||
rule(Map.merge(attrs, %{destination: "::/0"}))
|
||||
end
|
||||
|
||||
def rule(attrs \\ %{}) do
|
||||
default_attrs = %{
|
||||
destination: "10.10.10.0/24"
|
||||
}
|
||||
|
||||
{:ok, rule} = Rules.create_rule(Map.merge(default_attrs, attrs))
|
||||
rule
|
||||
end
|
||||
end
|
||||
15
apps/fz_http/test/support/fixtures/sessions_fixtures.ex
Normal file
15
apps/fz_http/test/support/fixtures/sessions_fixtures.ex
Normal file
@@ -0,0 +1,15 @@
|
||||
defmodule FzHttp.SessionsFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `FzHttp.Sessions` context.
|
||||
"""
|
||||
alias FzHttp.{Sessions, UsersFixtures}
|
||||
|
||||
def session(_attrs \\ %{}) do
|
||||
email = UsersFixtures.user().email
|
||||
record = Sessions.get_session!(email: email)
|
||||
create_params = %{email: email, password: "testtest"}
|
||||
{:ok, session} = Sessions.create_session(record, create_params)
|
||||
session
|
||||
end
|
||||
end
|
||||
15
apps/fz_http/test/support/fixtures/settings_fixtures.ex
Normal file
15
apps/fz_http/test/support/fixtures/settings_fixtures.ex
Normal file
@@ -0,0 +1,15 @@
|
||||
defmodule FzHttp.SettingsFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `FzHttp.Settings` context.
|
||||
"""
|
||||
|
||||
alias FzHttp.Settings
|
||||
|
||||
@doc """
|
||||
Generate a setting.
|
||||
"""
|
||||
def setting_fixture(key \\ "default.device.dns_servers") do
|
||||
Settings.get_setting!(key: key)
|
||||
end
|
||||
end
|
||||
32
apps/fz_http/test/support/fixtures/users_fixtures.ex
Normal file
32
apps/fz_http/test/support/fixtures/users_fixtures.ex
Normal file
@@ -0,0 +1,32 @@
|
||||
defmodule FzHttp.UsersFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `FzHttp.Users` context.
|
||||
"""
|
||||
|
||||
alias FzHttp.{Repo, Users, Users.User}
|
||||
|
||||
@doc """
|
||||
Generate a user specified by email, or generate a new otherwise.
|
||||
"""
|
||||
def user(attrs \\ %{}) do
|
||||
email = Map.get(attrs, :email, "test-#{counter()}@test")
|
||||
|
||||
case Repo.get_by(User, email: email) do
|
||||
nil ->
|
||||
{:ok, user} =
|
||||
%{email: email, password: "testtest", password_confirmation: "testtest"}
|
||||
|> Map.merge(attrs)
|
||||
|> Users.create_user()
|
||||
|
||||
user
|
||||
|
||||
%User{} = user ->
|
||||
user
|
||||
end
|
||||
end
|
||||
|
||||
defp counter do
|
||||
System.unique_integer([:positive])
|
||||
end
|
||||
end
|
||||
@@ -1,21 +0,0 @@
|
||||
defmodule FzHttp.MockHelpers do
|
||||
@moduledoc """
|
||||
Helpers for test life cycle
|
||||
"""
|
||||
|
||||
import ExUnit.Callbacks
|
||||
|
||||
def mock_enable_signup do
|
||||
mock_disable_signup(false)
|
||||
end
|
||||
|
||||
def mock_disable_signup do
|
||||
mock_disable_signup(true)
|
||||
end
|
||||
|
||||
def mock_disable_signup(val) when val in [true, false] do
|
||||
old_val = Application.get_env(:fz_http, :disable_signup)
|
||||
Application.put_env(:fz_http, :disable_signup, val)
|
||||
on_exit(fn -> Application.put_env(:fz_http, :disable_signup, old_val) end)
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,17 @@ defmodule FzHttp.TestHelpers do
|
||||
@moduledoc """
|
||||
Test setup helpers
|
||||
"""
|
||||
alias FzHttp.{Fixtures, Repo, Users, Users.User}
|
||||
|
||||
alias FzHttp.{
|
||||
ConnectivityChecksFixtures,
|
||||
DevicesFixtures,
|
||||
Repo,
|
||||
RulesFixtures,
|
||||
SessionsFixtures,
|
||||
Users,
|
||||
Users.User,
|
||||
UsersFixtures
|
||||
}
|
||||
|
||||
def clear_users do
|
||||
Repo.delete_all(User)
|
||||
@@ -11,19 +21,19 @@ defmodule FzHttp.TestHelpers do
|
||||
def create_device(tags) do
|
||||
device =
|
||||
if tags[:unauthed] || is_nil(tags[:user_id]) do
|
||||
Fixtures.device()
|
||||
DevicesFixtures.device()
|
||||
else
|
||||
Fixtures.device(%{user_id: tags[:user_id]})
|
||||
DevicesFixtures.device(%{user_id: tags[:user_id]})
|
||||
end
|
||||
|
||||
{:ok, device: device}
|
||||
end
|
||||
|
||||
def create_other_user_device(_) do
|
||||
user_id = Fixtures.user(%{email: "other_user@test"}).id
|
||||
user_id = UsersFixtures.user(%{email: "other_user@test"}).id
|
||||
|
||||
device =
|
||||
Fixtures.device(%{
|
||||
DevicesFixtures.device(%{
|
||||
user_id: user_id,
|
||||
name: "other device",
|
||||
public_key: "other-pubkey",
|
||||
@@ -33,17 +43,26 @@ defmodule FzHttp.TestHelpers do
|
||||
{:ok, other_device: device}
|
||||
end
|
||||
|
||||
def create_connectivity_checks(_tags) do
|
||||
connectivity_checks =
|
||||
Enum.map(1..5, fn _i ->
|
||||
ConnectivityChecksFixtures.connectivity_check_fixture()
|
||||
end)
|
||||
|
||||
{:ok, connectivity_checks: connectivity_checks}
|
||||
end
|
||||
|
||||
def create_devices(tags) do
|
||||
user_id =
|
||||
if tags[:unathed] || is_nil(tags[:user_id]) do
|
||||
Fixtures.user().id
|
||||
UsersFixtures.user().id
|
||||
else
|
||||
tags[:user_id]
|
||||
end
|
||||
|
||||
devices =
|
||||
Enum.map(1..5, fn num ->
|
||||
Fixtures.device(%{
|
||||
DevicesFixtures.device(%{
|
||||
name: "device #{num}",
|
||||
public_key: "#{num}",
|
||||
private_key: "#{num}",
|
||||
@@ -55,37 +74,37 @@ defmodule FzHttp.TestHelpers do
|
||||
end
|
||||
|
||||
def create_user(_) do
|
||||
user = Fixtures.user()
|
||||
user = UsersFixtures.user()
|
||||
{:ok, user: user}
|
||||
end
|
||||
|
||||
def create_session(_) do
|
||||
session = Fixtures.session()
|
||||
session = SessionsFixtures.session()
|
||||
{:ok, session: session}
|
||||
end
|
||||
|
||||
def create_accept_rule(_) do
|
||||
rule = Fixtures.rule(%{action: :accept})
|
||||
rule = RulesFixtures.rule(%{action: :accept})
|
||||
{:ok, rule: rule}
|
||||
end
|
||||
|
||||
def create_drop_rule(_) do
|
||||
rule = Fixtures.rule(%{action: :drop})
|
||||
rule = RulesFixtures.rule(%{action: :drop})
|
||||
{:ok, rule: rule}
|
||||
end
|
||||
|
||||
def create_rule(_) do
|
||||
rule = Fixtures.rule(%{})
|
||||
rule = RulesFixtures.rule(%{})
|
||||
{:ok, rule: rule}
|
||||
end
|
||||
|
||||
def create_rule6(_) do
|
||||
rule = Fixtures.rule6(%{})
|
||||
rule = RulesFixtures.rule6(%{})
|
||||
{:ok, rule6: rule}
|
||||
end
|
||||
|
||||
def create_rule4(_) do
|
||||
rule = Fixtures.rule4(%{})
|
||||
rule = RulesFixtures.rule4(%{})
|
||||
{:ok, rule4: rule}
|
||||
end
|
||||
|
||||
@@ -97,26 +116,26 @@ defmodule FzHttp.TestHelpers do
|
||||
1..5
|
||||
|> Enum.map(fn num ->
|
||||
destination = "#{num}.#{num}.#{num}.0/24"
|
||||
Fixtures.rule(%{destination: destination})
|
||||
RulesFixtures.rule(%{destination: destination})
|
||||
end)
|
||||
|
||||
{:ok, rules: rules}
|
||||
end
|
||||
|
||||
def create_user_with_valid_sign_in_token(_) do
|
||||
{:ok, user: %User{} = Fixtures.user(Users.sign_in_keys())}
|
||||
{:ok, user: %User{} = UsersFixtures.user(Users.sign_in_keys())}
|
||||
end
|
||||
|
||||
def create_user_with_expired_sign_in_token(_) do
|
||||
expired = DateTime.add(DateTime.utc_now(), -1 * 86_401)
|
||||
params = %{Users.sign_in_keys() | sign_in_token_created_at: expired}
|
||||
{:ok, user: %User{} = Fixtures.user(params)}
|
||||
{:ok, user: %User{} = UsersFixtures.user(params)}
|
||||
end
|
||||
|
||||
def create_users(%{count: count}) do
|
||||
users =
|
||||
Enum.map(1..count, fn i ->
|
||||
Fixtures.user(%{email: "userlist#{i}@test"})
|
||||
UsersFixtures.user(%{email: "userlist#{i}@test"})
|
||||
end)
|
||||
|
||||
{:ok, users: users}
|
||||
|
||||
@@ -36,13 +36,17 @@ github_sha =
|
||||
end
|
||||
|
||||
config :fz_http,
|
||||
supervision_tree_mode: :full,
|
||||
http_client: HTTPoison,
|
||||
connectivity_checks_enabled: true,
|
||||
connectivity_checks_interval: 3_600,
|
||||
connectivity_checks_url: "https://ping-dev.firez.one/",
|
||||
github_sha: github_sha,
|
||||
cookie_signing_salt: "Z9eq8iof",
|
||||
ecto_repos: [FzHttp.Repo],
|
||||
admin_email: "firezone@localhost",
|
||||
default_admin_password: "firezone",
|
||||
events_module: FzHttpWeb.Events,
|
||||
disable_signup: true,
|
||||
server_process_opts: [name: {:global, :fz_http_server}]
|
||||
|
||||
config :fz_wall,
|
||||
|
||||
@@ -115,5 +115,3 @@ config :phoenix, :stacktrace_depth, 20
|
||||
|
||||
# Initialize plugs at runtime for faster development compilation
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
||||
config(:fz_http, :disable_signup, true)
|
||||
|
||||
@@ -27,3 +27,6 @@ config :fz_http, FzHttpWeb.Endpoint,
|
||||
|
||||
# Do not print debug messages in production
|
||||
config :logger, level: :info
|
||||
|
||||
config :fz_http,
|
||||
connectivity_check_url: "https://ping.firez.one/"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# although such is generally not recommended and you have to
|
||||
# remember to add this file to your .gitignore.
|
||||
import Config
|
||||
alias FzCommon.CLI
|
||||
alias FzCommon.{CLI, FzInteger, FzString}
|
||||
|
||||
# For releases, require that all these are set
|
||||
database_name = System.fetch_env!("DATABASE_NAME")
|
||||
@@ -16,13 +16,20 @@ url_host = System.fetch_env!("URL_HOST")
|
||||
admin_email = System.fetch_env!("ADMIN_EMAIL")
|
||||
default_admin_password = System.fetch_env!("DEFAULT_ADMIN_PASSWORD")
|
||||
wireguard_interface_name = System.fetch_env!("WIREGUARD_INTERFACE_NAME")
|
||||
wireguard_endpoint = System.fetch_env!("WIREGUARD_ENDPOINT")
|
||||
wireguard_port = String.to_integer(System.fetch_env!("WIREGUARD_PORT"))
|
||||
nft_path = System.fetch_env!("NFT_PATH")
|
||||
wg_path = System.fetch_env!("WG_PATH")
|
||||
egress_interface = System.fetch_env!("EGRESS_INTERFACE")
|
||||
wireguard_public_key = System.fetch_env!("WIREGUARD_PUBLIC_KEY")
|
||||
|
||||
connectivity_checks_enabled =
|
||||
FzString.to_boolean(System.fetch_env!("CONNECTIVITY_CHECKS_ENABLED"))
|
||||
|
||||
connectivity_checks_interval =
|
||||
System.fetch_env!("CONNECTIVITY_CHECKS_INTERVAL")
|
||||
|> String.to_integer()
|
||||
|> FzInteger.clamp(60, 86_400)
|
||||
|
||||
# secrets
|
||||
encryption_key = System.fetch_env!("DATABASE_ENCRYPTION_KEY")
|
||||
secret_key_base = System.fetch_env!("SECRET_KEY_BASE")
|
||||
@@ -32,9 +39,6 @@ cookie_signing_salt = System.fetch_env!("COOKIE_SIGNING_SALT")
|
||||
# Password is not needed if using bundled PostgreSQL, so use nil if it's not set.
|
||||
database_password = System.get_env("DATABASE_PASSWORD")
|
||||
|
||||
config :fz_http,
|
||||
disable_signup: true
|
||||
|
||||
# Database configuration
|
||||
connect_opts = [
|
||||
database: database_name,
|
||||
@@ -83,9 +87,10 @@ config :fz_wall,
|
||||
config :fz_vpn,
|
||||
wireguard_public_key: wireguard_public_key,
|
||||
wireguard_interface_name: wireguard_interface_name,
|
||||
wireguard_port: wireguard_port,
|
||||
wireguard_endpoint: wireguard_endpoint
|
||||
wireguard_port: wireguard_port
|
||||
|
||||
config :fz_http,
|
||||
connectivity_checks_enabled: connectivity_checks_enabled,
|
||||
connectivity_checks_interval: connectivity_checks_interval,
|
||||
admin_email: admin_email,
|
||||
default_admin_password: default_admin_password
|
||||
|
||||
@@ -37,8 +37,12 @@ config :fz_http, FzHttpWeb.Endpoint,
|
||||
],
|
||||
server: true
|
||||
|
||||
config :fz_http, :sql_sandbox, true
|
||||
config :fz_http, :events_module, FzHttpWeb.MockEvents
|
||||
config :fz_http,
|
||||
supervision_tree_mode: :test,
|
||||
connectivity_checks_interval: 86_400,
|
||||
sql_sandbox: true,
|
||||
events_module: FzHttpWeb.MockEvents,
|
||||
http_client: FzHttp.MockHttpClient
|
||||
|
||||
# Print only warnings and errors during test
|
||||
config :logger, level: :warn
|
||||
|
||||
3
mix.exs
3
mix.exs
@@ -30,6 +30,9 @@ defmodule FirezoneUmbrella.MixProject do
|
||||
extras: ["README.md", "SECURITY.md", "CONTRIBUTING.md"]
|
||||
],
|
||||
deps: deps(),
|
||||
dialyzer: [
|
||||
plt_file: {:no_warn, "priv/plts/dialyzer.plt"}
|
||||
],
|
||||
aliases: aliases(),
|
||||
default_release: :firezone,
|
||||
releases: [
|
||||
|
||||
27
mix.lock
27
mix.lock
@@ -10,37 +10,38 @@
|
||||
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
|
||||
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"},
|
||||
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
|
||||
"credo": {:hex, :credo, "1.5.6", "e04cc0fdc236fefbb578e0c04bd01a471081616e741d386909e527ac146016c6", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4b52a3e558bd64e30de62a648518a5ea2b6e3e5d2b164ef5296244753fc7eb17"},
|
||||
"db_connection": {:hex, :db_connection, "2.4.0", "d04b1b73795dae60cead94189f1b8a51cc9e1f911c234cc23074017c43c031e5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad416c21ad9f61b3103d254a71b63696ecadb6a917b36f563921e0de00d7d7c8"},
|
||||
"credo": {:hex, :credo, "1.6.1", "7dc76dcdb764a4316c1596804c48eada9fff44bd4b733a91ccbf0c0f368be61e", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "698607fb5993720c7e93d2d8e76f2175bba024de964e160e2f7151ef3ab82ac5"},
|
||||
"db_connection": {:hex, :db_connection, "2.4.1", "6411f6e23f1a8b68a82fa3a36366d4881f21f47fc79a9efb8c615e62050219da", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ea36d226ec5999781a9a8ad64e5d8c4454ecedc7a4d643e4832bf08efca01f00"},
|
||||
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
|
||||
"earmark_parser": {:hex, :earmark_parser, "1.4.15", "b29e8e729f4aa4a00436580dcc2c9c5c51890613457c193cc8525c388ccb2f06", [:mix], [], "hexpm", "044523d6438ea19c1b8ec877ec221b008661d3c27e3b848f4c879f500421ca5c"},
|
||||
"earmark_parser": {:hex, :earmark_parser, "1.4.17", "6f3c7e94170377ba45241d394389e800fb15adc5de51d0a3cd52ae766aafd63f", [:mix], [], "hexpm", "f93ac89c9feca61c165b264b5837bf82344d13bebc634cd575cb711e2e342023"},
|
||||
"ecto": {:hex, :ecto, "3.7.1", "a20598862351b29f80f285b21ec5297da1181c0442687f9b8329f0445d228892", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d36e5b39fc479e654cffd4dbe1865d9716e4a9b6311faff799b6f90ab81b8638"},
|
||||
"ecto_network": {:hex, :ecto_network, "1.3.0", "1e77fa37c20e0f6a426d3862732f3317b0fa4c18f123d325f81752a491d7304e", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 0.0.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.14.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "053a5e46ef2837e8ea5ea97c82fa0f5494699209eddd764e663c85f11b2865bd"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.7.0", "2fcaad4ab0c8d76a5afbef078162806adbe709c04160aca58400d5cbbe8eeac6", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a26135dfa1d99bf87a928c464cfa25bba6535a4fe761eefa56077a4febc60f70"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.7.1", "8de624ef50b2a8540252d8c60506379fbbc2707be1606853df371cf53df5d053", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2b42a32e2ce92f64aba5c88617891ab3b0ba34f3f3a503fa20009eae1a401c81"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
|
||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.25.3", "3edf6a0d70a39d2eafde030b8895501b1c93692effcbd21347296c18e47618ce", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "9ebebc2169ec732a38e9e779fd0418c9189b3ca93f4a676c961be6c1527913f5"},
|
||||
"excoveralls": {:hex, :excoveralls, "0.14.2", "f9f5fd0004d7bbeaa28ea9606251bb643c313c3d60710bad1f5809c845b748f0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "ca6fd358621cb4d29311b29d4732c4d47dac70e622850979bc54ed9a3e50f3e1"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.26.0", "1922164bac0b18b02f84d6f69cab1b93bc3e870e2ad18d5dacb50a9e06b542a3", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2775d66e494a9a48355db7867478ffd997864c61c65a47d31c4949459281c78d"},
|
||||
"excoveralls": {:hex, :excoveralls, "0.14.4", "295498f1ae47bdc6dce59af9a585c381e1aefc63298d48172efaaa90c3d251db", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e3ab02f2df4c1c7a519728a6f0a747e71d7d6e846020aae338173619217931c1"},
|
||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||
"floki": {:hex, :floki, "0.31.0", "f05ee8a8e6a3ced4e62beeb2c79a63bc8e12ab98fbaaf6e6a3d9b76b1278e23f", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "b05afa372f5c345a5bf240ac25ea1f0f3d5fcfd7490ac0beeb4a203f9444891e"},
|
||||
"floki": {:hex, :floki, "0.32.0", "f915dc15258bc997d49be1f5ef7d3992f8834d6f5695270acad17b41f5bcc8e2", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "1c5a91cae1fd8931c26a4826b5e2372c284813904c8bacb468b5de39c7ececbd"},
|
||||
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
|
||||
"hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"},
|
||||
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
||||
"httpoison": {:hex, :httpoison, "1.8.0", "6b85dea15820b7804ef607ff78406ab449dd78bed923a49c7160e1886e987a3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "28089eaa98cf90c66265b6b5ad87c59a3729bea2e74e9d08f9b51eb9729b3c3a"},
|
||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
|
||||
"inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"},
|
||||
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
|
||||
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"},
|
||||
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"mime": {:hex, :mime, "2.0.1", "0de4c81303fe07806ebc2494d5321ce8fb4df106e34dd5f9d787b637ebadc256", [:mix], [], "hexpm", "7a86b920d2aedce5fb6280ac8261ac1a739ae6c1a1ad38f5eadf910063008942"},
|
||||
"mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||
"mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "1.2.0", "b44d75e2a6542dcb6acf5d71c32c74ca88960421b6874777f79153bbbbd7dccc", [:mix], [], "hexpm", "52b2871a7515a5ac49b00f214e4165a40724cf99798d8e4a65e4fd64ebd002c1"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
|
||||
"phoenix": {:hex, :phoenix, "1.6.0", "7b85023f7ddef9a5c70909a51cc37c8b868b474d853f90f4280efd26b0e7cce5", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "52ffdd31f2daeb399b2e1eb57d468f99a1ad6eee5d8ea19d2353492f06c9fc96"},
|
||||
"phoenix": {:hex, :phoenix, "1.6.2", "6cbd5c8ed7a797f25a919a37fafbc2fb1634c9cdb12a4448d7a5d0b26926f005", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7bbee475acae0c3abc229b7f189e210ea788e63bd168e585f60c299a4b2f9133"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "3.0.4", "232d41884fe6a9c42d09f48397c175cd6f0d443aaa34c7424da47604201df2e1", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "ce17fd3cf815b2ed874114073e743507704b1f5288bb03c304a77458485efc8b"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
|
||||
@@ -50,7 +51,7 @@
|
||||
"plug": {:hex, :plug, "1.12.1", "645678c800601d8d9f27ad1aebba1fdb9ce5b2623ddb961a074da0b96c35187d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d57e799a777bc20494b784966dc5fbda91eb4a09f571f76545b72a634ce0d30b"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"},
|
||||
"postgrex": {:hex, :postgrex, "0.15.11", "50abbb50f33d22d79af402e549b9a566ba4f0451b4f5fd39b72d9bbd49743d24", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "6f0e5c3ea10f97468f5ff852277cb207f068399eb68b0c06c142ef68a4e82952"},
|
||||
"postgrex": {:hex, :postgrex, "0.15.13", "7794e697481799aee8982688c261901de493eb64451feee6ea58207d7266d54a", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "3ffb76e1a97cfefe5c6a95632a27ffb67f28871c9741fb585f9d1c3cd2af70f1"},
|
||||
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"},
|
||||
|
||||
@@ -16,7 +16,7 @@ require 'etc'
|
||||
#
|
||||
# { "postgresql": { "enable": false } }
|
||||
#
|
||||
# for example, it will set the node['firezone']['postgresql']['enable'] attribute to false.
|
||||
# for example, it will set the node['firezone']['postgresql']['enabled'] attribute to false.
|
||||
|
||||
# ## Top-level attributes
|
||||
#
|
||||
@@ -66,7 +66,7 @@ default['firezone']['sysvinit_id'] = 'SUP'
|
||||
|
||||
# These attributes control Firezone-specific portions of the Nginx
|
||||
# configuration and the virtual host for the Firezone Phoenix app.
|
||||
default['firezone']['nginx']['enable'] = true
|
||||
default['firezone']['nginx']['enabled'] = true
|
||||
default['firezone']['nginx']['force_ssl'] = true
|
||||
default['firezone']['nginx']['non_ssl_port'] = 80
|
||||
default['firezone']['nginx']['ssl_port'] = 443
|
||||
@@ -80,7 +80,7 @@ default['firezone']['nginx']['log_x_forwarded_for'] = false
|
||||
default['firezone']['nginx']['redirect_to_canonical'] = false
|
||||
|
||||
# Controls nginx caching, used to cache some endpoints
|
||||
default['firezone']['nginx']['cache']['enable'] = false
|
||||
default['firezone']['nginx']['cache']['enabled'] = false
|
||||
default['firezone']['nginx']['cache']['directory'] = "#{node['firezone']['var_directory']}/nginx/cache"
|
||||
|
||||
# These attributes control the main nginx.conf, including the events and http
|
||||
@@ -142,7 +142,7 @@ default['firezone']['nginx']['default']['modules'] = []
|
||||
# ### Use the bundled Postgres instance (default, recommended):
|
||||
#
|
||||
|
||||
default['firezone']['postgresql']['enable'] = true
|
||||
default['firezone']['postgresql']['enabled'] = true
|
||||
default['firezone']['postgresql']['username'] = node['firezone']['user']
|
||||
default['firezone']['postgresql']['data_directory'] = "#{node['firezone']['var_directory']}/postgresql/13.3/data"
|
||||
|
||||
@@ -150,7 +150,7 @@ default['firezone']['postgresql']['data_directory'] = "#{node['firezone']['var_d
|
||||
#
|
||||
# Disable the provided Postgres instance and connect to your own:
|
||||
#
|
||||
# default['firezone']['postgresql']['enable'] = false
|
||||
# default['firezone']['postgresql']['enabled'] = false
|
||||
# default['firezone']['database']['user'] = 'my_db_user_name'
|
||||
# default['firezone']['database']['name'] = 'my_db_name''
|
||||
# default['firezone']['database']['host'] = 'my.db.server.address'
|
||||
@@ -189,20 +189,20 @@ default['firezone']['database']['extensions'] = { 'plpgsql' => true, 'pg_trgm' =
|
||||
# default['firezone']['database']['password'] = 'change_me'
|
||||
|
||||
# ## Phoenix
|
||||
#
|
||||
|
||||
# ### The Phoenix web app for Firezone
|
||||
default['firezone']['phoenix']['enable'] = true
|
||||
default['firezone']['phoenix']['enabled'] = true
|
||||
default['firezone']['phoenix']['port'] = 13000
|
||||
default['firezone']['phoenix']['log_directory'] = "#{node['firezone']['log_directory']}/phoenix"
|
||||
default['firezone']['phoenix']['log_rotation']['file_maxbytes'] = 104857600
|
||||
default['firezone']['phoenix']['log_rotation']['num_to_keep'] = 10
|
||||
|
||||
# ## WireGuard
|
||||
#
|
||||
|
||||
# ### Interface Management
|
||||
# Enable management of the WireGuard interface itself. Set this to false if you
|
||||
# want to manually create your WireGuard interface and manage its interface properties.
|
||||
default['firezone']['wireguard']['enable'] = true
|
||||
default['firezone']['wireguard']['enabled'] = true
|
||||
default['firezone']['wireguard']['log_directory'] = "#{node['firezone']['log_directory']}/wireguard"
|
||||
default['firezone']['wireguard']['log_rotation']['file_maxbytes'] = 104857600
|
||||
default['firezone']['wireguard']['log_rotation']['num_to_keep'] = 10
|
||||
@@ -214,13 +214,6 @@ default['firezone']['wireguard']['port'] = 51820
|
||||
# WireGuard interface MTU
|
||||
default['firezone']['wireguard']['mtu'] = 1420
|
||||
|
||||
# ### WireGuard endpoint
|
||||
# IPv4, IPv6, or hostname that device configs will use to connect to this server.
|
||||
# If left blank, this will be set to the IPv4 address of the default egress interface.
|
||||
# Override this to your publicly routable IP if you're behind a NAT and need to
|
||||
# set up port forwarding to your Firezone server.
|
||||
default['firezone']['wireguard']['endpoint'] = nil
|
||||
|
||||
# ## Runit
|
||||
|
||||
# This is missing from the enterprise cookbook
|
||||
@@ -246,9 +239,9 @@ default['firezone']['ssl']['ssl_dhparam'] = nil
|
||||
|
||||
# These are used in creating a self-signed cert if you haven't brought your own.
|
||||
default['firezone']['ssl']['country_name'] = 'US'
|
||||
default['firezone']['ssl']['state_name'] = 'WA'
|
||||
default['firezone']['ssl']['locality_name'] = 'Seattle'
|
||||
default['firezone']['ssl']['company_name'] = 'My Firezone'
|
||||
default['firezone']['ssl']['state_name'] = 'CA'
|
||||
default['firezone']['ssl']['locality_name'] = 'San Francisco'
|
||||
default['firezone']['ssl']['company_name'] = 'My Company'
|
||||
default['firezone']['ssl']['organizational_unit_name'] = 'Operations'
|
||||
default['firezone']['ssl']['email_address'] = 'you@example.com'
|
||||
|
||||
@@ -289,3 +282,18 @@ default['firezone']['smtp_address'] = nil
|
||||
default['firezone']['smtp_password'] = nil
|
||||
default['firezone']['smtp_port'] = nil
|
||||
default['firezone']['smtp_user_name'] = nil
|
||||
|
||||
# ## Diagnostics Settings
|
||||
|
||||
# ### Connectivity Checks
|
||||
#
|
||||
# By default, Firezone periodically checks for WAN connectivity to the Internet
|
||||
# by issuing a POST request with an empty body to https://ping.firez.one. This
|
||||
# is used to determine the server's publicly routable IP address for populating
|
||||
# device configurations and setting up firewall rules. Set this to false to
|
||||
# disable.
|
||||
default['firezone']['connectivity_checks']['enabled'] = true
|
||||
|
||||
# Amount of time to sleep between connectivity checks, in seconds.
|
||||
# Default: 3600 (1 hour). Minimum: 60 (1 minute). Maximum: 86400 (1 day).
|
||||
default['firezone']['connectivity_checks']['interval'] = 3_600
|
||||
|
||||
@@ -221,6 +221,7 @@ class Firezone
|
||||
def self.app_env(attributes, reject = [])
|
||||
attributes = attributes.reject { |k| reject.include?(k) }
|
||||
|
||||
# NOTE: All these variables must be Strings
|
||||
env = {
|
||||
'EGRESS_INTERFACE' => attributes['egress_interface'],
|
||||
'WG_PATH' => "#{attributes['install_directory']}/embedded/bin/wg",
|
||||
@@ -235,9 +236,10 @@ class Firezone
|
||||
'URL_HOST' => attributes['fqdn'],
|
||||
'ADMIN_EMAIL' => attributes['admin_email'],
|
||||
'WIREGUARD_INTERFACE_NAME' => attributes['wireguard']['interface_name'],
|
||||
'WIREGUARD_ENDPOINT' => attributes['wireguard']['endpoint'],
|
||||
'WIREGUARD_PORT' => attributes['wireguard']['port'].to_s,
|
||||
'WIREGUARD_PUBLIC_KEY' => attributes['wireguard_public_key'],
|
||||
'CONNECTIVITY_CHECKS_ENABLED' => attributes['connectivity_checks']['enabled'].to_s,
|
||||
'CONNECTIVITY_CHECKS_INTERVAL' => attributes['connectivity_checks']['interval'].to_s,
|
||||
|
||||
# secrets
|
||||
'SECRET_KEY_BASE' => attributes['secret_key_base'],
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Cookbook:: firezone
|
||||
# Recipe:: config
|
||||
#
|
||||
# Copyright:: 2014 Chef Software, Inc.
|
||||
# Copyright:: 2021 Firezone
|
||||
# Copyright:: 2021 Firezone, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -46,7 +46,7 @@ template 'nginx.conf' do
|
||||
variables(nginx: node['firezone']['nginx'])
|
||||
end
|
||||
|
||||
if node['firezone']['nginx']['enable']
|
||||
if node['firezone']['nginx']['enabled']
|
||||
component_runit_service 'nginx' do
|
||||
package 'firezone'
|
||||
action :enable
|
||||
|
||||
@@ -49,7 +49,7 @@ template 'phoenix.nginx.conf' do
|
||||
app_directory: node['firezone']['app_directory'])
|
||||
end
|
||||
|
||||
if node['firezone']['phoenix']['enable']
|
||||
if node['firezone']['phoenix']['enabled']
|
||||
component_runit_service 'phoenix' do
|
||||
package 'firezone'
|
||||
control ['t']
|
||||
|
||||
@@ -35,7 +35,7 @@ directory node['firezone']['postgresql']['log_directory'] do
|
||||
recursive true
|
||||
end
|
||||
|
||||
if node['firezone']['postgresql']['enable']
|
||||
if node['firezone']['postgresql']['enabled']
|
||||
enterprise_pg_cluster 'firezone' do
|
||||
data_dir node['firezone']['postgresql']['data_directory']
|
||||
encoding 'UTF8'
|
||||
|
||||
@@ -16,7 +16,7 @@ directory node['firezone']['wireguard']['log_directory'] do
|
||||
recursive true
|
||||
end
|
||||
|
||||
if node['firezone']['wireguard']['enable']
|
||||
if node['firezone']['wireguard']['enabled']
|
||||
component_runit_service 'wireguard' do
|
||||
package 'firezone'
|
||||
action :enable
|
||||
|
||||
@@ -6,7 +6,7 @@ upstream phoenix {
|
||||
server 127.0.0.1:<%= @phoenix['port'] %>;
|
||||
}
|
||||
|
||||
<% if @nginx['cache']['enable'] -%>
|
||||
<% if @nginx['cache']['enabled'] -%>
|
||||
proxy_cache_path <%= @nginx['cache']['directory'] %>/firezone levels=1:2 keys_zone=firezone-cache:512m max_size=1000m inactive=600m;
|
||||
proxy_temp_path <%= @nginx['cache']['directory'] %>/tmp;
|
||||
|
||||
@@ -55,7 +55,7 @@ server {
|
||||
}
|
||||
<% end -%>
|
||||
|
||||
<% if @nginx['cache']['enable'] -%>
|
||||
<% if @nginx['cache']['enabled'] -%>
|
||||
access_log <%= @nginx['log_directory'] %>/cache.log cache;
|
||||
<% end -%>
|
||||
|
||||
@@ -76,7 +76,7 @@ server {
|
||||
return 410;
|
||||
}
|
||||
|
||||
<% if @nginx['cache']['enable'] -%>
|
||||
<% if @nginx['cache']['enabled'] -%>
|
||||
# TODO: Is this useful to Firezone?
|
||||
location ~ ^/api/v1/cookbooks/.*/versions/.*(/download)?$ {
|
||||
proxy_set_header HOST $host;
|
||||
|
||||
Reference in New Issue
Block a user