Conversation
There was a problem hiding this comment.
Pull request overview
Adds a configurable web bind address so deployments can restrict which network interface the (unencrypted) HTTP server listens on, reducing accidental exposure on multi-homed hosts or behind proxies.
Changes:
- Introduces
web_bind_ipconfiguration across INI/env/defaults, API, and server startup (libuv bind). - Exposes the setting in the web UI (settings form + i18n) and includes it in config export/backup JSON.
- Documents
bind_ipin configuration docs and wires it into the Docker entrypoint default config.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| web/public/locales/en.json | Adds UI label for the new bind address setting. |
| web/js/utils/settings-utils.js | Adds default web_bind_ip value for frontend settings model. |
| web/js/components/preact/SettingsView.jsx | Adds bind address field to settings load/save + renders an input. |
| src/web/libuv_server.c | Binds the listener to the configured IPv4 address and improves logging. |
| src/web/api_handlers_system.c | Includes web_bind_ip in system backup JSON. |
| src/web/api_handlers_settings.c | Exposes web_bind_ip in GET settings and accepts it in POST updates. |
| src/web/api_handlers_common_utils.c | Includes web_bind_ip in generic config JSON output. |
| src/core/main.c | Passes bind address into HTTP server config and updates log messages. |
| src/core/config.c | Adds env mapping/default/INI/save/print/reload logging for web_bind_ip. |
| include/web/http_server.h | Extends http_server_config_t with bind_ip. |
| include/core/config.h | Adds web_bind_ip to the core config struct. |
| docs/CONFIGURATION.md | Documents the new [web] bind_ip setting. |
| docker-entrypoint.sh | Adds bind_ip to the generated default config in container entrypoint. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } else if (strcmp(name, "bind_ip") == 0) { | ||
| strncpy(config->web_bind_ip, value, 31); | ||
| } else if (strcmp(name, "root") == 0) { |
There was a problem hiding this comment.
strncpy(config->web_bind_ip, value, 31); does not guarantee NUL-termination when value is length >= 31. This can leave web_bind_ip unterminated and later %s logging / uv_ip4_addr reads past the buffer. Ensure the destination is always NUL-terminated (or use a bounded copy helper that always terminates).
| // Web server settings | ||
| {"WEB_PORT", CONFIG_TYPE_INT, CONFIG_OFFSET(web_port), 0, NULL, 8080, false}, | ||
| {"WEB_BIND_IP", CONFIG_TYPE_STRING, CONFIG_OFFSET(web_bind_ip), 32, "0.0.0.0", 0, false}, | ||
| {"WEB_AUTH_ENABLED", CONFIG_TYPE_BOOL, CONFIG_OFFSET(web_auth_enabled), 0, NULL, 0, true}, |
There was a problem hiding this comment.
Config loading already has unit tests (e.g., tests/unit/test_config.c covers web_port defaults/env overrides). This PR adds a new config field (WEB_BIND_IP, default 0.0.0.0) but no corresponding tests to prevent regressions in defaults, INI parsing, and env override behavior. Please add unit coverage for the new field similar to the existing web_port tests.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Web bind address | ||
| cJSON *web_bind_ip = cJSON_GetObjectItem(settings, "web_bind_ip"); | ||
| if (web_bind_ip && cJSON_IsString(web_bind_ip)) { | ||
| const char *new_bind_ip = web_bind_ip->valuestring; | ||
| bool bind_ip_empty = (new_bind_ip == NULL); | ||
|
|
||
| if (!bind_ip_empty) { | ||
| while (isspace((unsigned char)*new_bind_ip)) { | ||
| new_bind_ip++; | ||
| } | ||
| bind_ip_empty = (*new_bind_ip == '\0'); | ||
| } | ||
|
|
||
| if (bind_ip_empty) { | ||
| log_warn("Rejected empty web_bind_ip update"); | ||
| } else if (strcmp(g_config.web_bind_ip, new_bind_ip) != 0) { | ||
| strncpy(g_config.web_bind_ip, new_bind_ip, sizeof(g_config.web_bind_ip) - 1); | ||
| g_config.web_bind_ip[sizeof(g_config.web_bind_ip) - 1] = '\0'; | ||
| settings_changed = true; | ||
| restart_required = true; | ||
| log_info("Updated web_bind_ip: %s (restart required)", g_config.web_bind_ip); | ||
| } |
There was a problem hiding this comment.
web_bind_ip updates are only checked for non-empty, then persisted and logged. Since this value is later written into the INI via fprintf("bind_ip = %s\n", ...) and parsed by uv_ip4_addr, allowing whitespace/control characters or non-IPv4 strings can corrupt the saved config (newline injection) or make the web server fail to start on next restart. Consider trimming both leading/trailing whitespace and validating the value as an IPv4 address (or explicitly document/implement IPv6/hostnames) before accepting it.
| // Web port | ||
| cJSON *web_port = cJSON_GetObjectItem(settings, "web_port"); | ||
| if (web_port && cJSON_IsNumber(web_port)) { | ||
| g_config.web_port = web_port->valueint; | ||
| settings_changed = true; | ||
| log_info("Updated web_port: %d", g_config.web_port); | ||
| } | ||
|
|
||
| // Web bind address | ||
| cJSON *web_bind_ip = cJSON_GetObjectItem(settings, "web_bind_ip"); |
There was a problem hiding this comment.
web_port is updated and persisted, but restart_required is not set (unlike web_bind_ip and web_thread_pool_size). Since the running libuv server won’t rebind to a new port without a restart, the settings API response/UI will incorrectly imply the change is live. Set restart_required = true (and ideally return an appropriate restart_required_message) when web_port changes.
| // Initialize HTTP server (libuv + llhttp) | ||
| log_info("Initializing web server on port %d (daemon_mode: %s)", | ||
| config.web_port, daemon_mode ? "true" : "false"); | ||
| log_info("Initializing web server on %s:%d (daemon_mode: %s)", | ||
| config.web_bind_ip, config.web_port, daemon_mode ? "true" : "false"); |
There was a problem hiding this comment.
Daemon-mode port verification still attempts to connect to INADDR_LOOPBACK regardless of the configured bind address. With web_bind_ip now configurable, binding to a specific non-loopback interface (e.g., 192.168.x.x) will cause this verification to fail and log misleading warnings. Update the verification logic to connect to the configured bind address (or treat 0.0.0.0 as a special case and verify via loopback).
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Useful for deployments with many network interfaces or behind a proxy, to avoid exposing the unencrypted HTTP port.