mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
Expose some http client ssl opts via HTTP_CLIENT_SSL_OPTS (#1221)
Expose the most commonly-used SSL client options to our OIDC and ConnectivityChecks HTTP clients. Resolves some lingering issues some users were facing with OIDC where they needed a custom TLS version enforced or cacert file used to fetch the `discovery_document` and resulting keys. SSL misconfiguration can be a security concern, so we intentionally puke when an unexpected key is passed. This should result in a new GitHub issue being opened and dialog created to learn more about the use-case. Fixes #996
This commit is contained in:
@@ -2,4 +2,31 @@ defmodule FzCommon do
|
||||
@moduledoc """
|
||||
Documentation for `FzCommon`.
|
||||
"""
|
||||
|
||||
@doc ~S"""
|
||||
Maps JSON-decoded ssl opts to pass to Erlang's ssl module. Most users
|
||||
don't need to override many, if any, SSL opts. Most commonly this is
|
||||
to use custom cacert files and TLS versions.
|
||||
|
||||
## Examples:
|
||||
|
||||
iex> FzCommon.map_ssl_opts(%{"verify" => "verify_none", "versions" => ["tlsv1.3"]})
|
||||
[verify: :verify_none, versions: ['tlsv1.3']]
|
||||
|
||||
iex> FzCommon.map_ssl_opts(%{"keep_secrets" => true})
|
||||
** (ArgumentError) unsupported key keep_secrets in ssl opts
|
||||
|
||||
iex> FzCommon.map_ssl_opts(%{"cacertfile" => "/tmp/cacerts.pem"})
|
||||
[cacertfile: '/tmp/cacerts.pem']
|
||||
"""
|
||||
def map_ssl_opts(decoded_json) do
|
||||
Keyword.new(decoded_json, fn {k, v} ->
|
||||
{String.to_atom(k), map_values(k, v)}
|
||||
end)
|
||||
end
|
||||
|
||||
defp map_values("verify", v), do: String.to_atom(v)
|
||||
defp map_values("versions", v), do: Enum.map(v, &String.to_charlist/1)
|
||||
defp map_values("cacertfile", v), do: String.to_charlist(v)
|
||||
defp map_values(k, _v), do: raise(ArgumentError, message: "unsupported key #{k} in ssl opts")
|
||||
end
|
||||
|
||||
@@ -34,7 +34,7 @@ defmodule FzHttp.ConnectivityCheckService do
|
||||
def post_request(request_url) do
|
||||
body = ""
|
||||
|
||||
case http_client().post(request_url, body) do
|
||||
case http_client().post(request_url, body, [], http_client_options()) do
|
||||
{:ok, response} ->
|
||||
ConnectivityChecks.create_connectivity_check(%{
|
||||
response_body: response.body,
|
||||
@@ -79,4 +79,8 @@ defmodule FzHttp.ConnectivityCheckService do
|
||||
defp enabled? do
|
||||
FzHttp.Config.fetch_env!(:fz_http, :connectivity_checks_enabled)
|
||||
end
|
||||
|
||||
defp http_client_options do
|
||||
Application.fetch_env!(:fz_http, :http_client_options)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,4 +29,6 @@ defmodule FzHttp.Mocks.HttpClient do
|
||||
@success_response
|
||||
end
|
||||
end
|
||||
|
||||
def post(url, _, _, _), do: post(url, nil)
|
||||
end
|
||||
|
||||
@@ -23,6 +23,7 @@ config :fz_http, FzHttpWeb.Auth.JSON.Authentication,
|
||||
config :fz_http, FzHttp.Repo, migration_timestamps: [type: :timestamptz]
|
||||
|
||||
config :fz_http,
|
||||
http_client_options: [],
|
||||
external_trusted_proxies: [],
|
||||
private_clients: [],
|
||||
sandbox: true,
|
||||
|
||||
@@ -56,6 +56,7 @@ if config_env() == :prod do
|
||||
database_ssl = FzString.to_boolean(System.get_env("DATABASE_SSL", "false"))
|
||||
database_ssl_opts = Jason.decode!(System.get_env("DATABASE_SSL_OPTS", "{}"))
|
||||
database_parameters = Jason.decode!(System.get_env("DATABASE_PARAMETERS", "{}"))
|
||||
http_client_ssl_opts = Jason.decode!(System.get_env("HTTP_CLIENT_SSL_OPTS", "{}"))
|
||||
phoenix_listen_address = System.get_env("PHOENIX_LISTEN_ADDRESS", "0.0.0.0")
|
||||
phoenix_port = String.to_integer(System.get_env("PHOENIX_PORT", "13000"))
|
||||
external_trusted_proxies = Jason.decode!(System.get_env("EXTERNAL_TRUSTED_PROXIES", "[]"))
|
||||
@@ -118,28 +119,6 @@ if config_env() == :prod do
|
||||
# Password is not needed if using bundled PostgreSQL, so use nil if it's not set.
|
||||
database_password = System.get_env("DATABASE_PASSWORD")
|
||||
|
||||
# XXX: Using to_atom here because this is trusted input and to_existing_atom
|
||||
# won't work because we won't know the keys ahead of time. Hardcoding supported
|
||||
# ssl_opts as well.
|
||||
map_ssl_opt_val = fn k, v ->
|
||||
case k do
|
||||
"verify" ->
|
||||
# verify expects an atom
|
||||
String.to_atom(v)
|
||||
|
||||
"versions" ->
|
||||
# versions expects a list of atoms
|
||||
Enum.map(v, &String.to_atom(&1))
|
||||
|
||||
_ ->
|
||||
# Everything else is usually a string
|
||||
v
|
||||
end
|
||||
end
|
||||
|
||||
ssl_opts =
|
||||
Keyword.new(database_ssl_opts, fn {k, v} -> {String.to_atom(k), map_ssl_opt_val.(k, v)} end)
|
||||
|
||||
parameters = Keyword.new(database_parameters, fn {k, v} -> {String.to_atom(k), v} end)
|
||||
|
||||
# Database configuration
|
||||
@@ -150,7 +129,7 @@ if config_env() == :prod do
|
||||
port: database_port,
|
||||
pool_size: database_pool,
|
||||
ssl: database_ssl,
|
||||
ssl_opts: ssl_opts,
|
||||
ssl_opts: FzCommon.map_ssl_opts(database_ssl_opts),
|
||||
parameters: parameters,
|
||||
queue_target: 500
|
||||
]
|
||||
@@ -213,6 +192,7 @@ if config_env() == :prod do
|
||||
secret_key: guardian_secret_key
|
||||
|
||||
config :fz_http,
|
||||
http_client_options: [ssl: FzCommon.map_ssl_opts(http_client_ssl_opts)],
|
||||
saml_entity_id: saml_entity_id,
|
||||
saml_certfile_path: saml_certfile_path,
|
||||
saml_keyfile_path: saml_keyfile_path,
|
||||
@@ -235,6 +215,10 @@ if config_env() == :prod do
|
||||
admin_email: admin_email,
|
||||
default_admin_password: default_admin_password
|
||||
|
||||
# Configure OpenID Connect
|
||||
config :openid_connect,
|
||||
http_client_options: [ssl: FzCommon.map_ssl_opts(http_client_ssl_opts)]
|
||||
|
||||
# Configure strategies
|
||||
identity_strategy =
|
||||
{:identity,
|
||||
|
||||
@@ -43,6 +43,7 @@ default). Required fields in **bold**.
|
||||
| `DATABASE_SSL` | Whether to connect to the database over SSL | Boolean | `false` |
|
||||
| `DATABASE_SSL_OPTS` | Map of options to send to the `:ssl_opts` option when connecting over SSL. See [Ecto.Adapters.Postgres documentation](https://hexdocs.pm/ecto_sql/Ecto.Adapters.Postgres.html#module-connection-options) | JSON-encoded String | `{}` |
|
||||
| `DATABASE_PARAMETERS` | Map of parameters to send to the `:parameters` option when connecting to the database. See [Ecto.Adapters.Postgres documentation](https://hexdocs.pm/ecto_sql/Ecto.Adapters.Postgres.html#module-connection-options). | JSON-encoded String | `{}` |
|
||||
| `HTTP_CLIENT_SSL_OPTS` | Map of options to use for outbound SSL connections for OIDC document retrieval and Connectivity Checks. | JSON-encoded String | `{}` |
|
||||
| `CONNECTIVITY_CHECKS_ENABLED` | Enable / disable periodic checking for egress connectivity. Determines the instance's public IP to populate `Endpoint` fields. | Boolean | `true` |
|
||||
| `CONNECTIVITY_CHECKS_INTERVAL` | Periodicity in seconds to check for egress connectivity. | Integer | `3600` |
|
||||
| `EXTERNAL_TRUSTED_PROXIES` | List of trusted reverse proxies. | JSON-encoded array | `[]` |
|
||||
|
||||
Reference in New Issue
Block a user