Files

175 lines
8.7 KiB
Bash
Executable File
Raw Permalink 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.
#!/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