fixes
This commit is contained in:
1 parent
1a2a5f0faf
commit
c0068d624e
13 files changed
+230
-17
No files matched your search
@@ -69,9 +69,11 @@ dist/
|
||||
|
||||
This version intentionally produces an installable `.pkg`, not a full `pkg repo` catalogue. Installation/update is currently done with `pkg add -f`.
|
||||
|
||||
The Linux-side package builder validates the FreeBSD package payload before publishing it: payload paths must be absolute (`/usr/local/...`), no undeclared directory entries are archived, and every payload file must have an identical entry in `+MANIFEST`.
|
||||
|
||||
## OPNsense UI
|
||||
|
||||
The first version exposes only **Services -> RustProxy -> Enabled**. RustProxy runs as the unprivileged `rustproxy` user.
|
||||
The OPNsense page **Services -> RustProxy** exposes enable/disable plus HTTP, HTTPS/TLS and administration listener settings. RustProxy runs as the unprivileged `rustproxy` user.
|
||||
|
||||
## Validation
|
||||
|
||||
@@ -80,3 +82,14 @@ The first version exposes only **Services -> RustProxy -> Enabled**. RustProxy r
|
||||
```
|
||||
|
||||
See `docs/FIRST_RUN.md` for Debian setup.
|
||||
|
||||
## OPNsense listener settings
|
||||
|
||||
The OPNsense page **Services -> RustProxy** exposes the runtime listeners used by the rc.d service:
|
||||
|
||||
- HTTP listen: `0.0.0.0:8080`
|
||||
- HTTPS/TLS listen: `0.0.0.0:8181` (toggleable)
|
||||
- Admin listen: `127.0.0.1:9090`
|
||||
- Allow remote admin: off by default; enable it when binding admin to a non-loopback address such as `0.0.0.0:9090`
|
||||
|
||||
The service runs as the unprivileged `rustproxy` user and keeps `/var/db/rustproxy` mode `0700`, as required by RustProxy. Package installation restarts `configd` so `configctl rustproxy ...` actions are immediately available even after direct `pkg add`.
|
||||
@@ -41,7 +41,7 @@ if [[ "$RUN_LINUX_TESTS" == "1" ]]; then
|
||||
fi
|
||||
|
||||
echo "Cross-compiling RustProxy for x86_64-unknown-freebsd..."
|
||||
docker run "${run_args[@]}" "$DOCKER_IMAGE" bash -lc '
|
||||
docker run "${run_args[@]}" "$DOCKER_IMAGE" bash -c '
|
||||
set -euo pipefail
|
||||
cargo build --locked --release --target x86_64-unknown-freebsd
|
||||
install -m 0555 /target/x86_64-unknown-freebsd/release/rust-proxy /out/rust-proxy
|
||||
|
||||
@@ -89,7 +89,7 @@ common = {
|
||||
"version": version,
|
||||
"origin": "opnsense/os-rustproxy",
|
||||
"comment": "RustProxy reverse proxy service",
|
||||
"desc": "RustProxy packaged as an OPNsense plugin. The initial UI exposes Enabled/Disabled only.",
|
||||
"desc": "RustProxy packaged as an OPNsense plugin with configurable HTTP, HTTPS/TLS and administration listeners.",
|
||||
"maintainer": "local@localhost",
|
||||
"www": "https://git.linuxiarz.pl/gru/RustProxy",
|
||||
"prefix": "/usr/local",
|
||||
@@ -120,11 +120,67 @@ manifest["scripts"] = {
|
||||
PY
|
||||
|
||||
archive="$work/os-rustproxy.tar"
|
||||
rm -f "$archive" "$package_file"
|
||||
payload_list="$work/payload.list"
|
||||
rm -f "$archive" "$package_file" "$payload_list"
|
||||
|
||||
# FreeBSD pkg expects payload paths to match +MANIFEST exactly. The manifest
|
||||
# uses absolute paths (/usr/local/...), so the tar members must also be
|
||||
# absolute. Do not archive directory entries: pkg creates parent directories
|
||||
# while extracting files, and an undeclared directory entry makes pkg add fail.
|
||||
(
|
||||
cd "$stage"
|
||||
find . \( -type f -o -type l \) -print \
|
||||
| LC_ALL=C sort \
|
||||
| sed 's#^\./##' > "$payload_list"
|
||||
)
|
||||
|
||||
[[ -s "$payload_list" ]] || { echo "Package payload is empty" >&2; exit 1; }
|
||||
|
||||
tar --format=ustar --owner=0 --group=0 --numeric-owner \
|
||||
-cf "$archive" -C "$meta" +COMPACT_MANIFEST +MANIFEST
|
||||
tar --format=ustar --owner=0 --group=0 --numeric-owner \
|
||||
-rf "$archive" -C "$stage" usr
|
||||
tar -P --format=ustar --owner=0 --group=0 --numeric-owner --no-recursion \
|
||||
--transform='s,^,/,S' \
|
||||
-rf "$archive" -C "$stage" -T "$payload_list"
|
||||
|
||||
# Validate the raw package archive before compression. This catches the exact
|
||||
# class of errors that pkg(8) reports as "not specified in the manifest".
|
||||
python3 - "$archive" "$meta/+MANIFEST" <<'PYVERIFY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tarfile
|
||||
|
||||
archive = Path(sys.argv[1])
|
||||
manifest = json.loads(Path(sys.argv[2]).read_text(encoding="utf-8"))
|
||||
expected = set(manifest.get("files", {}))
|
||||
|
||||
with tarfile.open(archive, "r:") as tf:
|
||||
members = tf.getmembers()
|
||||
|
||||
if [m.name for m in members[:2]] != ["+COMPACT_MANIFEST", "+MANIFEST"]:
|
||||
raise SystemExit("Package metadata must be the first two tar members")
|
||||
|
||||
payload = members[2:]
|
||||
directories = [m.name for m in payload if m.isdir()]
|
||||
if directories:
|
||||
raise SystemExit(f"Directory entries are not allowed in payload: {directories[:5]}")
|
||||
|
||||
actual = {m.name for m in payload if m.isfile() or m.issym() or m.islnk()}
|
||||
relative = sorted(name for name in actual if not name.startswith("/"))
|
||||
if relative:
|
||||
raise SystemExit(f"Payload contains non-absolute paths: {relative[:5]}")
|
||||
|
||||
missing = sorted(expected - actual)
|
||||
extra = sorted(actual - expected)
|
||||
if missing or extra:
|
||||
raise SystemExit(
|
||||
"Package payload does not match +MANIFEST; "
|
||||
f"missing={missing[:5]} extra={extra[:5]}"
|
||||
)
|
||||
|
||||
print(f"Verified package payload: {len(actual)} files, manifest paths match")
|
||||
PYVERIFY
|
||||
|
||||
zstd -q -f -19 -T0 "$archive" -o "$package_file"
|
||||
|
||||
# Convenience stable path for direct deployment.
|
||||
|
||||
@@ -5,7 +5,7 @@ FROM ubuntu:24.04 AS sysroot
|
||||
ARG FREEBSD_RELEASE=15.1-RELEASE
|
||||
ARG FREEBSD_MIRROR=https://download.freebsd.org/releases/amd64/amd64
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
ca-certificates wget xz-utils \
|
||||
ca-certificates wget xz-utils \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /tmp/freebsd
|
||||
RUN set -eux; \
|
||||
@@ -21,11 +21,16 @@ RUN set -eux; \
|
||||
# RustProxy requires rust-version = 1.88.
|
||||
FROM rust:1.88-slim-bookworm AS builder
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
ca-certificates clang lld llvm file git perl pkg-config python3 \
|
||||
ca-certificates clang lld llvm file git perl pkg-config python3 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=sysroot /freebsd-sysroot /opt/freebsd-sysroot
|
||||
RUN rustup target add x86_64-unknown-freebsd
|
||||
|
||||
ENV RUSTUP_TOOLCHAIN=1.88.0
|
||||
|
||||
RUN rustup target add \
|
||||
--toolchain 1.88.0 \
|
||||
x86_64-unknown-freebsd
|
||||
|
||||
ENV CARGO_TARGET_X86_64_UNKNOWN_FREEBSD_LINKER=clang \
|
||||
CC_x86_64_unknown_freebsd=clang \
|
||||
|
||||
@@ -4,8 +4,11 @@ set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CONFIG_FILE="${RUSTPROXY_BUILDER_CONFIG:-/etc/rustproxy-opnsense-builder.env}"
|
||||
if [[ -r "$CONFIG_FILE" ]]; then
|
||||
# Export all builder configuration to child build scripts.
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$CONFIG_FILE"
|
||||
set +a
|
||||
fi
|
||||
|
||||
: "${BUILD_MODE:=docker-freebsd}"
|
||||
|
||||
@@ -87,6 +87,8 @@ file dist/docker-build/rust-proxy
|
||||
zstd -dc dist/os-rustproxy-latest.pkg | tar -tf - | head -50
|
||||
```
|
||||
|
||||
The package payload should contain files such as `/usr/local/sbin/rust-proxy` and must not contain directory entries such as `/usr/` or `/usr/local/`. The build fails automatically when payload paths differ from `+MANIFEST`.
|
||||
|
||||
The binary should identify as an x86-64 FreeBSD ELF executable, not a Linux executable.
|
||||
|
||||
## 7. Install on a test OPNsense
|
||||
@@ -136,3 +138,19 @@ AUTO_DEPLOY_SSH_KEY=/root/.ssh/opnsense_test
|
||||
```
|
||||
|
||||
Leave both empty for manual deployment.
|
||||
|
||||
## OPNsense runtime settings
|
||||
|
||||
After installation open **Services -> RustProxy**. Defaults are:
|
||||
|
||||
- HTTP listen: `0.0.0.0:8080`
|
||||
- HTTPS/TLS enabled: yes
|
||||
- HTTPS/TLS listen: `0.0.0.0:8181`
|
||||
- Admin listen: `127.0.0.1:9090`
|
||||
- Allow remote admin: no
|
||||
|
||||
If you want the RustProxy administration UI reachable from another host, set the admin listener to an address such as `0.0.0.0:9090` and enable **Allow remote admin**. Apply restarts/reloads the service with the corresponding RustProxy CLI flags.
|
||||
|
||||
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.
|
||||
|
||||
RustProxy requires its data directory to be private. The plugin enforces owner `rustproxy:rustproxy` and mode `0700` on `/var/db/rustproxy` before every start.
|
||||
Regular → Executable
+11
-1
@@ -8,6 +8,16 @@ if ! /usr/sbin/pw usershow rustproxy >/dev/null 2>&1; then
|
||||
/usr/sbin/pw useradd rustproxy -g rustproxy -c "RustProxy daemon" -d /var/db/rustproxy -s /usr/sbin/nologin
|
||||
fi
|
||||
|
||||
/usr/bin/install -d -o rustproxy -g rustproxy -m 0750 /var/db/rustproxy
|
||||
/usr/bin/install -d -o rustproxy -g rustproxy -m 0700 /var/db/rustproxy
|
||||
/usr/sbin/chown rustproxy:rustproxy /var/db/rustproxy
|
||||
/bin/chmod 0700 /var/db/rustproxy
|
||||
/usr/bin/install -d -o rustproxy -g rustproxy -m 0750 /var/log/rustproxy
|
||||
/usr/bin/install -d -o root -g wheel -m 0755 /usr/local/etc/rustproxy
|
||||
|
||||
# configd discovers actions only on startup/reload. A direct `pkg add` must
|
||||
# therefore refresh configd before the MVC service controller can call them.
|
||||
/usr/sbin/service configd restart >/dev/null 2>&1 || true
|
||||
/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
|
||||
|
||||
exit 0
|
||||
@@ -1,4 +1,4 @@
|
||||
RustProxy packaged as an OPNsense plugin.
|
||||
RustProxy packaged as an OPNsense service plugin.
|
||||
|
||||
The initial OPNsense UI intentionally exposes only one setting: Enabled.
|
||||
Runtime proxy configuration remains managed by RustProxy itself.
|
||||
The OPNsense UI exposes service enable/disable, HTTP and HTTPS/TLS listener
|
||||
addresses, the administration listener and the remote-admin opt-in.
|
||||
@@ -19,18 +19,46 @@ load_rc_config "$name"
|
||||
: ${rustproxy_log_file:="${rustproxy_log_dir}/rustproxy.log"}
|
||||
: ${rustproxy_command:="/usr/local/sbin/rust-proxy"}
|
||||
: ${rustproxy_restart_delay:="5"}
|
||||
: ${rustproxy_proxy_listen:="0.0.0.0:8080"}
|
||||
: ${rustproxy_tls_enabled:="1"}
|
||||
: ${rustproxy_tls_listen:="0.0.0.0:8181"}
|
||||
: ${rustproxy_admin_listen:="127.0.0.1:9090"}
|
||||
: ${rustproxy_allow_remote_admin:="0"}
|
||||
|
||||
enabled="0"
|
||||
config_file="/usr/local/etc/rustproxy/opnsense.conf"
|
||||
if [ -r "$config_file" ]; then
|
||||
. "$config_file"
|
||||
fi
|
||||
|
||||
if [ "$enabled" = "1" ]; then
|
||||
rustproxy_enable="YES"
|
||||
else
|
||||
rustproxy_enable="NO"
|
||||
fi
|
||||
|
||||
rustproxy_runtime_args="--data-dir ${rustproxy_data_dir} --admin-listen ${rustproxy_admin_listen} --update-check-interval-secs 0 serve --proxy-listen ${rustproxy_proxy_listen}"
|
||||
if [ "$rustproxy_tls_enabled" = "1" ]; then
|
||||
rustproxy_runtime_args="${rustproxy_runtime_args} --tls-listen ${rustproxy_tls_listen}"
|
||||
fi
|
||||
if [ "$rustproxy_allow_remote_admin" = "1" ]; then
|
||||
rustproxy_runtime_args="${rustproxy_runtime_args} --allow-remote-admin"
|
||||
fi
|
||||
|
||||
pidfile="/var/run/rustproxy-daemon.pid"
|
||||
child_pidfile="/var/run/rustproxy.pid"
|
||||
command="/usr/sbin/daemon"
|
||||
command_args="-r -R ${rustproxy_restart_delay} -P ${pidfile} -p ${child_pidfile} -u ${rustproxy_user} -o ${rustproxy_log_file} ${rustproxy_command} --data-dir ${rustproxy_data_dir} --update-check-interval-secs 0 serve"
|
||||
command_args="-r -R ${rustproxy_restart_delay} -P ${pidfile} -p ${child_pidfile} -u ${rustproxy_user} -o ${rustproxy_log_file} ${rustproxy_command} ${rustproxy_runtime_args}"
|
||||
required_files="${rustproxy_command}"
|
||||
start_precmd="rustproxy_prestart"
|
||||
stop_postcmd="rustproxy_poststop"
|
||||
|
||||
rustproxy_prestart()
|
||||
{
|
||||
/usr/bin/install -d -o "${rustproxy_user}" -g "${rustproxy_group}" -m 0750 "${rustproxy_data_dir}"
|
||||
/usr/bin/install -d -o "${rustproxy_user}" -g "${rustproxy_group}" -m 0700 "${rustproxy_data_dir}"
|
||||
/usr/sbin/chown "${rustproxy_user}:${rustproxy_group}" "${rustproxy_data_dir}"
|
||||
/bin/chmod 0700 "${rustproxy_data_dir}"
|
||||
|
||||
/usr/bin/install -d -o "${rustproxy_user}" -g "${rustproxy_group}" -m 0750 "${rustproxy_log_dir}"
|
||||
/usr/bin/touch "${rustproxy_log_file}"
|
||||
/usr/sbin/chown "${rustproxy_user}:${rustproxy_group}" "${rustproxy_log_file}"
|
||||
|
||||
+45
@@ -1,8 +1,53 @@
|
||||
<form>
|
||||
<field>
|
||||
<label>General</label>
|
||||
<type>header</type>
|
||||
<static>true</static>
|
||||
</field>
|
||||
<field>
|
||||
<id>rustproxy.general.enabled</id>
|
||||
<label>Enabled</label>
|
||||
<type>checkbox</type>
|
||||
<help>Start RustProxy and keep it enabled after OPNsense restarts.</help>
|
||||
</field>
|
||||
<field>
|
||||
<label>Proxy listeners</label>
|
||||
<type>header</type>
|
||||
<static>true</static>
|
||||
</field>
|
||||
<field>
|
||||
<id>rustproxy.general.proxyListen</id>
|
||||
<label>HTTP listen</label>
|
||||
<type>text</type>
|
||||
<help>HTTP listener passed as --proxy-listen. Default: 0.0.0.0:8080.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rustproxy.general.tlsEnabled</id>
|
||||
<label>Enable HTTPS/TLS listener</label>
|
||||
<type>checkbox</type>
|
||||
<help>When enabled, pass --tls-listen to RustProxy.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rustproxy.general.tlsListen</id>
|
||||
<label>HTTPS/TLS listen</label>
|
||||
<type>text</type>
|
||||
<help>HTTPS/TLS listener passed as --tls-listen. Default: 0.0.0.0:8181.</help>
|
||||
</field>
|
||||
<field>
|
||||
<label>Administration</label>
|
||||
<type>header</type>
|
||||
<static>true</static>
|
||||
</field>
|
||||
<field>
|
||||
<id>rustproxy.general.adminListen</id>
|
||||
<label>Admin listen</label>
|
||||
<type>text</type>
|
||||
<help>RustProxy administration listener passed as --admin-listen. Default: 127.0.0.1:9090.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rustproxy.general.allowRemoteAdmin</id>
|
||||
<label>Allow remote admin</label>
|
||||
<type>checkbox</type>
|
||||
<help>Required when Admin listen is not a loopback address, for example 0.0.0.0:9090. This passes --allow-remote-admin.</help>
|
||||
</field>
|
||||
</form>
|
||||
+25
-2
@@ -1,13 +1,36 @@
|
||||
<model>
|
||||
<mount>//OPNsense/rustproxy</mount>
|
||||
<version>1.0.0</version>
|
||||
<version>1.1.0</version>
|
||||
<description>RustProxy service settings</description>
|
||||
<items>
|
||||
<general>
|
||||
<enabled type="BooleanField">
|
||||
<default>0</default>
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</enabled>
|
||||
<proxyListen type="IPPortField">
|
||||
<Default>0.0.0.0:8080</Default>
|
||||
<Required>Y</Required>
|
||||
<ValidationMessage>Enter a valid HTTP listen address and port, for example 0.0.0.0:8080.</ValidationMessage>
|
||||
</proxyListen>
|
||||
<tlsEnabled type="BooleanField">
|
||||
<Default>1</Default>
|
||||
<Required>Y</Required>
|
||||
</tlsEnabled>
|
||||
<tlsListen type="IPPortField">
|
||||
<Default>0.0.0.0:8181</Default>
|
||||
<Required>Y</Required>
|
||||
<ValidationMessage>Enter a valid HTTPS/TLS listen address and port, for example 0.0.0.0:8181.</ValidationMessage>
|
||||
</tlsListen>
|
||||
<adminListen type="IPPortField">
|
||||
<Default>127.0.0.1:9090</Default>
|
||||
<Required>Y</Required>
|
||||
<ValidationMessage>Enter a valid administration listen address and port, for example 127.0.0.1:9090.</ValidationMessage>
|
||||
</adminListen>
|
||||
<allowRemoteAdmin type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</allowRemoteAdmin>
|
||||
</general>
|
||||
</items>
|
||||
</model>
|
||||
+7
@@ -3,33 +3,40 @@ command:/usr/local/opnsense/scripts/rustproxy/service.sh start
|
||||
parameters:
|
||||
type:script
|
||||
message:starting RustProxy
|
||||
allowed_groups:wheel,wwwonly
|
||||
|
||||
[stop]
|
||||
command:/usr/local/opnsense/scripts/rustproxy/service.sh stop
|
||||
parameters:
|
||||
type:script
|
||||
message:stopping RustProxy
|
||||
allowed_groups:wheel,wwwonly
|
||||
|
||||
[restart]
|
||||
command:/usr/local/opnsense/scripts/rustproxy/service.sh restart
|
||||
parameters:
|
||||
type:script
|
||||
message:restarting RustProxy
|
||||
allowed_groups:wheel,wwwonly
|
||||
|
||||
[reload]
|
||||
command:/usr/local/opnsense/scripts/rustproxy/service.sh restart
|
||||
parameters:
|
||||
type:script
|
||||
message:reloading RustProxy
|
||||
allowed_groups:wheel,wwwonly
|
||||
|
||||
[status]
|
||||
command:/usr/local/opnsense/scripts/rustproxy/service.sh status
|
||||
parameters:
|
||||
type:script_output
|
||||
errors:no
|
||||
message:RustProxy status
|
||||
allowed_groups:wheel,wwwonly
|
||||
|
||||
[boot]
|
||||
command:/usr/local/opnsense/scripts/rustproxy/service.sh boot
|
||||
parameters:
|
||||
type:script
|
||||
message:starting RustProxy at boot
|
||||
allowed_groups:wheel,wwwonly
|
||||
+5
@@ -1 +1,6 @@
|
||||
enabled="{% if not helpers.empty('OPNsense.rustproxy.general.enabled') %}1{% else %}0{% endif %}"
|
||||
rustproxy_proxy_listen="{% if not helpers.empty('OPNsense.rustproxy.general.proxyListen') %}{{ OPNsense.rustproxy.general.proxyListen }}{% else %}0.0.0.0:8080{% endif %}"
|
||||
rustproxy_tls_enabled="{% if not helpers.empty('OPNsense.rustproxy.general.tlsEnabled') %}1{% else %}0{% endif %}"
|
||||
rustproxy_tls_listen="{% if not helpers.empty('OPNsense.rustproxy.general.tlsListen') %}{{ OPNsense.rustproxy.general.tlsListen }}{% else %}0.0.0.0:8181{% endif %}"
|
||||
rustproxy_admin_listen="{% if not helpers.empty('OPNsense.rustproxy.general.adminListen') %}{{ OPNsense.rustproxy.general.adminListen }}{% else %}127.0.0.1:9090{% endif %}"
|
||||
rustproxy_allow_remote_admin="{% if not helpers.empty('OPNsense.rustproxy.general.allowRemoteAdmin') %}1{% else %}0{% endif %}"
|
||||
Reference in new issue
Block a user