-
Notifications
You must be signed in to change notification settings - Fork 417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add configuration to serve public routes from a different base url path #2704
Add configuration to serve public routes from a different base url path #2704
Conversation
Uffizzi Preview |
7806e41
to
4f7a881
Compare
4f7a881
to
9f44f9e
Compare
~p"/public/sessions/node/#{node_id}/assets/#{hash}/#{file_parts}" | ||
|> String.replace( | ||
Livebook.Config.base_url_path(), | ||
Livebook.Config.public_base_url_path() | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replacing in the whole string may break in very specific cases. Let's define a function:
def public_path(path) do
base_url_path = Livebook.Config.base_url_path()
public_base_url_path = Livebook.Config.public_base_url_path()
^base_url_path <> rest = url
public_base_url_path <> rest
end
We can put it in LivebookWeb
and import in verified_routes
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry, I don't know how to make verified_routes
use the newly defined function.
I've tried removing the String.replace()
call from the highlighted code, and adding this to LivebookWeb
, but it doesn't seem to be doing anything
def public_path(path) do
base_url_path = Livebook.Config.base_url_path()
public_base_url_path = Livebook.Config.public_base_url_path()
^base_url_path <> rest = path
public_base_url_path <> rest
end
def verified_routes do
quote do
use Phoenix.VerifiedRoutes,
endpoint: LivebookWeb.Endpoint,
router: LivebookWeb.Router,
statics: LivebookWeb.static_paths()
# We don't know the hostname Livebook runs on, so we don't use
# absolute URL helpers
import Phoenix.VerifiedRoutes, only: :sigils
import LivebookWeb, only: [public_path: 1]
end
end
Could you explain what I need to do, please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@elepedus oh, this looks good, we still need to call it explicitly, but at least it's more convenient. So in the highlighted code, do this:
public_path(~p"/public/sessions/node/#{node_id}/assets/#{hash}/#{file_parts}")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome -- I've done that :)
Out of curiosity, would there be a way to integrate that new function with sigil_p
, so we don't need to call it explicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aaactually, we can :)
def verified_routes do
quote do
use Phoenix.VerifiedRoutes,
endpoint: LivebookWeb.Endpoint,
router: LivebookWeb.Router,
statics: LivebookWeb.static_paths()
# We don't know the hostname Livebook runs on, so we don't use
# absolute URL helpers. We don't import sigil_p either, because
# we override it.
import Phoenix.VerifiedRoutes, only: []
import LivebookWeb, only: [sigil_p: 2]
end
end
# ...
# Overrides
require Phoenix.VerifiedRoutes
defmacro sigil_p({:<<>>, _meta, ["/public/" <> _ | _]} = route, extra) do
# We allow configuring a base path for all routes and we configure
# Phoenix to use it. However, we have an additional configuration
# for base path applying only to /public. We use a custom sigil_p
# to insert this base path if needed.
quote do
path = Phoenix.VerifiedRoutes.sigil_p(unquote(route), unquote(extra))
LivebookWeb.__rewrite_public_base_path__(path)
end
end
defmacro sigil_p(route, extra) do
quote do
Phoenix.VerifiedRoutes.sigil_p(unquote(route), unquote(extra))
end
end
def __rewrite_public_base_path__(path) do
base_url_path = Livebook.Config.base_url_path()
public_base_url_path = Livebook.Config.public_base_url_path()
^base_url_path <> rest = path
public_base_url_path <> rest
end
@josevalim wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That looks fine to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that's really cool! 😎
I'm curious, though, why do we still need import Phoenix.VerifiedRoutes, only: []
?
Reading naively, it looks like we're saying "import nothing from Phoenix.VerifiedRoutes", which should be equivalent to deleting the line entirely?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use Phoenix.VerifiedRoutes
imports everything (ref), so we need to unimport it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh! I see! Thanks! :)
We also need to fix the paths in image and audio input components. Search for |
9f44f9e
to
06e0a7e
Compare
I've done this, but the relevant smart cells (Neural Network Task -> Image Classification / Speech-to-Text) seemed to work even before the change? |
acb4a11
to
aad5688
Compare
In case of these inputs we use But I think we should apply the base path to all /public for consistency. With the overridden sigil it will happen automatically, so we are good :) |
aad5688
to
669c065
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks :)
Background
I've been trying to deploy Livebook in Elastic Kubernetes Service, at a non-root path (
/livebook/
), behind an Application Load Balancer Ingress with SSO enabled via annotations. This breaks smart cell JS loading, because/livebook/public/*
can't be excluded from the authenticated routes (annotations apply to all rules in the Ingress)It is possible to create another, unauthenticated, ingress using the same ALB group name, and the rules will get combined. Unfortunately, when combining the rules, the ALB Ingress implementation doesn't follow the official Kubernetes Ingress specification, which dictates that longer path prefix rules should get higher priority. Instead, all the rules from the secure ingress are added first, and then the ones from the public ingress are added. This means that /livebook/* authenticated route matches /livebook/public/* requests, making them require auth. I've verified that manually swapping the priority of these rules in the AWS console makes everything work, but since they're controlled by Kubernetes, they reset every time the ingress is redeployed.
Until AWS fixes their ingress implementation, we can work around this limitation by mapping the public routes to a completely different path prefix, that doesn't match the secure route.
Implementation
This PR introduces a new environment variable called
LIVEBOOK_PUBLIC_BASE_URL_PATH_OVERRIDE
, that overrides the base URL path only for/public/*
routes.Testing
The easiest way to test this locally is using Caddy as a reverse proxy.
# Run Caddy caddy run
Visit
localhost:8080/livebook/
, create a new notebook, add a Map smart cell. Observe scripts loading correctly.Counter-example
Visit
localhost:8080/livebook/
, create a new notebook, add a Map smart cell. Observe error loading scripts, as the Iframe attempts to load the scripts from the authenticated base path without the correct basic auth credentials.I appreciate this is a bit of a hack, and will gladly re-work as needed, if there's a better way of making Livebook work in this scenario :)