From 22d02a927d9e88b727317d37eb036eba5e18826d Mon Sep 17 00:00:00 2001
From: Victor Rodrigues <rodrigues@users.noreply.github.com>
Date: Sun, 22 Oct 2023 11:01:15 +0200
Subject: [PATCH] Support custom HTTP certificate (#2287)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Jonatan KÅ‚osko <jonatanklosko@gmail.com>
---
 README.md                  |  6 +++++-
 lib/livebook.ex            |  4 ++++
 lib/livebook/config.ex     | 15 +++++++++++++++
 lib/livebook/utils/http.ex | 10 +++++++++-
 4 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index e97d57259cb..5785c2d8405 100644
--- a/README.md
+++ b/README.md
@@ -191,7 +191,7 @@ The following environment variables can be used to configure Livebook on boot:
     are deployed on Livebook startup with the persisted settings. Password-protected
     notebooks will receive a random password, unless LIVEBOOK_APPS_PATH_PASSWORD
     is set. When deploying using Livebook's Docker image, consider using
-    `LIVEBOOK_APPS_PATH_WARMUP`.
+    LIVEBOOK_APPS_PATH_WARMUP.
 
   * LIVEBOOK_APPS_PATH_HUB_ID - deploy only the notebooks in
     LIVEBOOK_APPS_PATH that belong to the given Hub ID
@@ -208,6 +208,10 @@ The following environment variables can be used to configure Livebook on boot:
   * LIVEBOOK_BASE_URL_PATH - sets the base url path the web application is
     served on. Useful when deploying behind a reverse proxy.
 
+  * LIVEBOOK_CACERTFILE - path to a local file containing CA certificates.
+    Those certificates are used during for server authentication when Livebook
+    accesses files from external sources.
+
   * LIVEBOOK_COOKIE - sets the cookie for running Livebook in a cluster.
     Defaults to a random string that is generated on boot.
 
diff --git a/lib/livebook.ex b/lib/livebook.ex
index 1e3118eafef..0c6f9eff8e0 100644
--- a/lib/livebook.ex
+++ b/lib/livebook.ex
@@ -177,6 +177,10 @@ defmodule Livebook do
       config :livebook, :force_ssl_host, force_ssl_host
     end
 
+    if cacertfile = Livebook.Config.cacertfile!("LIVEBOOK_CACERTFILE") do
+      config :livebook, :cacertfile, cacertfile
+    end
+
     config :livebook,
            :cookie,
            Livebook.Config.cookie!("LIVEBOOK_COOKIE") ||
diff --git a/lib/livebook/config.ex b/lib/livebook/config.ex
index 52585038c3e..e1630920c89 100644
--- a/lib/livebook/config.ex
+++ b/lib/livebook/config.ex
@@ -319,6 +319,14 @@ defmodule Livebook.Config do
     Application.fetch_env!(:livebook, :force_ssl_host)
   end
 
+  @doc """
+  Returns the application cacertfile if any.
+  """
+  @spec cacertfile() :: String.t() | nil
+  def cacertfile() do
+    Application.get_env(:livebook, :cacertfile)
+  end
+
   @feature_flags Application.compile_env(:livebook, :feature_flags)
 
   @doc """
@@ -513,6 +521,13 @@ defmodule Livebook.Config do
     System.get_env(env)
   end
 
+  @doc """
+  Parses application cacertfile from env.
+  """
+  def cacertfile!(env) do
+    System.get_env(env)
+  end
+
   @doc """
   Parses application service name from env.
   """
diff --git a/lib/livebook/utils/http.ex b/lib/livebook/utils/http.ex
index 6e41c9a9b01..c71800fd48c 100644
--- a/lib/livebook/utils/http.ex
+++ b/lib/livebook/utils/http.ex
@@ -213,9 +213,17 @@ defmodule Livebook.Utils.HTTP do
 
   defp http_ssl_opts() do
     # Use secure options, see https://gist.github.com/jonatanklosko/5e20ca84127f6b31bbe3906498e1a1d7
+
+    cacert_opt =
+      if cacertfile = Livebook.Config.cacertfile() do
+        {:cacertfile, to_charlist(cacertfile)}
+      else
+        {:cacerts, @cacerts}
+      end
+
     [
+      cacert_opt,
       verify: :verify_peer,
-      cacerts: @cacerts,
       customize_hostname_check: [
         match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
       ]