Files
webui4frpc/packaging/macos/build_pkg.sh
JianFeeeee ee46b758fa feat(packaging): 单元与安装包改为配置文件启动,口令不再写入 ExecStart
所有交付形态统一成「只传 -config」,凭据留在配置文件 / secrets 文件里。

- systemd 单元:ExecStart 改为 `-config /etc/webui4frpc/config.json`,
  EnvironmentFile 加 `-` 前缀变成可选;新增 ReadOnlyPaths=-/etc/webui4frpc/secret。
- 新增 packaging/common/config.json 示例:口令默认用
  file:/etc/webui4frpc/secret/password 引用,内嵌以 "_" 开头的说明键。
- webui4frpc.env 瘦身为可选环境变量(${VAR} 取值来源),不再承载明文口令。
- deb/rpm:安装 config.json 与 0750 的 secret 目录,config.json 进 conffiles,
  安装后提示改成「设置 password 文件」。
- release.sh 的分发单元模板同步改 -config;tar.gz 附 config.json.example。
- macOS pkg:写 config.json + secret/password,launch 包装脚本只传 -config。
- Windows NSIS:安装时把口令写进 secret-password.txt(icacls 限制为
  SYSTEM/Administrators),生成 data/config.json 用 file: 引用,启动脚本只传
  -config;顺带按 JSON 规则转义 addr/user。makensis 实测可编译通过。
2026-10-23 09:12:30 +08:00

199 lines
7.3 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
#
# 在 Linux 上手工组装 macOS .pkg (flat package)。
# macOS 的 pkgbuild/productbuild 不可用,因此用 xar + mkbom 手搓 flat package:
#
# archive.xar
# ├── Distribution 安装器界面描述 (choices / title)
# └── webui4frpc.pkg/
# ├── PackageInfo 组件元数据 + postinstall 声明
# ├── Bom 文件清单 (mkbom 生成)
# ├── Payload 根文件系统 cpio.gz
# └── Scripts 安装脚本 cpio.gz
#
# 用法: build_pkg.sh <version> <arch:amd64|arm64> <binary> <outfile>
set -euo pipefail
VERSION="$1"; ARCH="$2"
# xar 与 cpio 都在临时目录里执行,输入输出必须先转成绝对路径
BINARY="$(cd "$(dirname "$3")" && pwd)/$(basename "$3")"
mkdir -p "$(dirname "$4")"
OUTFILE="$(cd "$(dirname "$4")" && pwd)/$(basename "$4")"
case "$ARCH" in
amd64) PKG_ARCH="x86_64" ;;
arm64) PKG_ARCH="arm64" ;;
*) echo "未知架构: $ARCH" >&2; exit 1 ;;
esac
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
ROOT="$WORK/root"
SCRIPTS="$WORK/scripts"
FLAT="$WORK/flat"
PKGDIR="$FLAT/webui4frpc.pkg"
mkdir -p "$ROOT/usr/local/bin" "$ROOT/usr/local/etc/webui4frpc" "$SCRIPTS" "$PKGDIR"
# ---- payload 根文件系统 ----
install -m 0755 "$BINARY" "$ROOT/usr/local/bin/webui4frpc"
# 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"
cat > "$ROOT/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist" <<'PLISTEOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.jianfeeeee.webui4frpc</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/webui4frpc-launch</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/usr/local/var/log/webui4frpc.log</string>
<key>StandardErrorPath</key>
<string>/usr/local/var/log/webui4frpc.err.log</string>
<key>WorkingDirectory</key>
<string>/usr/local/var/lib/webui4frpc</string>
</dict>
</plist>
PLISTEOF
chmod 0644 "$ROOT/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist"
# launchd 不读 EnvironmentFile,用小包装脚本 source 配置再 exec
cat > "$ROOT/usr/local/bin/webui4frpc-launch" <<'LAUNCHEOF'
#!/bin/sh
# 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
CONFIG_FILE=/usr/local/etc/webui4frpc/config.json
[ -f "$ENV_FILE" ] && . "$ENV_FILE"
exec /usr/local/bin/webui4frpc -config "$CONFIG_FILE"
LAUNCHEOF
chmod 0755 "$ROOT/usr/local/bin/webui4frpc-launch"
# ---- postinstall ----
cat > "$SCRIPTS/postinstall" <<'POSTEOF'
#!/bin/sh
set -e
mkdir -p /usr/local/var/lib/webui4frpc /usr/local/var/log
chmod 750 /usr/local/var/lib/webui4frpc
PLIST=/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist
chown root:wheel "$PLIST"
chmod 644 "$PLIST"
# 覆盖安装时先卸载旧的
launchctl bootout system "$PLIST" 2>/dev/null || true
launchctl bootstrap system "$PLIST" 2>/dev/null || \
launchctl load -w "$PLIST" 2>/dev/null || true
exit 0
POSTEOF
chmod 0755 "$SCRIPTS/postinstall"
# ---- Payload / Scripts (cpio.gz) ----
( cd "$ROOT" && find . -print | cpio -o --format odc --owner 0:0 2>/dev/null ) \
| gzip -9 > "$PKGDIR/Payload"
( cd "$SCRIPTS" && find . -print | cpio -o --format odc --owner 0:0 2>/dev/null ) \
| gzip -9 > "$PKGDIR/Scripts"
# ---- Bom ----
mkbom -u 0 -g 80 "$ROOT" "$PKGDIR/Bom"
NFILES=$(find "$ROOT" | wc -l)
INSTALL_KB=$(du -sk "$ROOT" | cut -f1)
# ---- PackageInfo ----
cat > "$PKGDIR/PackageInfo" <<INFOEOF
<?xml version="1.0" encoding="utf-8"?>
<pkg-info format-version="2"
identifier="com.jianfeeeee.webui4frpc"
version="$VERSION"
install-location="/"
auth="root"
relocatable="false"
overwrite-permissions="true">
<payload installKBytes="$INSTALL_KB" numberOfFiles="$NFILES"/>
<scripts>
<postinstall file="./postinstall"/>
</scripts>
<bundle-version/>
</pkg-info>
INFOEOF
# ---- Distribution ----
cat > "$FLAT/Distribution" <<DISTEOF
<?xml version="1.0" encoding="utf-8"?>
<installer-gui-script minSpecVersion="1">
<title>webui4frpc $VERSION</title>
<organization>com.jianfeeeee</organization>
<options customize="never" require-scripts="false" hostArchitectures="$PKG_ARCH"/>
<domains enable_localSystem="true" enable_anywhere="false" enable_currentUserHome="false"/>
<volume-check>
<allowed-os-versions><os-version min="11.0"/></allowed-os-versions>
</volume-check>
<welcome language="zh_CN" mime-type="text/plain">webui4frpc — 可视化 frpc 控制器
安装内容:
/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 —— 请立即修改
/usr/local/etc/webui4frpc/secret/password 后再重启
(sudo launchctl kickstart -k system/com.jianfeeeee.webui4frpc)。</welcome>
<choices-outline>
<line choice="default">
<line choice="com.jianfeeeee.webui4frpc"/>
</line>
</choices-outline>
<choice id="default"/>
<choice id="com.jianfeeeee.webui4frpc" visible="false">
<pkg-ref id="com.jianfeeeee.webui4frpc"/>
</choice>
<pkg-ref id="com.jianfeeeee.webui4frpc"
version="$VERSION"
onConclusion="none">#webui4frpc.pkg</pkg-ref>
</installer-gui-script>
DISTEOF
# ---- 打成 xar (Distribution 必须是第一个成员) ----
rm -f "$OUTFILE"
( cd "$FLAT" && xar --compression none -cf "$OUTFILE" Distribution webui4frpc.pkg )
echo "已生成: $OUTFILE ($(du -h "$OUTFILE" | cut -f1))"