Skip to content

Feat: Allow web interface to serve content behind an optional URL path prefix#1124

Merged
xingzhang-suse merged 5 commits into
neuvector:mainfrom
ngearhart:feat/enable-path-prefix-http
Jun 26, 2026
Merged

Feat: Allow web interface to serve content behind an optional URL path prefix#1124
xingzhang-suse merged 5 commits into
neuvector:mainfrom
ngearhart:feat/enable-path-prefix-http

Conversation

@ngearhart

@ngearhart ngearhart commented Jan 27, 2026

Copy link
Copy Markdown
Contributor

The Neuvector UI (manager) requires all traffic to be hosted at the root URL behind an FQDN. Although this fits most deployment scenarios, some users (me included) need to route traffic based on URL path prefixes. For example, instead of hosting at "https://neuvector.example.com/", I use "https://example.com/neuvector/".

This PR adds the necessary scaffolding to support a path prefix as described using the optional PATH_PREFIX environment variable. If unset, the Neuvector manager will continue to work as previous, serving at the root URL.

This environment variable must not contain a preceding slash.

To test, simply set the environment variable in your environment, and navigate to that new path (for example, if you usually navigate to http://localhost:8443, set the var to test-path and navigate to http://localhost:8443/test-path/).

@ngearhart
ngearhart requested a review from a team as a code owner January 27, 2026 15:24
@ngearhart
ngearhart requested a review from lsongsuse January 27, 2026 15:24
@ngearhart

Copy link
Copy Markdown
Contributor Author

I want to add that I am not married to the environment variable being named PATH_PREFIX - I have a coworker who prefers BASENAME - so let me know if you would like me to change it. That is an easy fix.

@wilderk

wilderk commented Jan 30, 2026

Copy link
Copy Markdown

I am the other person ngearhart was talking about. My suggestion was BASE_PATH. And there are other alternatives like RELATIVE_PATH, etc. Just depends on what the reviewers prefer.

@xingzhang-suse xingzhang-suse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a discussion comment to you.
As this PR is valid case according to PM's confirmation, we will merge it in the next release.
Could you please confirm my question? Thank you!

val rawPathOption: Option[String] = sys.env.get("PATH_PREFIX")

rawPathOption match {
case Some(value) => "/" + value.trim

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the value be escaped before it append to the URL?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should. Let me make that change and get back to you.

@olblak olblak added the enhancement New feature or request label Mar 24, 2026
@xingzhang-suse

Copy link
Copy Markdown
Collaborator

@ngearhart Could you please let me know if your will still update the code? Thanks

@ngearhart

Copy link
Copy Markdown
Contributor Author

@ngearhart Could you please let me know if your will still update the code? Thanks

Good morning, I will put this on my TODO list for this week

@ngearhart

Copy link
Copy Markdown
Contributor Author

@xingzhang-suse The change has been made as requested. Thank you for your patience

…path prefix

Signed-off-by: Noah Gearhart <noah.gearhart@darkwolfsolutions.com>
Signed-off-by: Noah Gearhart <noah.gearhart@darkwolfsolutions.com>
@ngearhart
ngearhart force-pushed the feat/enable-path-prefix-http branch from 6a0301c to 52a3bfc Compare June 17, 2026 12:24
@xingzhang-suse

Copy link
Copy Markdown
Collaborator

@ngearhart
Thank you for your update and contribution for the code.
I will verify it in the testbed first.

@xingzhang-suse

xingzhang-suse commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

@ngearhart
When testing the PR, there is an error during opening the page. It shows Request is missing required HTTP header 'Token' in the page. It means it redirected to a URL which is not non-token validating path.

I debugged it and made some changes as below

diff --git a/admin/src/main/scala/com/neu/api/Api.scala b/admin/src/main/scala/com/neu/api/Api.scala
index 188df1bd..d0de9d62 100644
--- a/admin/src/main/scala/com/neu/api/Api.scala
+++ b/admin/src/main/scala/com/neu/api/Api.scala
@@ -39,6 +39,8 @@ import scala.concurrent.ExecutionContext.Implicits.global
  * access to the top-level actors that make up the system.
  */
 trait Api extends Directives with CoreActors with Core {
+  private val managerPathPrefix: Option[String] =
+    sys.env.get("PATH_PREFIX").map(_.trim).filter(_.nonEmpty)
 
   private final val timeOutStatus              = "Status: 408"
   private final val authenticationFailedStatus = "Status: 401"
@@ -93,7 +95,7 @@ trait Api extends Directives with CoreActors with Core {
   private val workloadApi       = new WorkloadApi(workloadService)
 
   val routes: Route = handleExceptions(exceptionHandler) {
-    rawPathPrefix(sys.env.get("PATH_PREFIX").map(r => Slash ~ r).getOrElse("")) {
+    rawPathPrefix(managerPathPrefix.map(r => Slash ~ r).getOrElse("")) {
       authenticationApi.route ~
       dashboardApi.route ~
       clusterApi.route ~
diff --git a/admin/src/main/scala/com/neu/web/StaticResources.scala b/admin/src/main/scala/com/neu/web/StaticResources.scala
index 678e0963..a42bfc42 100644
--- a/admin/src/main/scala/com/neu/web/StaticResources.scala
+++ b/admin/src/main/scala/com/neu/web/StaticResources.scala
@@ -16,12 +16,14 @@ import org.apache.pekko.http.scaladsl.server.Directives
 import org.apache.pekko.http.scaladsl.server.Route
 
 trait StaticResources extends Directives with LazyLogging {
-  private val shortPath           = 10
-  private val isUsingSSL: Boolean = sys.env.getOrElse("MANAGER_SSL", "on") == "on"
-  private val isDev: Boolean      = sys.env.getOrElse("IS_DEV", "false") == "true"
+  private val shortPath                         = 10
+  private val isUsingSSL: Boolean               = sys.env.getOrElse("MANAGER_SSL", "on") == "on"
+  private val isDev: Boolean                    = sys.env.getOrElse("IS_DEV", "false") == "true"
+  private val managerPathPrefix: Option[String] =
+    sys.env.get("PATH_PREFIX").map(_.trim).filter(_.nonEmpty)
 
   private val _managerPathPrefixWithPrependedSlash: String = {
-    val rawPathOption: Option[String] = sys.env.get("PATH_PREFIX")
+    val rawPathOption: Option[String] = managerPathPrefix
 
     rawPathOption match {
       case Some(value) => "/" + UrlEscapers.urlFragmentEscaper().escape(value.trim)
@@ -53,7 +55,7 @@ trait StaticResources extends Directives with LazyLogging {
     }
 
   val staticResources: Route = get {
-    rawPathPrefix(sys.env.get("PATH_PREFIX").map(r => Slash ~ r).getOrElse("")) {
+    rawPathPrefix(managerPathPrefix.map(r => Slash ~ r).getOrElse("")) {
       path("") {
         redirectMe(
           UrlEscapers
PATH_PREFIX .map(_.trim) .map(.trim).filter(.nonEmpty)
Not defined None None
/api Some("/api") Some("/api")
/api Some("/api") Some("/api")
`` (empty string) Some("") None
(spaces only) Some("") None

Without the trim and nonEmpty, if PATH_PREFIX is not defined, it won't be returned as None which can be covered by _.getOrElse(""). Instead, it will be parsed by .map(r => Slash ~ r) first.

Could you please review it and make the fix?

@xingzhang-suse
xingzhang-suse removed the request for review from lsongsuse June 23, 2026 02:13
@ngearhart

Copy link
Copy Markdown
Contributor Author

@xingzhang-suse I am working on your requested revision. I am slightly delayed by some local build problems but I am hoping to get it resolved today or tomorrow.

@ngearhart

Copy link
Copy Markdown
Contributor Author

@xingzhang-suse I have integrated your changes and tested it successfully on my end. This is good to go from my perspective.
Also, for Request is missing required HTTP header 'Token' - That happens if your request path does not end in a trailing slash (e.g. PATH_PREFIX=test and you navigate to https://example.com/test instead of https://example.com/test/). Is there somewhere you would like to document this, or build in a redirect?

@xingzhang-suse

Copy link
Copy Markdown
Collaborator

@ngearhart
Thank you for your update.

Also, for Request is missing required HTTP header 'Token' - That happens if your request path does not end in a trailing slash (e.g. PATH_PREFIX=test and you navigate to https://example.com/test instead of https://example.com/test/). Is there somewhere you would like to document this, or build in a redirect?

Sure, this is a known behavior after the fix. We will document it.

@xingzhang-suse xingzhang-suse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM now

@xingzhang-suse
xingzhang-suse merged commit 451cd67 into neuvector:main Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants