diff --git a/.gitignore b/.gitignore
index e63ec81d4..fe5338137 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
# macOS cruft
.DS_Store
+.devcontainer/pki/authorities/local/
+
# The directory Mix will write compiled artifacts to.
/_build/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f2479c0f8..3ae804dc3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -9,6 +9,7 @@ started.
* [Developer Environment Setup](#developer-environment-setup)
* [Docker Setup](#docker-setup)
* [Docker Caveat](#docker-caveat)
+ * [Local HTTPS](#local-https)
* [asdf-vm](#asdf-vm)
* [Pre-commit](#pre-commit)
* [The .env File](#the-env-file)
@@ -81,6 +82,18 @@ reach their destination through the tunnel just fine. Because of this, it's
recommended to use `172.28.0.0/16` for your `AllowedIPs` parameter when using
host-based WireGuard clients with Firezone running under Docker Desktop.
+Routing packets from _another_ host on the local network, through your development
+machine, and out to the external Internet should work as well.
+
+### Local HTTPS
+
+We use Caddy as a development proxy. The `docker-compose.yml` is set up to link
+Caddy's local root cert into your `.devcontainer/pki/authorities/local/` directory.
+
+Simply add the `root.crt` file to your browser and/or OS certificate store in
+order to have working local HTTPS. This file is generated when Caddy launches for
+the first time and will be different for each developer.
+
### asdf-vm Setup
While not strictly required, we use [asdf-vm](https://asdf-vm.com) to manage
diff --git a/apps/fz_http/lib/fz_http/conf/oidc_config.ex b/apps/fz_http/lib/fz_http/conf/oidc_config.ex
index b4181b91d..8faae738f 100644
--- a/apps/fz_http/lib/fz_http/conf/oidc_config.ex
+++ b/apps/fz_http/lib/fz_http/conf/oidc_config.ex
@@ -5,6 +5,7 @@ defmodule FzHttp.Conf.OIDCConfig do
use Ecto.Schema
import Ecto.Changeset
+ import FzHttp.Validators.OpenIDConnect
@primary_key false
embedded_schema do
@@ -15,7 +16,7 @@ defmodule FzHttp.Conf.OIDCConfig do
field :client_id, :string
field :client_secret, :string
field :discovery_document_uri, :string
- field :auto_create_users, :boolean
+ field :auto_create_users, :boolean, default: true
end
def changeset(data) do
@@ -43,5 +44,6 @@ defmodule FzHttp.Conf.OIDCConfig do
:discovery_document_uri,
:auto_create_users
])
+ |> validate_discovery_document_uri()
end
end
diff --git a/apps/fz_http/lib/fz_http/conf/saml_config.ex b/apps/fz_http/lib/fz_http/conf/saml_config.ex
index ff04c7966..cada17658 100644
--- a/apps/fz_http/lib/fz_http/conf/saml_config.ex
+++ b/apps/fz_http/lib/fz_http/conf/saml_config.ex
@@ -5,18 +5,20 @@ defmodule FzHttp.Conf.SAMLConfig do
use Ecto.Schema
import Ecto.Changeset
+ import FzHttp.Validators.SAML
@primary_key false
embedded_schema do
field :id, :string
field :label, :string
field :metadata, :string
- field :auto_create_users, :boolean
+ field :auto_create_users, :boolean, default: true
end
def changeset(data) do
%__MODULE__{}
|> cast(data, [:id, :label, :metadata, :auto_create_users])
|> validate_required([:id, :label, :metadata, :auto_create_users])
+ |> validate_metadata()
end
end
diff --git a/apps/fz_http/lib/fz_http/devices/device.ex b/apps/fz_http/lib/fz_http/devices/device.ex
index f333eea04..8aa2bc0ae 100644
--- a/apps/fz_http/lib/fz_http/devices/device.ex
+++ b/apps/fz_http/lib/fz_http/devices/device.ex
@@ -7,7 +7,7 @@ defmodule FzHttp.Devices.Device do
import Ecto.Changeset
require Logger
- import FzHttp.SharedValidators,
+ import FzHttp.Validators.Common,
only: [
trim: 2,
validate_fqdn_or_ip: 2,
diff --git a/apps/fz_http/lib/fz_http/mfa/method.ex b/apps/fz_http/lib/fz_http/mfa/method.ex
index c560bc9d3..a88ccc887 100644
--- a/apps/fz_http/lib/fz_http/mfa/method.ex
+++ b/apps/fz_http/lib/fz_http/mfa/method.ex
@@ -5,7 +5,7 @@ defmodule FzHttp.MFA.Method do
use Ecto.Schema
import Ecto.Changeset
- import FzHttp.SharedValidators, only: [trim: 2]
+ import FzHttp.Validators.Common, only: [trim: 2]
@primary_key {:id, :binary_id, autogenerate: true}
@whitespace_trimmed_fields :name
diff --git a/apps/fz_http/lib/fz_http/oidc/start_proxy.ex b/apps/fz_http/lib/fz_http/oidc/start_proxy.ex
index 7569e1cb0..2a6ee43e7 100644
--- a/apps/fz_http/lib/fz_http/oidc/start_proxy.ex
+++ b/apps/fz_http/lib/fz_http/oidc/start_proxy.ex
@@ -23,17 +23,7 @@ defmodule FzHttp.OIDC.StartProxy do
if parsed = auth_oidc_env && parse(auth_oidc_env) do
Conf.Cache.put!(:parsed_openid_connect_providers, parsed)
- # XXX: This is needed because this call can error out, bringing down
- # the whole application if the OIDC config was entered incorrectly.
- # Instead, swallow the Error and print to console.
- #
- # This should be fixed when refactoring OIDC.
- try do
- OpenIDConnect.Worker.start_link(parsed)
- rescue
- e in RuntimeError ->
- Logger.error("ERROR starting OIDC worker: #{e}")
- end
+ OpenIDConnect.Worker.start_link(parsed)
else
:ignore
end
diff --git a/apps/fz_http/lib/fz_http/sites/site.ex b/apps/fz_http/lib/fz_http/sites/site.ex
index 566c442f0..8b5dd5ea3 100644
--- a/apps/fz_http/lib/fz_http/sites/site.ex
+++ b/apps/fz_http/lib/fz_http/sites/site.ex
@@ -6,7 +6,7 @@ defmodule FzHttp.Sites.Site do
use Ecto.Schema
import Ecto.Changeset
- import FzHttp.SharedValidators,
+ import FzHttp.Validators.Common,
only: [
trim: 2,
validate_fqdn_or_ip: 2,
diff --git a/apps/fz_http/lib/fz_http/users/user.ex b/apps/fz_http/lib/fz_http/users/user.ex
index 8ffcaf81b..15c93ab1e 100644
--- a/apps/fz_http/lib/fz_http/users/user.ex
+++ b/apps/fz_http/lib/fz_http/users/user.ex
@@ -9,7 +9,7 @@ defmodule FzHttp.Users.User do
use Ecto.Schema
import Ecto.Changeset
import FzHttp.Users.PasswordHelpers
- import FzHttp.SharedValidators, only: [trim: 2]
+ import FzHttp.Validators.Common, only: [trim: 2]
alias FzHttp.{Devices.Device, OIDC.Connection}
diff --git a/apps/fz_http/lib/fz_http/shared_validators.ex b/apps/fz_http/lib/fz_http/validators/common.ex
similarity index 98%
rename from apps/fz_http/lib/fz_http/shared_validators.ex
rename to apps/fz_http/lib/fz_http/validators/common.ex
index ba52b0d32..f518bcb54 100644
--- a/apps/fz_http/lib/fz_http/shared_validators.ex
+++ b/apps/fz_http/lib/fz_http/validators/common.ex
@@ -1,4 +1,4 @@
-defmodule FzHttp.SharedValidators do
+defmodule FzHttp.Validators.Common do
@moduledoc """
Shared validators to use between schemas.
"""
diff --git a/apps/fz_http/lib/fz_http/validators/openid_connect.ex b/apps/fz_http/lib/fz_http/validators/openid_connect.ex
new file mode 100644
index 000000000..3c634dcc3
--- /dev/null
+++ b/apps/fz_http/lib/fz_http/validators/openid_connect.ex
@@ -0,0 +1,20 @@
+defmodule FzHttp.Validators.OpenIDConnect do
+ @moduledoc """
+ Validators various fields related to OpenID Connect
+ before they're saved and passed to the underlying
+ openid_connect library where they could become an issue.
+ """
+ import Ecto.Changeset
+
+ def validate_discovery_document_uri(changeset) do
+ changeset
+ |> validate_change(:discovery_document_uri, fn :discovery_document_uri, value ->
+ case OpenIDConnect.update_documents(discovery_document_uri: value) do
+ {:ok, _update_result} ->
+ []
+ {:error, :update_documents, reason} ->
+ [discovery_document_uri: "is invalid. Reason: #{inspect(reason)}"]
+ end
+ end)
+ end
+end
diff --git a/apps/fz_http/lib/fz_http/validators/saml.ex b/apps/fz_http/lib/fz_http/validators/saml.ex
new file mode 100644
index 000000000..f886ecb23
--- /dev/null
+++ b/apps/fz_http/lib/fz_http/validators/saml.ex
@@ -0,0 +1,21 @@
+defmodule FzHttp.Validators.SAML do
+ @moduledoc """
+ Validators for SAML configs.
+ """
+
+ alias Samly.IdpData
+ import Ecto.Changeset
+
+ def validate_metadata(changeset) do
+ changeset
+ |> validate_change(:metadata, fn :metadata, value ->
+ try do
+ IdpData.from_xml(value, %IdpData{})
+ []
+ catch
+ :exit, e ->
+ [metadata: "is invalid. Details: #{inspect(e)}."]
+ end
+ end)
+ end
+end
diff --git a/apps/fz_http/mix.exs b/apps/fz_http/mix.exs
index c0a246507..611e21754 100644
--- a/apps/fz_http/mix.exs
+++ b/apps/fz_http/mix.exs
@@ -64,8 +64,8 @@ defmodule FzHttp.MixProject do
{:mox, "~> 1.0.1", only: :test},
{:guardian, "~> 2.0"},
{:guardian_db, "~> 2.0"},
- {:openid_connect, "~> 0.2.2"},
- {:samly, github: "dropbox/samly"},
+ {:openid_connect, github: "firezone/openid_connect"},
+ {:samly, github: "firezone/samly"},
{:ueberauth, "~> 0.7"},
{:ueberauth_identity, "~> 0.4"},
{:httpoison, "~> 1.8"},
diff --git a/apps/fz_http/test/fz_http_web/live/setting_live/security_test.exs b/apps/fz_http/test/fz_http_web/live/setting_live/security_test.exs
index e2a8d6484..e74c32a35 100644
--- a/apps/fz_http/test/fz_http_web/live/setting_live/security_test.exs
+++ b/apps/fz_http/test/fz_http_web/live/setting_live/security_test.exs
@@ -3,6 +3,7 @@ defmodule FzHttpWeb.SettingLive.SecurityTest do
alias FzHttp.Configurations, as: Conf
alias FzHttpWeb.SettingLive.Security
+ import FzHttp.SAMLConfigFixtures
describe "authenticated mount" do
test "loads the active sessions table", %{admin_conn: conn} do
@@ -148,7 +149,7 @@ defmodule FzHttpWeb.SettingLive.SecurityTest do
setup %{admin_conn: conn} do
Conf.update_configuration(%{
openid_connect_providers: %{},
- saml_identity_providers: %{"test" => %{"metadata" => "
SAML Config
| - assert html =~ ~s|<test></test>| + assert html =~ ~s|entityID="http://localhost:8080/realms/firezone| end test "validate", %{view: view} do @@ -189,7 +190,7 @@ defmodule FzHttpWeb.SettingLive.SecurityTest do assert html =~ ~s|SAML Config
| # not updated - assert Conf.get!(:saml_identity_providers) == %{"test" => %{"metadata" => "