Skip to content

Add UI setting to bind to 0.0.0.0#233

Open
kaashmonee wants to merge 2 commits intoduckdb:mainfrom
kaashmonee:add-ui-local-host-setting
Open

Add UI setting to bind to 0.0.0.0#233
kaashmonee wants to merge 2 commits intoduckdb:mainfrom
kaashmonee:add-ui-local-host-setting

Conversation

@kaashmonee
Copy link
Copy Markdown

@kaashmonee kaashmonee commented Apr 4, 2026

Summary

  • Add a new ui_local_host DuckDB extension setting (default: localhost) that controls the host/address the UI HTTP server binds to.
  • Enables binding to 0.0.0.0 for remote/network access, e.g. SET ui_local_host = '0.0.0.0'; or UI_LOCAL_HOST=0.0.0.0 duckdb -ui.
  • Follows the same pattern as the existing ui_local_port setting: configurable via SQL, environment variable, or default.

Changes

  • src/include/settings.hpp — Define UI_LOCAL_HOST_SETTING_NAME / UI_LOCAL_HOST_SETTING_DEFAULT macros, declare GetLocalHost().
  • src/settings.cpp — Implement GetLocalHost().
  • src/ui_extension.cpp — Register ui_local_host as an extension option (VARCHAR, env-overridable).
  • src/include/http_server.hpp — Add local_host member, update DoStart signature.
  • src/http_server.cpp — Thread local_host through StartDoStartRun, use it in server.listen(), LocalUrl(), and local_url construction. IsRunningOnMachine() still probes localhost since that works regardless of bind address.
  • test/sql/ui.test — Add SQLLogicTests verifying the setting exists, has the correct default, and can be changed.

Implemented suggestion from #232.

Allow the UI HTTP server to bind to a configurable host instead of
hardcoded localhost. This enables binding to 0.0.0.0 for remote access.

The setting follows the same pattern as ui_local_port: configurable via
SQL (SET ui_local_host), environment variable (UI_LOCAL_HOST), or the
default (localhost).
@kaashmonee kaashmonee changed the title Add UI local host setting Add UI setting to bind to 0.0.0.0 Apr 4, 2026
@kaashmonee
Copy link
Copy Markdown
Author

@jraymakers: requesting review! Thank you 🙏

@jraymakers
Copy link
Copy Markdown
Collaborator

Thanks for the PR! Sorry for the delay. I mean to review this (carefully), but I haven't found the time yet.

Copy link
Copy Markdown
Collaborator

@jraymakers jraymakers left a comment

Choose a reason for hiding this comment

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

Finally got a chance to review this and also test it out. I have a range of comments, from small nits to functional problems that will need to be fixed.

The most important parts are the problem with Set-Cookie and the origin checks. Both of these prevent this from working properly.

Comment thread src/http_server.cpp
#include "settings.hpp"
#include "state.hpp"
#include "utils/encoding.hpp"
#include "utils/token.hpp"
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.

nit: alphabetize

Comment thread src/http_server.cpp
remote_url = _remote_url;
http_params = std::move(_http_params);
token_auth_enabled = _token_auth_enabled;
auth_token = GenerateToken();
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.

We shouldn't generate a token if token auth is disabled, both to avoid the unnecessary work, and also to avoid it showing up in the URL.

Comment thread src/include/settings.hpp
#define UI_POLLING_INTERVAL_SETTING_NAME "ui_polling_interval"
#define UI_POLLING_INTERVAL_SETTING_DEFAULT 284
#define UI_ENABLE_TOKEN_AUTH_SETTING_NAME "ui_enable_token_auth"
#define UI_ENABLE_TOKEN_AUTH_SETTING_DEFAULT true
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 think we should:

  • default to token auth disabled if the local host is "localhost"
  • default to token auth enabled otherwise
  • respect the value of "ui_enable_token_auth" if it is set explicitly.

Comment thread src/http_server.cpp
httplib::Response &res) {
if (!ValidateToken(req, res)) {
return;
}
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.

We don't need (and shouldn't) require the token for these forwarded GET requests. The purpose of the token auth is to protect access to the DuckDB instance, but this function just proxies requests to the Internet, so there's nothing local to protect.

Comment thread src/http_server.cpp

// Check URL parameter: ?token=<TOKEN>
if (req.has_param("token")) {
if (req.get_param_value("token") == auth_token) {
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.

We should name this URL param a bit more uniquely, to prevent it from interacting with other potential URL params. How about "duckdb-ui-token" or "duckdb_ui_token", to match the cookie?

Comment thread src/http_server.cpp
if (origin != local_url) {
res.status = 401;
return;
}
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.

Here and in the other methods below, we have an existing security mechanism to restrict calls by origin - we only allow calls from localhost. This will likely prevent calls from other hosts, even when the new local_host setting is set.

My preference would be to keep these origin checks when the local_host setting is "localhost", but disable them when the local_host setting is something else.

Comment thread src/http_server.cpp
// Referer includes the path, so only compare the start.
auto referer = req.get_header_value("Referer");
if (referer.compare(0, local_url.size(), local_url) != 0) {
res.status = 401;
Copy link
Copy Markdown
Collaborator

@jraymakers jraymakers Apr 11, 2026

Choose a reason for hiding this comment

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

We should keep this check unless the local_host setting is something other than "localhost". (This is a variant of the other origin checks I mentioned.)

Comment thread src/ui_extension.cpp
const auto token = server.GetAuthToken();
if (!token.empty()) {
url += "?token=" + token;
}
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.

To avoid repeating this logic to append the token, we could just put it into HttpServer::LocalUrl.


namespace duckdb {

std::string GenerateToken();
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.

General naming comment: There's an existing use of "token" in the extension - the "localToken" endpoint and the "GetMDToken" helper - for (optional) MotherDuck support. This change adds a second kind of token. To avoid confusion, we should qualify this new token wherever we refer to it, in function names, variables, etc. I recommend "DuckDBUIToken".

Comment thread src/http_server.cpp
}

// Repond with result of forwarded GET
res = result.value();
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 don't see the Set-Cookie header in the response to the initial request, and I suspect this line is why - we overwrite the res object with the result of the forwarded request. We need to add the cookie after this point. We should take care to add it and not clear any other cookies that might be present in the response. (There typically won't be any, even when using MotherDuck, but we want this mechanism to behave nicely with any potential future uses of cookies.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants