From a1f622eddeee77cdcdd7b3eda5c346258c2168c1 Mon Sep 17 00:00:00 2001 From: Luca Zaninotto Date: Fri, 13 Dec 2024 15:54:19 +0100 Subject: [PATCH] chore(database): Add ssl support Adds ssl/tls support for the edgehog <-> database communication. Closes #419. Signed-off-by: Luca Zaninotto --- .env | 16 +++++++ backend/config/runtime.exs | 97 ++++++++++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 10 deletions(-) diff --git a/.env b/.env index 264215f0b..9fc116042 100644 --- a/.env +++ b/.env @@ -22,6 +22,22 @@ IPBASE_API_KEY= GOOGLE_GEOLOCATION_API_KEY= GOOGLE_GEOCODING_API_KEY= +# Wether to use SSL/TLS connection to the database, defaults to `false`. +DATABASE_ENABLE_SSL=false + +# Whether to use the host machine certificates or not to verify the connection +# with the database. +# DATABASE_USE_OS_CERTS=true + +# The CA certificate file to use to verify the TLS connection to the database. +# DATABASE_SSL_CACERTFILE=./example_cacert.pem + +# Whether verify the SSL connection with the database or not. +# DATABASE_SSL_VERIFY=true + +# The server name indication for the SSL connection with the database. +# DATABASE_SERVER_NAME_INDICATION=https://www.example.com + # The top level domain of your edgehog instance. # In case you want to make Edgehog visible in your LAN, consider setting the variable # to .nip.io diff --git a/backend/config/runtime.exs b/backend/config/runtime.exs index efeb95621..6f05be827 100644 --- a/backend/config/runtime.exs +++ b/backend/config/runtime.exs @@ -31,10 +31,86 @@ end # any compile-time configuration in here, as it won't be applied. # The block below contains prod specific runtime configuration. if config_env() == :prod do - database_username = System.fetch_env!("DATABASE_USERNAME") - database_password = System.fetch_env!("DATABASE_PASSWORD") - database_hostname = System.fetch_env!("DATABASE_HOSTNAME") - database_name = System.fetch_env!("DATABASE_NAME") + database = %{ + username: System.fetch_env!("DATABASE_USERNAME"), + password: System.fetch_env!("DATABASE_PASSWORD"), + hostname: System.fetch_env!("DATABASE_HOSTNAME"), + name: System.fetch_env!("DATABASE_NAME"), + ssl_enable: System.fetch_env!("DATABASE_ENABLE_SSL"), + # with ssl_enable = false no additional config is required + ssl_opts: nil + } + + database = + if database.ssl_enable == "true" do + use_os_certs = + case System.get_env("DATABASE_USE_OS_CERTS") || "false" do + "true" -> + true + + "false" -> + false + + other -> + raise """ + invalid database SSL configuration: + unknown value `#{other}` for variable DATABASE_USE_OS_CERTS. + """ + end + + certfile = System.get_env("DATABASE_SSL_CACERTFILE") + + cert_config = + case {certfile, use_os_certs} do + {nil, false} -> + raise """ + invalid database SSL configuration: + either set DATABASE_USE_OS_CERTS true to use system's certificates + or provide a CA certificate file with DATABASE_SSL_CACERTFILE. + The latter will take precedence. + """ + + {nil, true} -> + {:cacerts, :public_key.cacerts_get()} + + {file, _} -> + # Assuming `file` is a file path + {:cacertfile, file} + end + + # Found conflicting informations on this. According to the documentation it + # can default to the `Host` argument of `connect/3,4` + # (see "https://www.erlang.org/doc/apps/ssl/ssl.html#t:client_option_cert/0") + # so it is acceptable to levae it as `nil` by default + server_name_indication = + {:server_name_indication, "DATABASE_SERVER_NAME_INDICATION" |> System.get_env() |> to_charlist()} + + verify = + case System.get_env("DATABASE_SSL_VERIFY") || "false" do + "true" -> + {:verify, :verify_peer} + + "false" -> + {:verify, :verify_none} + + _ -> + raise """ + invalid database SSL configuration, DATABASE_SSL_VERIFY can be set to `true` + to verify TLS certificates or `false` to skip verification + """ + end + + opts = [ + cert_config, + verify, + server_name_indication + ] + + # add config to the database map + Map.put(database, :ssl_opts, opts) + else + database + end # The secret key base is used to sign/encrypt cookies and other secrets. # A default value is used in config/dev.exs and config/test.exs but you @@ -102,13 +178,14 @@ if config_env() == :prod do forwarder_hostname = System.get_env("EDGEHOG_FORWARDER_HOSTNAME") config :edgehog, Edgehog.Repo, - # ssl: true, # socket_options: [:inet6], - username: database_username, - password: database_password, - hostname: database_hostname, - database: database_name, - pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10") + username: database.username, + password: database.password, + hostname: database.hostname, + database: database.name, + pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"), + ssl: database.ssl_enable, + ssl_opts: database.ssl_opts config :edgehog, EdgehogWeb.Endpoint, http: [