From 38cceb56aceee3acafcdabeca8b262bcdd04d495 Mon Sep 17 00:00:00 2001 From: Andrew Dryga Date: Thu, 23 Feb 2023 13:57:10 -0600 Subject: [PATCH] Fix various issues after config rework (#1462) Closes #1461 Closes #1458 Closes #1415 Closes #1460 --- .../fz_http/lib/fz_http/config/definitions.ex | 7 ++-- apps/fz_http/lib/fz_http/devices/device.ex | 11 +++--- apps/fz_http/lib/fz_http/release.ex | 39 ++++++++++++------- apps/fz_http/lib/fz_http/validator.ex | 3 +- .../fz_http/lib/fz_http_web/header_helpers.ex | 5 ++- .../20230223175621_trim_dns_fields.exs | 33 ++++++++++++++++ apps/fz_http/test/fz_http/devices_test.exs | 6 ++- apps/fz_http/test/fz_http/rules_test.exs | 11 +++++- config/config.exs | 18 ++++++--- .../cookbooks/firezone/attributes/default.rb | 38 ++++++++++++------ .../cookbooks/firezone/libraries/config.rb | 26 +++++++------ rel/overlays/bin/create-api-token | 1 + rel/overlays/bin/create-or-reset-admin | 1 + rel/overlays/bin/migrate | 1 + rel/overlays/bin/server | 2 +- 15 files changed, 144 insertions(+), 58 deletions(-) create mode 100644 apps/fz_http/priv/repo/migrations/20230223175621_trim_dns_fields.exs diff --git a/apps/fz_http/lib/fz_http/config/definitions.ex b/apps/fz_http/lib/fz_http/config/definitions.ex index 933691c8e..7221c7691 100644 --- a/apps/fz_http/lib/fz_http/config/definitions.ex +++ b/apps/fz_http/lib/fz_http/config/definitions.ex @@ -157,7 +157,6 @@ defmodule FzHttp.Config.Definitions do changeset |> FzHttp.Validator.validate_uri(key) |> FzHttp.Validator.normalize_url(key) - |> to_string() end ) @@ -684,8 +683,10 @@ defmodule FzHttp.Config.Definitions do ) defconfig(:wireguard_private_key_path, :string, - default: "/var/firezone/private_key", - changeset: &FzHttp.Validator.validate_file(&1, &2) + default: "/var/firezone/private_key" + # We don't check if the file exists, because it is generated on + # the first boot. + # changeset: &FzHttp.Validator.validate_file(&1, &2) ) defconfig(:wireguard_interface_name, :string, default: "wg-firezone") diff --git a/apps/fz_http/lib/fz_http/devices/device.ex b/apps/fz_http/lib/fz_http/devices/device.ex index 0555da857..d898e00cf 100644 --- a/apps/fz_http/lib/fz_http/devices/device.ex +++ b/apps/fz_http/lib/fz_http/devices/device.ex @@ -157,14 +157,13 @@ defmodule FzHttp.Devices.Device do end defp wireguard_network(field) do - cidr_string = FzHttp.Config.fetch_env!(:fz_http, :"wireguard_#{field}_network") - [inet, network] = String.split(cidr_string, "/") - network = String.to_integer(network) - "#{inet}/#{limit_cidr_range(field, network)}" + cidr = FzHttp.Config.fetch_env!(:fz_http, :"wireguard_#{field}_network") + cidr = %{cidr | netmask: limit_cidr_netmask(field, cidr.netmask)} + FzHttp.Types.CIDR.to_string(cidr) end - defp limit_cidr_range(:ipv4, network), do: network - defp limit_cidr_range(:ipv6, network), do: max(network, 70) + defp limit_cidr_netmask(:ipv4, network), do: network + defp limit_cidr_netmask(:ipv6, network), do: max(network, 70) defp ipv4_address do FzHttp.Config.fetch_env!(:fz_http, :wireguard_ipv4_address) diff --git a/apps/fz_http/lib/fz_http/release.ex b/apps/fz_http/lib/fz_http/release.ex index d82f8e144..033a5e57e 100644 --- a/apps/fz_http/lib/fz_http/release.ex +++ b/apps/fz_http/lib/fz_http/release.ex @@ -26,22 +26,33 @@ defmodule FzHttp.Release do def create_admin_user do boot_database_app() - reply = - if Repo.exists?(from u in User, where: u.email == ^email()) do - change_password(email(), default_password()) - reset_role(email(), :admin) + if Repo.exists?(from u in User, where: u.email == ^email()) do + change_password(email(), default_password()) + {:ok, user} = reset_role(email(), :admin) + + # Notify the user + Logger.info("Password for user specified by ADMIN_EMAIL reset to DEFAULT_ADMIN_PASSWORD!") + + {:ok, user} + else + with {:ok, user} <- + Users.create_admin_user(%{ + email: email(), + password: default_password(), + password_confirmation: default_password() + }) do + # Notify the user + Logger.info( + "An admin user specified by ADMIN_EMAIL is created with a DEFAULT_ADMIN_PASSWORD!" + ) + + {:ok, user} else - Users.create_admin_user(%{ - email: email(), - password: default_password(), - password_confirmation: default_password() - }) + {:error, changeset} -> + Logger.error("Failed to create admin user: #{inspect(changeset.errors)}") + {:error, changeset} end - - # Notify the user - Logger.info("Password for user specified by ADMIN_EMAIL reset to DEFAULT_ADMIN_PASSWORD!") - - reply + end end def create_api_token(device \\ :stdio) do diff --git a/apps/fz_http/lib/fz_http/validator.ex b/apps/fz_http/lib/fz_http/validator.ex index 5ef131ec2..82372415b 100644 --- a/apps/fz_http/lib/fz_http/validator.ex +++ b/apps/fz_http/lib/fz_http/validator.ex @@ -48,7 +48,8 @@ defmodule FzHttp.Validator do scheme = uri.scheme || "https" port = URI.default_port(scheme) path = uri.path || "/" - put_change(changeset, field, %{uri | scheme: scheme, port: port, path: path}) + uri_string = URI.to_string(%{uri | scheme: scheme, port: port, path: path}) + put_change(changeset, field, uri_string) else :error -> changeset diff --git a/apps/fz_http/lib/fz_http_web/header_helpers.ex b/apps/fz_http/lib/fz_http_web/header_helpers.ex index 388465009..3848a0643 100644 --- a/apps/fz_http/lib/fz_http_web/header_helpers.ex +++ b/apps/fz_http/lib/fz_http_web/header_helpers.ex @@ -10,7 +10,10 @@ defmodule FzHttpWeb.HeaderHelpers do |> Enum.map(&to_string/1) end - def clients, do: FzHttp.Config.fetch_env!(:fz_http, :private_clients) + def clients do + FzHttp.Config.fetch_env!(:fz_http, :private_clients) + |> Enum.map(&to_string/1) + end def proxied?, do: external_trusted_proxies() != [] diff --git a/apps/fz_http/priv/repo/migrations/20230223175621_trim_dns_fields.exs b/apps/fz_http/priv/repo/migrations/20230223175621_trim_dns_fields.exs new file mode 100644 index 000000000..5b31556f3 --- /dev/null +++ b/apps/fz_http/priv/repo/migrations/20230223175621_trim_dns_fields.exs @@ -0,0 +1,33 @@ +defmodule FzHttp.Repo.Migrations.TrimDNSFields do + use Ecto.Migration + + def change do + execute(""" + UPDATE devices + SET dns = string_to_array( + trim( + both ' ' from regexp_replace( + array_to_string(dns, ','), + '\s*,\s*', + ',' + ) + ), + ',' + ) + """) + + execute(""" + UPDATE configurations + SET default_client_dns = string_to_array( + trim( + both ' ' from regexp_replace( + array_to_string(default_client_dns, ','), + '\s*,\s*', + ',' + ) + ), + ',' + ) + """) + end +end diff --git a/apps/fz_http/test/fz_http/devices_test.exs b/apps/fz_http/test/fz_http/devices_test.exs index cbe375f60..edcccf637 100644 --- a/apps/fz_http/test/fz_http/devices_test.exs +++ b/apps/fz_http/test/fz_http/devices_test.exs @@ -83,7 +83,8 @@ defmodule FzHttp.DevicesTest do end test "soft limit max network range for IPv6", %{device: device} do - FzHttp.Config.put_env_override(:wireguard_ipv6_network, "fd00::/20") + {:ok, cidr} = FzHttp.Types.CIDR.cast("fd00::/20") + FzHttp.Config.put_env_override(:wireguard_ipv6_network, cidr) attrs = %{@device_attrs | ipv4: nil, ipv6: nil, user_id: device.user_id} assert {:ok, _device} = Devices.create_device(attrs) end @@ -91,7 +92,8 @@ defmodule FzHttp.DevicesTest do test "returns error when device IP can't be assigned due to CIDR pool exhaustion", %{ device: device } do - FzHttp.Config.put_env_override(:wireguard_ipv4_network, "10.3.2.0/30") + {:ok, cidr} = FzHttp.Types.CIDR.cast("10.3.2.0/30") + FzHttp.Config.put_env_override(:wireguard_ipv4_network, cidr) attrs = %{@device_attrs | ipv4: nil, ipv6: nil, user_id: device.user_id} assert {:ok, _device} = Devices.create_device(attrs) diff --git a/apps/fz_http/test/fz_http/rules_test.exs b/apps/fz_http/test/fz_http/rules_test.exs index 4e32a7c5a..a9f032683 100644 --- a/apps/fz_http/test/fz_http/rules_test.exs +++ b/apps/fz_http/test/fz_http/rules_test.exs @@ -4,8 +4,15 @@ defmodule FzHttp.RulesTest do alias FzHttp.Rules setup do - FzHttp.Config.put_env_override(:wireguard_ipv4_network, "100.64.0.0/10") - FzHttp.Config.put_env_override(:wireguard_ipv6_network, "fd00::0/106") + FzHttp.Config.put_env_override(:wireguard_ipv4_network, %Postgrex.INET{ + address: {100, 64, 0, 0}, + netmask: 10 + }) + + FzHttp.Config.put_env_override(:wireguard_ipv6_network, %Postgrex.INET{ + address: {64_768, 0, 0, 0, 0, 0, 0, 0}, + netmask: 106 + }) :ok end diff --git a/config/config.exs b/config/config.exs index d39bb2b6a..282ac9454 100644 --- a/config/config.exs +++ b/config/config.exs @@ -44,11 +44,19 @@ config :fz_http, FzHttpWeb.Endpoint, config :fz_http, wireguard_ipv4_enabled: true, - wireguard_ipv4_network: "100.64.0.0/10", - wireguard_ipv4_address: "100.64.0.1", + wireguard_ipv4_network: %{__struct__: Postgrex.INET, address: {100, 64, 0, 0}, netmask: 10}, + wireguard_ipv4_address: %{__struct__: Postgrex.INET, address: {100, 64, 0, 1}, netmask: nil}, wireguard_ipv6_enabled: true, - wireguard_ipv6_network: "fd00::/106", - wireguard_ipv6_address: "fd00::1" + wireguard_ipv6_network: %{ + __struct__: Postgrex.INET, + address: {64768, 0, 0, 0, 0, 0, 0, 0}, + netmask: 106 + }, + wireguard_ipv6_address: %{ + __struct__: Postgrex.INET, + address: {64768, 0, 0, 0, 0, 0, 0, 1}, + netmask: nil + } config :fz_http, saml_entity_id: "urn:firezone.dev:firezone-app", @@ -57,7 +65,7 @@ config :fz_http, config :fz_http, external_trusted_proxies: [], - private_clients: ["172.28.0.0/16"] + private_clients: [%{__struct__: Postgrex.INET, address: {172, 28, 0, 0}, netmask: 16}] config :fz_http, telemetry_id: "firezone-dev", diff --git a/omnibus/cookbooks/firezone/attributes/default.rb b/omnibus/cookbooks/firezone/attributes/default.rb index b9e8e10f1..c1e2d678b 100644 --- a/omnibus/cookbooks/firezone/attributes/default.rb +++ b/omnibus/cookbooks/firezone/attributes/default.rb @@ -117,11 +117,6 @@ default['firezone']['sysvinit_id'] = 'SUP' # Local email/password authentication is enabled by default default['firezone']['authentication']['local']['enabled'] = true -# Automatically create users siging in from OIDC for the first time. Disable this -# and manually create them (leaving their password blank) if you wish to only -# allow existing certain existing users to sign in. -default['firezone']['authentication']['auto_create_oidc_users'] = true - # OIDC Authentication # # Firezone can disable a user's VPN if there's any error detected trying @@ -135,15 +130,16 @@ default['firezone']['authentication']['disable_vpn_on_oidc_error'] = false # Any OpenID Connect provider can be used here. # Multiple OIDC configs can be added to the same Firezone instance. # This is an example using Google and Okta as an SSO identity provider. -# default['firezone']['authentication']['oidc'] = { -# google: { +# default['firezone']['authentication']['oidc'] = [ +# { # discovery_document_uri: "https://accounts.google.com/.well-known/openid-configuration", # client_id: "", # client_secret: "", # redirect_uri: "https://firezone.example.com/auth/oidc/google/callback/", # response_type: "code", # scope: "openid email profile", -# label: "Google" +# label: "Google", +# auto_create_users: true # }, # okta: { # discovery_document_uri: "https:///.well-known/openid-configuration", @@ -152,10 +148,30 @@ default['firezone']['authentication']['disable_vpn_on_oidc_error'] = false # redirect_uri: "https://firezone.example.com/auth/oidc/okta/callback/", # response_type: "code", # scope: "openid email profile offline_access", -# label: "Okta" +# label: "Okta", +# auto_create_users: true # } -# } -default['firezone']['authentication']['oidc'] = {} +# ] +default['firezone']['authentication']['oidc'] = [] + +# SAML Authentication providers +# +# Example adding an OKTA provider: +# +# default['firezone']['authentication']['saml'] = [ +# { +# "auto_create_users": false, +# "base_url": "https://saml", +# "id": "okta", +# "label": "okta", +# "metadata": "...", +# "sign_metadata": false, +# "sign_requests": false, +# "signed_assertion_in_resp": false, +# "signed_envelopes_in_resp": false +# } +# ] +default['firezone']['authentication']['saml'] = [] # ## Custom Reverse Proxy # diff --git a/omnibus/cookbooks/firezone/libraries/config.rb b/omnibus/cookbooks/firezone/libraries/config.rb index 63540b461..7792c38c1 100644 --- a/omnibus/cookbooks/firezone/libraries/config.rb +++ b/omnibus/cookbooks/firezone/libraries/config.rb @@ -212,8 +212,8 @@ class Firezone # NOTE: All these variables must be Strings env = { - 'EGRESS_INTERFACE' => attributes['egress_interface'], - 'NFT_PATH' => "#{attributes['install_directory']}/embedded/sbin/nft", + 'GATEWAY_EGRESS_INTERFACE' => attributes['egress_interface'], + 'GATEWAY_NFT_PATH' => "#{attributes['install_directory']}/embedded/sbin/nft", 'MIX_ENV' => 'prod', 'DATABASE_NAME' => attributes['database']['name'], 'DATABASE_USER' => attributes['database']['user'], @@ -224,16 +224,17 @@ class Firezone 'DATABASE_SSL_OPTS' => attributes['database']['ssl_opts'].to_json, 'DATABASE_PARAMETERS' => attributes['database']['parameters'].to_json, 'PHOENIX_LISTEN_ADDRESS' => attributes['phoenix']['listen_address'].to_s, - 'PHOENIX_PORT' => attributes['phoenix']['port'].to_s, - 'EXTERNAL_TRUSTED_PROXIES' => Chef::JSONCompat.to_json(attributes['phoenix']['external_trusted_proxies']), - 'PRIVATE_CLIENTS' => Chef::JSONCompat.to_json(attributes['phoenix']['private_clients']), + 'PHOENIX_HTTP_PORT' => attributes['phoenix']['port'].to_s, + 'PHOENIX_EXTERNAL_TRUSTED_PROXIES' => + Chef::JSONCompat.to_json(attributes['phoenix']['external_trusted_proxies']), + 'PHOENIX_PRIVATE_CLIENTS' => Chef::JSONCompat.to_json(attributes['phoenix']['private_clients']), 'EXTERNAL_URL' => attributes['external_url'] || fqdn_url, - 'ADMIN_EMAIL' => attributes['admin_email'], + 'DEFAULT_ADMIN_EMAIL' => attributes['admin_email'], 'WIREGUARD_INTERFACE_NAME' => attributes['wireguard']['interface_name'], 'WIREGUARD_PORT' => attributes['wireguard']['port'].to_s, 'WIREGUARD_MTU' => attributes['wireguard']['mtu'].to_s, - 'WIREGUARD_ENDPOINT' => attributes['wireguard']['endpoint'].to_s, - 'WIREGUARD_DNS' => attributes['wireguard']['dns'].to_s, + 'DEFAULT_CLIENT_ENDPOINT' => attributes['wireguard']['endpoint'].to_s, + 'DEFAULT_CLIENT_DNS' => attributes['wireguard']['dns'].to_s, 'WIREGUARD_ALLOWED_IPS' => attributes['wireguard']['allowed_ips'].to_s, 'WIREGUARD_PERSISTENT_KEEPALIVE' => attributes['wireguard']['persistent_keepalive'].to_s, 'WIREGUARD_IPV4_ENABLED' => attributes['wireguard']['ipv4']['enabled'].to_s, @@ -256,8 +257,8 @@ class Firezone 'CONNECTIVITY_CHECKS_INTERVAL' => attributes['connectivity_checks']['interval'].to_s, # Outbound Emails - 'OUTBOUND_EMAIL_PROVIDER' => attributes['outbound_email']['provider'], - 'OUTBOUND_EMAIL_CONFIGS' => attributes['outbound_email']['configs'].to_json, + 'OUTBOUND_EMAIL_ADAPTER' => attributes['outbound_email']['provider'], + 'OUTBOUND_EMAIL_ADAPTER_OPTS' => attributes['outbound_email']['configs'].to_json, 'OUTBOUND_EMAIL_FROM' => attributes['outbound_email']['from'], # XXX: Remove this in the future when we're fairly sure that users won't upgrade across @@ -271,10 +272,11 @@ class Firezone 'LOCAL_AUTH_ENABLED' => attributes['authentication']['local']['enabled'].to_s, 'DISABLE_VPN_ON_OIDC_ERROR' => attributes['authentication']['disable_vpn_on_oidc_error'].to_s, - 'AUTO_CREATE_OIDC_USERS' => attributes['authentication']['auto_create_oidc_users'].to_s, # OpenID Connect auth settings are serialized to json for consumption by fz_http - 'AUTH_OIDC_JSON' => attributes['authentication']['oidc'].to_json, + 'OPENID_CONNECT_PROVIDERS' => attributes['authentication']['oidc'].to_json, + # SAML auth settings are serialized to json for consumption by fz_http + 'SAML_IDENTITY_PROVIDERS' => attributes['authentication']['saml'].to_json, # secrets 'GUARDIAN_SECRET_KEY' => attributes['guardian_secret_key'], diff --git a/rel/overlays/bin/create-api-token b/rel/overlays/bin/create-api-token index 6987b3524..cfa2da372 100755 --- a/rel/overlays/bin/create-api-token +++ b/rel/overlays/bin/create-api-token @@ -1,3 +1,4 @@ #!/bin/sh +set -e source "$(dirname -- "$0")/bootstrap" exec ./firezone eval FzHttp.Release.create_api_token diff --git a/rel/overlays/bin/create-or-reset-admin b/rel/overlays/bin/create-or-reset-admin index dffc6fe4c..14159f9d8 100755 --- a/rel/overlays/bin/create-or-reset-admin +++ b/rel/overlays/bin/create-or-reset-admin @@ -1,3 +1,4 @@ #!/bin/sh +set -e source "$(dirname -- "$0")/bootstrap" exec ./firezone eval FzHttp.Release.create_admin_user diff --git a/rel/overlays/bin/migrate b/rel/overlays/bin/migrate index e5d9f912e..9146f99bb 100755 --- a/rel/overlays/bin/migrate +++ b/rel/overlays/bin/migrate @@ -1,3 +1,4 @@ #!/bin/sh +set -e source "$(dirname -- "$0")/bootstrap" exec ./firezone eval FzHttp.Release.migrate diff --git a/rel/overlays/bin/server b/rel/overlays/bin/server index 4bb1cfb0a..53cfabba5 100755 --- a/rel/overlays/bin/server +++ b/rel/overlays/bin/server @@ -1,5 +1,5 @@ #!/bin/sh - +set -e source "$(dirname -- "$0")/bootstrap" ./firezone eval FzHttp.Release.migrate