初始化蓝鸟文件快递柜源码与部署文档

This commit is contained in:
lhc committed 2026-10-05 23:41:07 +08:00
commit 5643d22242
70 files changed
+18669

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
# 两个明确入口:HTTP直接提供服务,HTTPS使用自动证书。
# 代理异机时改为蓝鸟实际内网IP,并将代理IP加入trusted_proxies。
{
auto_https disable_redirects
}
http://share.example.com {
reverse_proxy 192.168.1.10:8080 {
flush_interval -1
# 不配置request_buffers/response_buffers;流式传输,避免额外磁盘副本
}
}
https://share.example.com {
reverse_proxy 192.168.1.10:8080 {
flush_interval -1
}
}
+35
View File
@@ -0,0 +1,35 @@
[Unit]
Description=Bluebird anonymous file cabinet
After=network.target
[Service]
Type=simple
User=bluebird
Group=bluebird
WorkingDirectory=/opt/bluebird
ExecStart=/opt/bluebird/bluebird serve --config /opt/bluebird/config.toml
Restart=on-failure
RestartSec=5
TimeoutStopSec=90
UMask=0077
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/bluebird/var
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
CapabilityBoundingSet=
LockPersonality=true
MemoryMax=768M
TasksMax=256
LimitNOFILE=4096
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
+46
View File
@@ -0,0 +1,46 @@
listen = "127.0.0.1:8080"
public_url = "http://localhost:8080" # 主入口,支持HTTP或HTTPS
access_urls = [] # 额外入口,如 ["http://files.example.com", "http://192.168.1.10:8080"]
secure_cookies = false # 主入口HTTPS时为true;各入口按自身协议设置cookie
allow_lan_http = false # 私有IP HTTP入口需显式开启;纯内网模式需绑定具体私有IP
# 相对路径相对于此配置文件,而非进程当前目录
data_dir = "var"
encryption = false
deduplication = true
trusted_proxies = [] # 仅填实际反向代理 CIDR,如 127.0.0.1/32
# 二进制字节单位,文件预算须小于数据预算至少64MiB
data_budget = 10737418240
file_budget = 9663676416
min_free_bytes = 1073741824
# 以下容量和审计值仅用于首次初始化;之后在管理员后台管理
audit_retention_days = 30
audit_max_records = 100000
audit_max_bytes = 33554432
max_uploads = 8
max_downloads = 16
max_ip_uploads = 2
max_ip_downloads = 4
max_records = 200000
max_transfer_seconds = 3600
transfer_idle_seconds = 30
# 内置 HTTPS 可选,否则由已有代理终止 TLS
# tls_cert = "cert.pem"
# tls_key = "key.pem"
[policy]
anonymous_upload = true
anonymous_download = true
max_file_bytes = 104857600
max_batch_bytes = 524288000
max_files = 10
default_days = 1
max_initial_days = 7
max_lifetime_days = 30
default_downloads = 5
max_downloads = 20
renewal_days = 3
renewal_mode = "add" # add:现到期+3天;sliding:至少从下载完成保留3天
ip_daily_bytes = 1073741824
ip_daily_uploads = 20
session_daily_uploads = 20
require_password = false
+174
View File
@@ -0,0 +1,174 @@
#!/bin/sh
# Fresh installation; existing services, configuration and data are never replaced.
set -eu
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
umask 077
usage() {
cat <<'HELP'
用法:sudo ./install.sh --url 站点地址 [选项]
--url URL 完整站点地址,如 https://files.example.com
--prefix DIR 安装目录,默认 /opt/bluebird
--listen ADDR 后端监听地址,HTTPS代理默认 127.0.0.1:8080
--lan-http 允许绑定具体私有IP提供内网HTTP服务
--access-url URL 增加其他HTTP/HTTPS入口,可重复
--lan-url URL 增加内网IP入口,并默认监听该IP和端口
--encrypt 启用新文件的服务端落盘加密
--no-start 安装并初始化,但暂不启用/启动systemd服务
-h, --help 显示帮助
首次安装会在终端询问管理员密码,账号为 admin。
自动化可通过 BLUEBIRD_ADMIN_PASSWORD 环境变量传入;密码不写入配置文件。
系统需使用systemd,并提供getent、useradd、groupadd、runuser和coreutils。
HTTPS证书由你已有的反向代理管理;脚本不安装代理或修改防火墙。
已有安装请按 docs/operations.md 升级,脚本不会覆盖它。
HELP
}
die() { printf '%s\n' "安装失败:$*" >&2; exit 1; }
require_value() { [ "$#" -ge 2 ] && [ -n "$2" ] || die "$1 缺少参数"; }
prefix=/opt/bluebird
public_url=
listen=
lan_http=false
extra_urls=
lan_url=
encryption=false
start_service=true
while [ "$#" -gt 0 ]; do
case "$1" in
--url) require_value "$@"; public_url=$2; shift 2 ;;
--prefix) require_value "$@"; prefix=$2; shift 2 ;;
--listen) require_value "$@"; listen=$2; shift 2 ;;
--lan-http) lan_http=true; shift ;;
--access-url|--lan-url)
require_value "$@"
case "$2" in *[[:space:]]*|*'"'*|*'\'*) die '额外入口不能包含空白、引号或反斜杠' ;; esac
extra_urls="$extra_urls\"$2\","
if [ "$1" = --lan-url ]; then lan_url=$2; lan_http=true; fi
shift 2 ;;
--encrypt) encryption=true; shift ;;
--no-start) start_service=false; shift ;;
-h|--help) usage; exit 0 ;;
*) die "未知选项:$1" ;;
esac
done
[ -n "$public_url" ] || { usage; exit 1; }
[ "$(id -u)" = 0 ] || die '请使用sudo或root运行'
[ "$(uname -s)" = Linux ] || die '仅支持Linux'
# Keep TOML and systemd paths literal; reject newline, quote and expansion syntax.
case "$public_url" in *[[:space:]]*|*'"'*|*'\'*) die '站点地址不能包含空白、引号或反斜杠' ;; esac
case "$listen" in *[[:space:]]*|*'"'*|*'\'*) die '监听地址格式无效' ;; esac
printf '%s\n' "$prefix" | grep -Eq '^/[A-Za-z0-9._/-]+$' || die '安装目录须为不含空格的绝对路径'
prefix=$(realpath -m -- "$prefix")
[ "$prefix" != / ] || die '不能安装到根目录'
case "$prefix" in /home/*|/root/*) die 'systemd保护home目录,请安装到/opt或/srv等目录' ;; esac
[ ! -e "$prefix" ] && [ ! -L "$prefix" ] || die "$prefix 已存在,未修改已有安装"
unit=/etc/systemd/system/bluebird.service
[ ! -e "$unit" ] && [ ! -L "$unit" ] || die "$unit 已存在,未修改已有服务"
package_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
[ -x "$package_dir/bluebird" ] && [ -r "$package_dir/config.toml" ] && [ -r "$package_dir/ARCH" ] || die '请从解压后的发布包运行install.sh'
case "$(cat "$package_dir/ARCH")" in
amd64) [ "$(uname -m)" = x86_64 ] || die '需要Linux amd64/x86_64主机' ;;
arm64) [ "$(uname -m)" = aarch64 ] || die '需要Linux arm64/aarch64主机' ;;
*) die '发布包架构信息无效' ;;
esac
for command in getent useradd groupadd runuser install mktemp realpath awk sed; do
command -v "$command" >/dev/null 2>&1 || die "缺少系统命令:$command"
done
if [ "$start_service" = true ]; then
command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ] || die 'systemd未运行;离线安装请使用--no-start'
fi
explicit_listen=$listen
public_url=${public_url%/}
case "$public_url" in
https://*) secure_cookies=true; trusted_proxies='["127.0.0.1/32", "::1/128"]'; [ -n "$listen" ] || listen=127.0.0.1:8080 ;;
http://*) secure_cookies=false; trusted_proxies='[]'
authority=${public_url#http://}
if [ -z "$listen" ]; then
case "$authority" in
localhost:*|127.*:*|10.*:*|192.168.*:*|172.*:*|\[*\]:*) listen=$authority ;;
localhost|127.*|10.*|192.168.*|172.*|\[*\]) die '内网HTTP地址须包含端口,如http://192.168.1.10:8080' ;;
*) listen=127.0.0.1:8080 ;;
esac
case "$listen" in localhost:*) listen="127.0.0.1:${listen#localhost:}" ;; esac
fi ;;
*) die '站点地址须以http://或https://开头' ;;
esac
if [ -n "$lan_url" ] && [ -z "$explicit_listen" ]; then
case "$lan_url" in http://*) listen=${lan_url#http://}; listen=${listen%/} ;; *) die '--lan-url须使用http://内网IP:端口' ;; esac
fi
port=${listen##*:}
case "$port" in ''|*[!0-9]*) die '监听地址须包含数字端口,如127.0.0.1:8080' ;; esac
[ "$port" -ge 1024 ] && [ "$port" -le 65535 ] || die '独立服务账号使用1024至65535端口;80/443交给反向代理'
parent=$(dirname -- "$prefix")
[ -d "$parent" ] || install -d -m 0755 -- "$parent"
stage=$(mktemp -d "$parent/.bluebird-install-XXXXXX")
cleanup() { [ -z "$stage" ] || rm -rf -- "$stage"; }
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM HUP
awk -v address="$listen" -v url="$public_url" -v secure="$secure_cookies" -v lan="$lan_http" -v encryption="$encryption" -v proxies="$trusted_proxies" -v extras="[$extra_urls]" '
/^listen = / {print "listen = \"" address "\"";next}
/^public_url = / {print "public_url = \"" url "\"";next}
/^access_urls = / {print "access_urls = " extras;next}
/^secure_cookies = / {print "secure_cookies = " secure;next}
/^allow_lan_http = / {print "allow_lan_http = " lan;next}
/^encryption = / {print "encryption = " encryption;next}
/^trusted_proxies = / {print "trusted_proxies = " proxies;next}
{print}
' "$package_dir/config.toml" > "$stage/config.toml"
# Validate before creating an account, database or final installation directory.
if [ -n "$lan_url" ]; then
"$package_dir/bluebird" configure-access --config "$stage/config.toml" --listen "$listen" --lan-url "$lan_url"
fi
"$package_dir/bluebird" validate-config --config "$stage/config.toml"
if ! getent group bluebird >/dev/null; then groupadd --system bluebird; fi
[ "$(getent group bluebird | cut -d: -f3)" != 0 ] || die 'bluebird组不能使用GID 0'
if ! getent passwd bluebird >/dev/null; then
nologin=/usr/sbin/nologin
[ -x "$nologin" ] || nologin=/sbin/nologin
[ -x "$nologin" ] || nologin=/bin/false
useradd --system --no-create-home --gid bluebird --home-dir "$prefix" --shell "$nologin" bluebird
fi
[ "$(id -u bluebird)" != 0 ] || die 'bluebird账号不能使用UID 0'
chown root:bluebird "$stage"
chmod 0750 "$stage"
install -o root -g bluebird -m 0750 "$package_dir/bluebird" "$stage/bluebird"
chown root:bluebird "$stage/config.toml"
chmod 0640 "$stage/config.toml"
install -d -o bluebird -g bluebird -m 0700 "$stage/var"
if [ -f "$package_dir/THIRD_PARTY_NOTICES.txt" ]; then
install -o root -g bluebird -m 0644 "$package_dir/THIRD_PARTY_NOTICES.txt" "$stage/THIRD_PARTY_NOTICES.txt"
fi
# runuser preserves the terminal and optional bootstrap environment variable.
runuser --preserve-environment -u bluebird -- "$stage/bluebird" init --config "$stage/config.toml"
unset BLUEBIRD_ADMIN_PASSWORD
sed "s|/opt/bluebird|$prefix|g" "$package_dir/deploy/bluebird.service" > "$stage/bluebird.service"
chown root:bluebird "$stage/bluebird.service"
chmod 0640 "$stage/bluebird.service"
# Stage and target share a filesystem; publish the initialized installation once.
[ ! -e "$prefix" ] && [ ! -L "$prefix" ] || die "$prefix 已出现,未覆盖它"
[ ! -e "$unit" ] && [ ! -L "$unit" ] || die "$unit 已出现,未覆盖它"
mv -T -- "$stage" "$prefix"
stage=
[ -d /etc/systemd/system ] || install -d -m 0755 /etc/systemd/system
install -m 0644 "$prefix/bluebird.service" "$unit"
if [ "$start_service" = true ]; then
systemctl daemon-reload
if ! systemctl enable --now bluebird.service; then
printf '%s\n' '文件和数据已保留;请执行 journalctl -u bluebird -n 50 查看启动错误。' >&2
exit 1
fi
fi
printf '%s\n' '' "蓝鸟已安装:$prefix" "配置:$prefix/config.toml" "数据、数据库和密钥:$prefix/var" "后端监听:$listen" "站点:$public_url" "管理地址:$public_url/#/admin(账号admin)"
if [ "$secure_cookies" = true ]; then
printf '%s\n' 'HTTPS入口通过反向代理访问;已配置的HTTP或内网入口也可使用,详见docs/deployment.md。'
fi
if [ -n "$extra_urls" ]; then printf '%s\n' "额外入口:[$extra_urls]"; fi
if [ "$start_service" = false ]; then
printf '%s\n' '服务尚未启动。配置好代理后执行:' ' systemctl daemon-reload' ' systemctl enable --now bluebird.service'
else
printf '%s\n' '已启用开机启动;查看状态:systemctl status bluebird'
fi
+92
View File
@@ -0,0 +1,92 @@
#!/bin/sh
# Upgrade the existing installation, retaining its data and administrator account.
set -eu
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
umask 077
usage() {
cat <<'HELP'
用法:sudo ./upgrade.sh [--prefix /opt/bluebird] [--access-url URL ...] [--lan-url URL] [--listen IP:端口] [--no-start]
保留当前配置参数、文件、密钥和管理员密码;新增入口与指定监听地址会合并到配置中。
默认停止服务、一致性备份、更新程序及配置、检查后启动。失败时恢复旧程序和配置。
--no-start用于已手动停服的离线升级,升级后暂不启动服务。
HELP
}
die(){ printf '%s\n' "升级失败:$*" >&2;exit 1; }
value(){ [ "$#" -ge 2 ] && [ -n "$2" ] || die "$1缺少参数"; }
prefix=/opt/bluebird
additions=
lan_url=
listen=
no_start=false
while [ "$#" -gt 0 ];do
case "$1" in
--prefix) value "$@";prefix=$2;shift 2 ;;
--access-url) value "$@";case "$2" in *[[:space:]]*) die '访问地址不能含空白' ;;esac;additions="$additions $2";shift 2 ;;
--lan-url) value "$@";lan_url=$2;shift 2 ;;
--listen) value "$@";listen=$2;shift 2 ;;
--no-start) no_start=true;shift ;;
-h|--help) usage;exit 0 ;;
*) die "未知选项:$1" ;;
esac
done
[ "$(id -u)" = 0 ] || die '请使用sudo或root运行'
[ "$(uname -s)" = Linux ] || die '仅支持Linux'
prefix=$(realpath -m -- "$prefix")
[ -x "$prefix/bluebird" ] && [ -f "$prefix/config.toml" ] || die '未找到已有安装,请使用install.sh'
exec 9>"$prefix/.upgrade.lock"
flock -n 9 || die '另一升级进程正在运行'
package_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
[ -x "$package_dir/bluebird" ] && [ -r "$package_dir/ARCH" ] || die '请在新版发布包目录运行'
case "$(cat "$package_dir/ARCH"):$(uname -m)" in amd64:x86_64|arm64:aarch64) ;;*) die '发布包架构与主机不匹配' ;;esac
if [ "$no_start" = false ];then [ -d /run/systemd/system ] && command -v systemctl >/dev/null || die 'systemd未运行,先手动停服再使用--no-start';fi
candidate=$(mktemp "$prefix/.upgrade-config-XXXXXX.toml")
backup=
changed=false
was_active=false
success=false
cleanup(){
rm -f -- "$candidate" "$prefix/bluebird.new"
if [ "$success" = false ];then
if [ "$changed" = true ];then
if [ "$no_start" = false ];then systemctl stop bluebird.service || :;fi
install -o root -g bluebird -m 0750 "$backup/bluebird" "$prefix/bluebird.rollback"
mv -f -- "$prefix/bluebird.rollback" "$prefix/bluebird"
cp -p -- "$backup/config.toml" "$prefix/config.toml"
printf '%s\n' "已恢复旧程序和配置;备份保留在$backup" >&2
fi
if [ "$was_active" = true ] && [ "$no_start" = false ];then systemctl start bluebird.service || :;fi
fi
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM HUP
cp -p -- "$prefix/config.toml" "$candidate"
set -- "$package_dir/bluebird" configure-access --config "$candidate"
[ -z "$listen" ] || set -- "$@" --listen "$listen"
[ -z "$lan_url" ] || set -- "$@" --lan-url "$lan_url"
# URLs are separate argv values, never shell code. Globbing is disabled.
set -f
for item in $additions;do set -- "$@" --access-url "$item";done
"$@"
"$package_dir/bluebird" validate-config --config "$candidate"
if [ "$no_start" = false ];then
if systemctl is-active --quiet bluebird.service;then was_active=true;fi
systemctl stop bluebird.service
fi
backup="$prefix/backups/upgrade-$(date -u +%Y%m%dT%H%M%SZ)-$$"
install -d -o bluebird -g bluebird -m 0700 "$backup"
cp -p -- "$prefix/config.toml" "$backup/config.toml"
install -o root -g bluebird -m 0750 "$prefix/bluebird" "$backup/bluebird"
runuser -u bluebird -- "$prefix/bluebird" backup --config "$prefix/config.toml" --output "$backup/data.tar.gz"
# No configuration/data reinitialization; keep all existing administrator hashes.
install -o root -g bluebird -m 0750 "$package_dir/bluebird" "$prefix/bluebird.new"
changed=true
mv -f -- "$prefix/bluebird.new" "$prefix/bluebird"
mv -f -- "$candidate" "$prefix/config.toml"
runuser -u bluebird -- "$prefix/bluebird" check --config "$prefix/config.toml"
if [ "$no_start" = false ];then systemctl start bluebird.service;systemctl is-active --quiet bluebird.service;fi
success=true
printf '%s\n' "升级完成:$prefix" "旧版本、配置及一致性数据备份:$backup"
if [ "$no_start" = true ];then printf '%s\n' '服务尚未启动,请执行:systemctl start bluebird.service';fi