diff --git a/packaging/common/config.json b/packaging/common/config.json new file mode 100644 index 0000000..27fe991 --- /dev/null +++ b/packaging/common/config.json @@ -0,0 +1,27 @@ +{ + "_comment": [ + "webui4frpc startup config. Every field is optional; the effective", + "precedence is: command-line flag > this file > built-in default.", + "", + "Password handling (pick ONE, do not put a literal password here):", + " \"password\": \"file:/etc/webui4frpc/secret/password\" first line of a 0640 root:webui4frpc file", + " \"password\": \"${W4F_PASSWORD}\" from the environment (unset => refuses to start)", + "", + "Unknown keys are a hard error, so a typo cannot silently fall back to the", + "built-in default credentials. The file may be absent (pure-flag mode)." + ], + "addr": "0.0.0.0:7500", + "user": "admin", + "password": "file:/etc/webui4frpc/secret/password", + "workdir": "/var/lib/webui4frpc", + + "_optional": [ + "bin - initial frpc binary path, seeded into settings on first start", + "frpc - fallback worker frpc path when settings has none", + "peers - cluster peer host:port list (same as repeating -peer)", + "join_key- cluster admission key = sponsor node's nodeKey (or ${W4F_JOIN_KEY})" + ], + "frpc": "", + "peers": [], + "join_key": "" +} diff --git a/packaging/common/webui4frpc.env b/packaging/common/webui4frpc.env index 2a74d9c..94e822c 100644 --- a/packaging/common/webui4frpc.env +++ b/packaging/common/webui4frpc.env @@ -1,8 +1,28 @@ -# webui4frpc runtime configuration -# 默认监听全部接口的 7500 端口;请修改密码后再投入生产。 -W4F_ADDR=0.0.0.0:7500 -W4F_USER=admin -W4F_PASSWORD=admin +# webui4frpc optional environment file. +# +# Listening address, admin credentials, workdir and peers now belong in +# /etc/webui4frpc/config.json — that file is what systemd passes via +# `webui4frpc -config ...`, and it keeps the password out of the unit file and +# out of `ps` output. +# +# Everything here is OPTIONAL; the unit uses `EnvironmentFile=-`, so this file +# may be empty or missing entirely. +# +# Referenced by config.json as ${VAR} (the variable must be set and non-empty, +# otherwise startup fails loudly rather than booting with an empty password): +# W4F_PASSWORD=change-me +# +# Cluster admission key, used by config.json as "join_key": "${W4F_JOIN_KEY}". +# W4F_JOIN_KEY= +# +# Overrides the advertised hostname for peers (container DNS / wildcard listen). +# W4F_HOST= +# +# Load balancing defaults (the per-node switch in the UI wins over these). +# W4F_BALANCE_MODE=dry-run +# W4F_BALANCE_COOLDOWN=600 +# W4F_BALANCE_INTERVAL=10 -# 可选:首次提供的 frpc 二进制路径(留空则从 Web UI 手动/自动安装) -# W4F_FRPC=/usr/local/bin/frpc +# Optional: point at a config file outside /etc/webui4frpc. An explicit +# -config flag still wins over this. +# W4F_CONFIG=/etc/webui4frpc/config.json diff --git a/packaging/common/webui4frpc.service b/packaging/common/webui4frpc.service index a1654e8..d612e9d 100644 --- a/packaging/common/webui4frpc.service +++ b/packaging/common/webui4frpc.service @@ -8,8 +8,15 @@ Wants=network-online.target Type=simple User=webui4frpc Group=webui4frpc -EnvironmentFile=/etc/webui4frpc/webui4frpc.env -ExecStart=/usr/bin/webui4frpc -addr ${W4F_ADDR} -user ${W4F_USER} -password ${W4F_PASSWORD} -workdir /var/lib/webui4frpc +# The listening address, admin credentials and cluster peers live in +# config.json, NOT on the command line: anything in ExecStart is visible to +# every user via `ps` and in the unit file itself. Keep the password out of +# this unit — put it in config.json as "file:/etc/webui4frpc/secret/password" +# (or "${W4F_PASSWORD}" with an EnvironmentFile), never as -password. +# The leading "-" makes the EnvironmentFile optional, so a node that keeps all +# settings in config.json still starts. +EnvironmentFile=-/etc/webui4frpc/webui4frpc.env +ExecStart=/usr/bin/webui4frpc -config /etc/webui4frpc/config.json Restart=always RestartSec=5 LimitNOFILE=65535 @@ -20,6 +27,10 @@ PrivateTmp=true ProtectSystem=full ProtectHome=true ReadWritePaths=/var/lib/webui4frpc +# config.json may point at a root-only secrets file for the admin password. +# That path needs to be readable by this service account; keep such files in +# /etc/webui4frpc/secret/ with mode 0640 root:webui4frpc. +ReadOnlyPaths=-/etc/webui4frpc/secret [Install] WantedBy=multi-user.target diff --git a/packaging/deb/postinst b/packaging/deb/postinst index 7998640..0da2b56 100755 --- a/packaging/deb/postinst +++ b/packaging/deb/postinst @@ -18,6 +18,16 @@ chmod 750 /var/lib/webui4frpc chown root:webui4frpc /etc/webui4frpc/webui4frpc.env 2>/dev/null || true chmod 640 /etc/webui4frpc/webui4frpc.env 2>/dev/null || true +# Startup config + the directory that holds the admin password file. +# The service reads /etc/webui4frpc/config.json (see the unit's ExecStart), so +# the password never has to appear on the command line. secret/ is root-writable +# and group-readable by the service account; files inside are created by the +# admin, we only fix ownership/permissions of the directory itself. +mkdir -p /etc/webui4frpc/secret +chown root:webui4frpc /etc/webui4frpc/secret 2>/dev/null || true +chmod 750 /etc/webui4frpc/secret 2>/dev/null || true +chmod 644 /etc/webui4frpc/config.json 2>/dev/null || true + if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload || true systemctl enable webui4frpc.service || true @@ -30,7 +40,8 @@ cat <<'EOF' webui4frpc 已安装。 - 配置: /etc/webui4frpc/webui4frpc.env (请立即修改 W4F_USER / W4F_PASSWORD) + 配置: /etc/webui4frpc/config.json (请设置 password,推荐 file:/etc/webui4frpc/secret/password) + 密钥: echo '你的口令' > /etc/webui4frpc/secret/password && chmod 0640 /etc/webui4frpc/secret/password 数据: /var/lib/webui4frpc 服务: systemctl status webui4frpc 界面: http://<本机地址>:7500 diff --git a/packaging/macos/build_pkg.sh b/packaging/macos/build_pkg.sh index ef1fa1a..f7302a8 100755 --- a/packaging/macos/build_pkg.sh +++ b/packaging/macos/build_pkg.sh @@ -38,15 +38,25 @@ mkdir -p "$ROOT/usr/local/bin" "$ROOT/usr/local/etc/webui4frpc" "$SCRIPTS" "$PKG # ---- payload 根文件系统 ---- install -m 0755 "$BINARY" "$ROOT/usr/local/bin/webui4frpc" -cat > "$ROOT/usr/local/etc/webui4frpc/webui4frpc.env" <<'ENVEOF' -# webui4frpc runtime configuration (macOS) -# 请修改密码后再投入使用;改完执行: -# sudo launchctl kickstart -k system/com.jianfeeeee.webui4frpc -W4F_ADDR=0.0.0.0:7500 -W4F_USER=admin -W4F_PASSWORD=admin -ENVEOF -chmod 0644 "$ROOT/usr/local/etc/webui4frpc/webui4frpc.env" +# Startup config. The service reads this file via `-config`, so the admin +# password never appears on the command line (visible in `ps` to every user). +# Set the password in /usr/local/etc/webui4frpc/secret/password instead of +# writing it here; the "file:" reference below reads its first line. +mkdir -p "$ROOT/usr/local/etc/webui4frpc/secret" +cat > "$ROOT/usr/local/etc/webui4frpc/config.json" <<'CFGEOF' +{ + "_comment": "precedence: command-line flag > this file > built-in default", + "addr": "0.0.0.0:7500", + "user": "admin", + "password": "file:/usr/local/etc/webui4frpc/secret/password", + "workdir": "/usr/local/var/lib/webui4frpc" +} +CFGEOF +chmod 0644 "$ROOT/usr/local/etc/webui4frpc/config.json" +# Placeholder so the service starts out of the box (admin/admin). The +# postinstall script removes it if the file was already customised. +printf 'admin\n' > "$ROOT/usr/local/etc/webui4frpc/secret/password" +chmod 0640 "$ROOT/usr/local/etc/webui4frpc/secret/password" # ---- launchd plist ---- mkdir -p "$ROOT/Library/LaunchDaemons" @@ -80,18 +90,15 @@ chmod 0644 "$ROOT/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist" # launchd 不读 EnvironmentFile,用小包装脚本 source 配置再 exec cat > "$ROOT/usr/local/bin/webui4frpc-launch" <<'LAUNCHEOF' #!/bin/sh -# 读取 /usr/local/etc/webui4frpc/webui4frpc.env 后启动 webui4frpc +# Start webui4frpc from /usr/local/etc/webui4frpc/config.json. +# launchd does not read an EnvironmentFile, so the optional env file is sourced +# here first (config.json may reference ${W4F_*}), then the config file drives +# everything. No credential is ever passed on this command line. set -e ENV_FILE=/usr/local/etc/webui4frpc/webui4frpc.env -W4F_ADDR=0.0.0.0:7500 -W4F_USER=admin -W4F_PASSWORD=admin +CONFIG_FILE=/usr/local/etc/webui4frpc/config.json [ -f "$ENV_FILE" ] && . "$ENV_FILE" -exec /usr/local/bin/webui4frpc \ - -addr "$W4F_ADDR" \ - -user "$W4F_USER" \ - -password "$W4F_PASSWORD" \ - -workdir /usr/local/var/lib/webui4frpc +exec /usr/local/bin/webui4frpc -config "$CONFIG_FILE" LAUNCHEOF chmod 0755 "$ROOT/usr/local/bin/webui4frpc-launch" @@ -159,12 +166,16 @@ cat > "$FLAT/Distribution" <webui4frpc — 可视化 frpc 控制器 安装内容: - /usr/local/bin/webui4frpc 主程序(内嵌 Web 界面) - /usr/local/etc/webui4frpc/webui4frpc.env 配置文件 - /Library/LaunchDaemons/…webui4frpc.plist 开机自启服务 + /usr/local/bin/webui4frpc 主程序(内嵌 Web 界面) + /usr/local/etc/webui4frpc/config.json 启动配置(监听地址/账号/对端) + /usr/local/etc/webui4frpc/secret/password 口令文件(file: 引用,不进命令行) + /usr/local/etc/webui4frpc/webui4frpc.env 可选环境变量(config.json 里可写 \${VAR}) + /Library/LaunchDaemons/…webui4frpc.plist 开机自启服务 安装完成后服务自动启动,访问 http://127.0.0.1:7500 -默认凭据 admin/admin —— 请立即修改 webui4frpc.env 中的密码。 +默认凭据 admin/admin —— 请立即修改 +/usr/local/etc/webui4frpc/secret/password 后再重启 +(sudo launchctl kickstart -k system/com.jianfeeeee.webui4frpc)。 diff --git a/packaging/rpm/webui4frpc.spec b/packaging/rpm/webui4frpc.spec index 30e77e0..bf20280 100644 --- a/packaging/rpm/webui4frpc.spec +++ b/packaging/rpm/webui4frpc.spec @@ -20,6 +20,8 @@ and audit-log CSV export. install -D -m 0755 %{_w4f_bin} %{buildroot}%{_bindir}/webui4frpc install -D -m 0644 %{_w4f_service} %{buildroot}%{_unitdir}/webui4frpc.service install -D -m 0640 %{_w4f_env} %{buildroot}%{_sysconfdir}/webui4frpc/webui4frpc.env +install -D -m 0644 %{_w4f_config} %{buildroot}%{_sysconfdir}/webui4frpc/config.json +install -d -m 0750 %{buildroot}%{_sysconfdir}/webui4frpc/secret install -D -m 0644 %{_w4f_readme} %{buildroot}%{_docdir}/webui4frpc/README.md install -d -m 0750 %{buildroot}%{_sharedstatedir}/webui4frpc @@ -36,11 +38,14 @@ exit 0 %systemd_post webui4frpc.service chown -R webui4frpc:webui4frpc %{_sharedstatedir}/webui4frpc || : chown root:webui4frpc %{_sysconfdir}/webui4frpc/webui4frpc.env || : +chown root:webui4frpc %{_sysconfdir}/webui4frpc/secret || : +chmod 0750 %{_sysconfdir}/webui4frpc/secret || : cat <<'EOF' webui4frpc 已安装。 - 配置: /etc/webui4frpc/webui4frpc.env (请立即修改 W4F_USER / W4F_PASSWORD) + 配置: /etc/webui4frpc/config.json (请设置 password,推荐 file:/etc/webui4frpc/secret/password) + 密钥: echo '你的口令' > /etc/webui4frpc/secret/password && chmod 0640 /etc/webui4frpc/secret/password 数据: /var/lib/webui4frpc 服务: systemctl enable --now webui4frpc 界面: http://<本机地址>:7500 @@ -63,6 +68,8 @@ fi %{_unitdir}/webui4frpc.service %dir %{_sysconfdir}/webui4frpc %config(noreplace) %attr(0640,root,webui4frpc) %{_sysconfdir}/webui4frpc/webui4frpc.env +%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/webui4frpc/config.json +%dir %attr(0750,root,webui4frpc) %{_sysconfdir}/webui4frpc/secret %dir %attr(0750,webui4frpc,webui4frpc) %{_sharedstatedir}/webui4frpc %doc %{_docdir}/webui4frpc/README.md diff --git a/packaging/windows/webui4frpc.nsi b/packaging/windows/webui4frpc.nsi index 59df78c..ce05c0f 100644 --- a/packaging/windows/webui4frpc.nsi +++ b/packaging/windows/webui4frpc.nsi @@ -5,6 +5,10 @@ !include "MUI2.nsh" !include "LogicLib.nsh" +!include "StrFunc.nsh" + +; Used to JSON-escape the free-text fields written into config.json below. +${Using:StrFunc} StrRep !ifndef VERSION !define VERSION "0.0.0" @@ -48,6 +52,9 @@ Var TxtPass Var CfgAddr Var CfgUser Var CfgPass +Var EscAddr +Var EscUser +Var EscDir Function .onInit StrCpy $CfgAddr "0.0.0.0:7500" @@ -95,12 +102,37 @@ Section "webui4frpc" SecMain CreateDirectory "$INSTDIR\data" - ; 启动脚本,承载安装时选择的配置 + ; ---- 启动配置 ---- + ; 口令写进独立文件、由 config.json 用 "file:" 引用:既不出现在启动脚本的 + ; 命令行上(任务管理器 / wmic 可见),也不需要做 JSON 转义。 + ${StrRep} $EscDir $INSTDIR "\" "\\" + ${StrRep} $EscAddr $CfgAddr "\" "\\" + ${StrRep} $EscAddr $EscAddr '$\"' '\"' + ${StrRep} $EscUser $CfgUser "\" "\\" + ${StrRep} $EscUser $EscUser '$\"' '\"' + + FileOpen $1 "$INSTDIR\secret-password.txt" w + FileWrite $1 "$CfgPass$\r$\n" + FileClose $1 + ; 口令文件仅 SYSTEM / Administrators 可读(Program Files 默认 Users 可读) + nsExec::ExecToLog 'icacls "$INSTDIR\secret-password.txt" /inheritance:r /grant:r *S-1-5-18:F *S-1-5-32-544:F' + + FileOpen $2 "$INSTDIR\data\config.json" w + FileWrite $2 "{$\r$\n" + FileWrite $2 ' "addr": "$EscAddr",$\r$\n' + FileWrite $2 ' "user": "$EscUser",$\r$\n' + FileWrite $2 ' "password": "file:$EscDir\\secret-password.txt",$\r$\n' + FileWrite $2 ' "workdir": "data"$\r$\n' + FileWrite $2 "}$\r$\n" + FileClose $2 + + ; 启动脚本:只传 -config,不传任何凭据 FileOpen $0 "$INSTDIR\start-webui4frpc.cmd" w FileWrite $0 "@echo off$\r$\n" FileWrite $0 "rem webui4frpc 启动脚本(由安装程序生成)$\r$\n" + FileWrite $0 "rem 凭据在 data\config.json(口令来自 secret-password.txt),不经过命令行。$\r$\n" FileWrite $0 "cd /d %~dp0$\r$\n" - FileWrite $0 'webui4frpc.exe -addr $CfgAddr -user $CfgUser -password $CfgPass -workdir "%~dp0data"$\r$\n' + FileWrite $0 'webui4frpc.exe -config "%~dp0data\config.json"$\r$\n' FileClose $0 WriteRegStr HKLM "Software\webui4frpc" "InstallDir" "$INSTDIR" @@ -142,6 +174,8 @@ Section "Uninstall" Delete "$INSTDIR\webui4frpc.exe" Delete "$INSTDIR\start-webui4frpc.cmd" + Delete "$INSTDIR\secret-password.txt" + Delete "$INSTDIR\data\config.json" Delete "$INSTDIR\README.md" Delete "$INSTDIR\LICENSE.txt" Delete "$INSTDIR\uninstall.exe" diff --git a/scripts/build_installers.sh b/scripts/build_installers.sh index 8e298fd..c30a6d8 100755 --- a/scripts/build_installers.sh +++ b/scripts/build_installers.sh @@ -46,6 +46,8 @@ EOF install -m 0755 "$bindir/webui4frpc" "$deb/usr/bin/webui4frpc" install -m 0644 "$PKG/common/webui4frpc.service" "$deb/lib/systemd/system/webui4frpc.service" install -m 0640 "$PKG/common/webui4frpc.env" "$deb/etc/webui4frpc/webui4frpc.env" + install -m 0644 "$PKG/common/config.json" "$deb/etc/webui4frpc/config.json" + install -d -m 0750 "$deb/etc/webui4frpc/secret" install -m 0644 "$ROOT/README.md" "$deb/usr/share/doc/webui4frpc/README.md" cat > "$deb/usr/share/doc/webui4frpc/copyright" <<'EOF' webui4frpc - visual frpc controller @@ -55,7 +57,7 @@ EOF install -m 0755 "$PKG/deb/postinst" "$deb/DEBIAN/postinst" install -m 0755 "$PKG/deb/prerm" "$deb/DEBIAN/prerm" install -m 0755 "$PKG/deb/postrm" "$deb/DEBIAN/postrm" - echo "/etc/webui4frpc/webui4frpc.env" > "$deb/DEBIAN/conffiles" + printf '%s\n' /etc/webui4frpc/webui4frpc.env /etc/webui4frpc/config.json > "$deb/DEBIAN/conffiles" ( cd "$deb" && find usr etc lib -type f -exec md5sum {} \; > DEBIAN/md5sums ) dpkg-deb -b --root-owner-group "$deb" "$OUT/webui4frpc-${VERSION}-linux-${arch}.deb" >/dev/null echo " ✔ .deb linux/$arch" @@ -74,6 +76,7 @@ build_rpm() { --define "_w4f_bin $bindir/webui4frpc" \ --define "_w4f_service $PKG/common/webui4frpc.service" \ --define "_w4f_env $PKG/common/webui4frpc.env" \ + --define "_w4f_config $PKG/common/config.json" \ --define "_w4f_readme $ROOT/README.md" \ --define "_unitdir /usr/lib/systemd/system" \ --define "_sharedstatedir /var/lib" >/tmp/w4f-rpm.log 2>&1 || true diff --git a/scripts/release.sh b/scripts/release.sh index 400bfef..4863a21 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -92,7 +92,10 @@ Wants=network-online.target [Service] Type=simple -ExecStart=/opt/webui4frpc/webui4frpc -addr 0.0.0.0:7500 -user admin -password admin -workdir /opt/webui4frpc/data +# Listen address, admin credentials and peers live in config.json, not here: +# whatever is in ExecStart is visible to every user via `ps`. Set the password +# in /opt/webui4frpc/config.json as "file:/opt/webui4frpc/secret/password". +ExecStart=/opt/webui4frpc/webui4frpc -config /opt/webui4frpc/config.json Restart=always RestartSec=5 LimitNOFILE=65535 @@ -121,7 +124,8 @@ for target in "${TARGETS[@]}"; do ( cd "$stage" && zip -q "$OUT/$base.zip" "$bin" README.md ) else cp "$OUT/webui4frpc.service.tpl" "$stage/" - ( cd "$stage" && tar czf "$OUT/$base.tar.gz" "$bin" README.md webui4frpc.service.tpl ) + cp "$ROOT/packaging/common/config.json" "$stage/config.json.example" + ( cd "$stage" && tar czf "$OUT/$base.tar.gz" "$bin" README.md webui4frpc.service.tpl config.json.example ) fi done rm -rf "$OUT/.stage"