diff --git a/README.md b/README.md index 626cef7..16dc64e 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,9 @@ The OPNsense page **Services -> RustProxy** exposes the runtime listeners used b - Admin bind address + port: `0.0.0.0` / `9090` - Allow remote admin: on by default so the RustProxy panel can be reached from other hosts; OPNsense firewall rules still decide which networks can connect -On OPNsense/FreeBSD the service runs as `root` and keeps `/var/db/rustproxy` mode `0700`. This avoids startup/access failures seen after privilege dropping with `daemon -u rustproxy`. Package installation restarts `configd` so `configctl rustproxy ...` actions are immediately available even after direct `pkg add`. +On OPNsense/FreeBSD the service runs as `root` and keeps `/var/db/rustproxy` mode `0700`. This avoids startup/access failures seen after privilege dropping with `daemon -u rustproxy`. Package installation restarts `configd` so `configctl rustproxy ...` actions are immediately available even after direct `pkg add`. When RustProxy is enabled, the package post-install hook also restarts it after `pkg upgrade`/`pkg add -f`, so an upgrade does not leave the service stopped. + +Allocator tuning is platform-native: Linux packages use `MALLOC_ARENA_MAX=2` for glibc, while this FreeBSD/OPNsense service exports `MALLOC_CONF=narenas:2` for jemalloc. Override it with `rustproxy_malloc_conf` in rc.conf, or set the value empty to disable the tuning. ## FreeBSD/OPNsense-only RustProxy patches diff --git a/ci/build-release.sh b/ci/build-release.sh index fabd30f..693629f 100755 --- a/ci/build-release.sh +++ b/ci/build-release.sh @@ -55,6 +55,13 @@ auto_deploy_tests() service configd restart /usr/local/sbin/pluginctl -cq cache_flush >/dev/null 2>&1 || true /usr/local/sbin/configctl template reload OPNsense/RustProxy >/dev/null 2>&1 || true + if [ -r /usr/local/etc/rustproxy/opnsense.conf ] && \ + grep -q "^enabled=\"1\"" /usr/local/etc/rustproxy/opnsense.conf; then + if ! /usr/local/opnsense/scripts/rustproxy/service.sh status >/dev/null 2>&1; then + /usr/local/opnsense/scripts/rustproxy/service.sh start + fi + /usr/local/opnsense/scripts/rustproxy/service.sh status + fi ' done } diff --git a/docs/FIRST_RUN.md b/docs/FIRST_RUN.md index a3debbf..1a41c04 100644 --- a/docs/FIRST_RUN.md +++ b/docs/FIRST_RUN.md @@ -151,7 +151,9 @@ After installation open **Services -> RustProxy**. Defaults are: The RustProxy administration UI is reachable from other hosts by default on port `9090`, subject to OPNsense firewall rules. Use **Open RustProxy Admin** after Apply to open the panel in a new browser tab. If the admin bind address is `0.0.0.0`, the button uses the hostname or IP address through which you opened OPNsense. -Direct `pkg add` is supported: the package post-install script restarts OPNsense `configd`, flushes the plugin cache and renders `/usr/local/etc/rustproxy/opnsense.conf` so `configctl rustproxy status/start/stop` is available immediately. +Direct `pkg add` is supported: the package post-install script restarts OPNsense `configd`, flushes the plugin cache and renders `/usr/local/etc/rustproxy/opnsense.conf` so `configctl rustproxy status/start/stop` is available immediately. If **Enabled** is set, the post-install hook also restarts RustProxy automatically after `pkg upgrade` or `pkg add -f`. + +FreeBSD uses jemalloc, so the OPNsense rc.d service sets `MALLOC_CONF=narenas:2`. This is the FreeBSD equivalent of the low-arena intent behind Linux `MALLOC_ARENA_MAX=2`; the glibc variable is intentionally not exported on OPNsense. Set `rustproxy_malloc_conf` in rc.conf to override or disable it. RustProxy requires its data directory to be private. On OPNsense/FreeBSD the plugin runs the service as `root` and enforces owner `root:wheel` with mode `0700` on `/var/db/rustproxy` before every start. diff --git a/opnsense-plugin/www/rustproxy/+POST_INSTALL.pre b/opnsense-plugin/www/rustproxy/+POST_INSTALL.pre index 36e6097..d55fd8c 100755 --- a/opnsense-plugin/www/rustproxy/+POST_INSTALL.pre +++ b/opnsense-plugin/www/rustproxy/+POST_INSTALL.pre @@ -30,4 +30,18 @@ if [ -x /usr/local/opnsense/mvc/script/run_migrations.php ]; then fi /usr/local/sbin/configctl template reload OPNsense/RustProxy >/dev/null 2>&1 || true +# A package upgrade invokes the previous package pre-deinstall hook, which stops +# RustProxy before replacing the binary. Start it again after migrations and +# template rendering. Use the installed service helper directly so this does +# not depend on configd having finished reloading its action registry. The +# helper itself checks enabled=1; on a fresh/default-disabled install this is a +# no-op. restart is deliberate: if an old process survived a forced pkg add, +# it is stopped before the new binary starts. +if [ -x /usr/local/opnsense/scripts/rustproxy/service.sh ]; then + if ! /usr/local/opnsense/scripts/rustproxy/service.sh restart; then + echo "WARNING: RustProxy package installed but the service could not be started." >&2 + echo "Check /var/log/rustproxy/rustproxy.log and run: configctl rustproxy start" >&2 + fi +fi + exit 0 diff --git a/opnsense-plugin/www/rustproxy/+PRE_DEINSTALL.pre b/opnsense-plugin/www/rustproxy/+PRE_DEINSTALL.pre old mode 100644 new mode 100755 index f41e117..0b70e65 --- a/opnsense-plugin/www/rustproxy/+PRE_DEINSTALL.pre +++ b/opnsense-plugin/www/rustproxy/+PRE_DEINSTALL.pre @@ -1,4 +1,13 @@ #!/bin/sh -/usr/local/sbin/configctl rustproxy stop >/dev/null 2>&1 || true +# pkg upgrade runs the old package pre-deinstall hook before installing the new +# binary. Stop the supervised child first so the upgrade never leaves an old +# RustProxy process running from an unlinked executable. configctl is preferred +# on OPNsense; the rc.d fallback also works while configd is restarting. +if ! /usr/local/sbin/configctl rustproxy stop >/dev/null 2>&1; then + if [ -x /usr/local/etc/rc.d/rustproxy ]; then + /usr/local/etc/rc.d/rustproxy onestop >/dev/null 2>&1 || true + fi +fi + exit 0 diff --git a/opnsense-plugin/www/rustproxy/src/etc/rc.d/rustproxy b/opnsense-plugin/www/rustproxy/src/etc/rc.d/rustproxy index 0f651b4..3338e84 100755 --- a/opnsense-plugin/www/rustproxy/src/etc/rc.d/rustproxy +++ b/opnsense-plugin/www/rustproxy/src/etc/rc.d/rustproxy @@ -24,6 +24,12 @@ load_rc_config "$name" : ${rustproxy_command:="/usr/local/sbin/rust-proxy"} : ${rustproxy_restart_delay:="5"} : ${rustproxy_allow_remote_admin:="1"} +# FreeBSD uses jemalloc rather than glibc. Keep the same low-arena intent as +# Linux MALLOC_ARENA_MAX=2 with the native jemalloc knob. rc.conf can override +# rustproxy_malloc_conf or set it to an empty value to disable the tuning. +if [ "${rustproxy_malloc_conf+x}" != "x" ]; then + rustproxy_malloc_conf="${MALLOC_CONF:-narenas:2}" +fi # Split listener settings written by the current OPNsense model. : ${rustproxy_proxy_address:=""} @@ -166,6 +172,16 @@ status_cmd="rustproxy_status" rustproxy_prestart() { + # RustProxy on OPNsense/FreeBSD uses jemalloc. MALLOC_ARENA_MAX is a + # glibc/Linux setting and has no useful effect here, so export MALLOC_CONF + # instead. daemon(8) inherits this environment and passes it to RustProxy. + if [ -n "${rustproxy_malloc_conf}" ]; then + MALLOC_CONF="${rustproxy_malloc_conf}" + export MALLOC_CONF + else + unset MALLOC_CONF + fi + # /var/run is ephemeral and recreated on boot. /usr/bin/install -d -o "${rustproxy_user}" -g "${rustproxy_group}" -m 0750 "${rustproxy_run_dir}" /usr/sbin/chown "${rustproxy_user}:${rustproxy_group}" "${rustproxy_run_dir}" @@ -181,6 +197,9 @@ rustproxy_prestart() /bin/chmod 0640 "${rustproxy_log_file}" echo "RustProxy listeners: HTTP=${rustproxy_proxy_listen_resolved} TLS=${rustproxy_tls_listen_resolved} ADMIN=${rustproxy_admin_listen_resolved}" + if [ -n "${rustproxy_malloc_conf}" ]; then + echo "RustProxy allocator: MALLOC_CONF=${rustproxy_malloc_conf}" + fi echo "RustProxy exec: ${rustproxy_command} ${rustproxy_runtime_args}" printf '%s\n' "${rustproxy_command} ${rustproxy_runtime_args}" > "${rustproxy_run_dir}/command.txt" /bin/chmod 0640 "${rustproxy_run_dir}/command.txt" diff --git a/pkg/os-rustproxy-latest.pkg b/pkg/os-rustproxy-latest.pkg deleted file mode 100644 index d17cb11..0000000 Binary files a/pkg/os-rustproxy-latest.pkg and /dev/null differ diff --git a/scripts/check.sh b/scripts/check.sh index 4030d7e..1508efd 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -56,6 +56,7 @@ rc_script="$ROOT/opnsense-plugin/www/rustproxy/src/etc/rc.d/rustproxy" model="$ROOT/opnsense-plugin/www/rustproxy/src/opnsense/mvc/app/models/OPNsense/RustProxy/RustProxy.xml" template="$ROOT/opnsense-plugin/www/rustproxy/src/opnsense/service/templates/OPNsense/RustProxy/rustproxy.conf" post_install="$ROOT/opnsense-plugin/www/rustproxy/+POST_INSTALL.pre" +pre_deinstall="$ROOT/opnsense-plugin/www/rustproxy/+PRE_DEINSTALL.pre" grep -q 'rustproxy_run_dir:="/var/run/rustproxy"' "$rc_script" grep -q 'rustproxy_user:="root"' "$rc_script" @@ -81,6 +82,15 @@ grep -q 'rustproxy_tls_port=' "$template" grep -q 'rustproxy_tls_listen_legacy=' "$template" grep -q 'root -g wheel' "$post_install" grep -q '/var/run/rustproxy' "$post_install" +grep -q 'rustproxy_malloc_conf="${MALLOC_CONF:-narenas:2}"' "$rc_script" +grep -q 'export MALLOC_CONF' "$rc_script" +if grep -q 'MALLOC_ARENA_MAX' "$rc_script"; then + # It may only appear in the explanatory FreeBSD comment, never as an export. + ! grep -Eq '(^|[[:space:]])(export[[:space:]]+)?MALLOC_ARENA_MAX=' "$rc_script" +fi +grep -q 'service.sh restart' "$post_install" +grep -q 'onestop' "$pre_deinstall" +grep -q 'service.sh status' "$ROOT/ci/build-release.sh" # The generated command must never contain a literal listener ending in a colon # from model defaults/templates. rc.d rejects/falls back from malformed legacy