diff --git a/bash/3x-ui-cert-sftp.sh b/bash/3x-ui-cert-sftp.sh new file mode 100644 index 0000000..0556b59 --- /dev/null +++ b/bash/3x-ui-cert-sftp.sh @@ -0,0 +1,173 @@ +#!/bin/bash +set -e + +# 自用创建上传证书 + +CERT_USER="3x-ui-cert" +CERT_DIR="/mnt/docker/docker-compose/dpanel/dpanel/compose/3x-ui/data/cert" +SFTP_ROOT="/srv/sftp/${CERT_USER}" +SFTP_DIR="${SFTP_ROOT}/cert" + +echo "========== 3x-ui 证书 SFTP 用户一条龙配置 ==========" + +if [ "$(id -u)" != "0" ]; then + echo "错误:请使用 root 用户执行" + exit 1 +fi + +echo +echo "1. 安装依赖..." +apt update +apt install -y openssh-server acl + +echo +echo "2. 创建/检查 3x-ui 证书目录..." +mkdir -p "$CERT_DIR" + +echo +echo "3. 创建/检查 SFTP 用户:$CERT_USER" +if id "$CERT_USER" >/dev/null 2>&1; then + echo "用户已存在:$CERT_USER" +else + useradd --badname -m -s /usr/sbin/nologin "$CERT_USER" + echo "请设置 $CERT_USER 的密码:" + passwd "$CERT_USER" +fi + +CERT_UID="$(id -u "$CERT_USER")" +CERT_GID="$(id -g "$CERT_USER")" + +echo "当前用户 UID:$CERT_UID" +echo "当前用户 GID:$CERT_GID" + +echo +echo "4. 创建 SFTP 隔离目录..." +mkdir -p "$SFTP_DIR" + +# Chroot 根目录必须 root 拥有,且不能给普通用户写权限 +chown root:root /srv +chmod 755 /srv + +mkdir -p /srv/sftp +chown root:root /srv/sftp +chmod 755 /srv/sftp + +chown root:root "$SFTP_ROOT" +chmod 755 "$SFTP_ROOT" + +echo +echo "5. 重新 bind 挂载证书目录..." +if mountpoint -q "$SFTP_DIR"; then + umount "$SFTP_DIR" 2>/dev/null || umount -l "$SFTP_DIR" +fi + +mount --bind "$CERT_DIR" "$SFTP_DIR" + +echo +echo "6. 修复 /etc/fstab 自动挂载..." +cp /etc/fstab "/etc/fstab.bak.$(date +%F-%H%M%S)" + +TMP_FSTAB="$(mktemp)" +awk -v sftp="$SFTP_DIR" '$2 != sftp {print}' /etc/fstab > "$TMP_FSTAB" +cat "$TMP_FSTAB" > /etc/fstab +rm -f "$TMP_FSTAB" + +echo "$CERT_DIR $SFTP_DIR none bind 0 0" >> /etc/fstab +systemctl daemon-reload 2>/dev/null || true + +echo +echo "7. 给真实路径上级目录添加穿透权限..." +P="$(dirname "$CERT_DIR")" +while [ "$P" != "/" ]; do + setfacl -m u:${CERT_USER}:--x "$P" 2>/dev/null || true + setfacl -m m::rwx "$P" 2>/dev/null || true + P="$(dirname "$P")" +done + +echo +echo "8. 修复 cert 目录本身权限..." +chgrp -R "$CERT_GID" "$CERT_DIR" +chmod -R g+rwX "$CERT_DIR" +find "$CERT_DIR" -type d -exec chmod g+s {} \; + +# 给 SFTP 用户完整读写权限 +setfacl -R -m u:${CERT_USER}:rwX "$CERT_DIR" +setfacl -R -m u:${CERT_UID}:rwX "$CERT_DIR" +setfacl -R -m m::rwx "$CERT_DIR" + +# 默认继承权限:以后新建文件夹/文件也能写 +find "$CERT_DIR" -type d -exec setfacl -m d:u:${CERT_USER}:rwx {} \; +find "$CERT_DIR" -type d -exec setfacl -m d:u:${CERT_UID}:rwx {} \; +find "$CERT_DIR" -type d -exec setfacl -m d:m::rwx {} \; + +echo +echo "9. 可选给 UID 1000 读取权限,避免覆盖当前用户..." +if [ "$CERT_UID" != "1000" ]; then + setfacl -R -m u:1000:rX "$CERT_DIR" 2>/dev/null || true + find "$CERT_DIR" -type d -exec setfacl -m d:u:1000:rX {} \; 2>/dev/null || true +else + echo "当前 $CERT_USER 就是 UID 1000,跳过 u:1000:rX,避免覆盖写权限" +fi + +# 最终再覆盖一次,确保 3x-ui-cert 一定是 rwx +setfacl -R -m u:${CERT_USER}:rwX "$CERT_DIR" +setfacl -R -m m::rwx "$CERT_DIR" +find "$CERT_DIR" -type d -exec setfacl -m d:u:${CERT_USER}:rwx {} \; +find "$CERT_DIR" -type d -exec setfacl -m d:m::rwx {} \; + +echo +echo "10. 写入 SSH SFTP 限制配置..." +rm -f /etc/ssh/sshd_config.d/3x-ui-cert-sftp.conf +sed -i '/# BEGIN 3X_UI_CERT_SFTP/,/# END 3X_UI_CERT_SFTP/d' /etc/ssh/sshd_config + +cat >>/etc/ssh/sshd_config <<'SSHEOF' + +# BEGIN 3X_UI_CERT_SFTP +Match User 3x-ui-cert + ChrootDirectory /srv/sftp/3x-ui-cert + ForceCommand internal-sftp -d /cert + PasswordAuthentication yes + PubkeyAuthentication no + PermitTTY no + AllowTcpForwarding no + X11Forwarding no + AllowAgentForwarding no +# END 3X_UI_CERT_SFTP +SSHEOF + +echo +echo "11. 检查并重启 SSH..." +sshd -t +systemctl restart ssh 2>/dev/null || systemctl restart sshd 2>/dev/null || service ssh restart + +echo +echo "12. 测试 SFTP 实际路径写入..." +runuser -u "$CERT_USER" -- touch "$SFTP_DIR/.sftp_write_test" +rm -f "$SFTP_DIR/.sftp_write_test" + +echo +echo "13. 测试新建域名目录和证书文件..." +TEST_DIR="$SFTP_DIR/.test-domain" +runuser -u "$CERT_USER" -- mkdir -p "$TEST_DIR" +runuser -u "$CERT_USER" -- touch "$TEST_DIR/fullchain.pem" +runuser -u "$CERT_USER" -- touch "$TEST_DIR/privkey.pem" +rm -rf "$TEST_DIR" + +echo +echo "========== 配置完成 ==========" +echo +echo "SFTP 用户:$CERT_USER" +echo "SFTP 端口:22" +echo "SFTP 上传目录:/cert" +echo "真实目录:$CERT_DIR" +echo +echo "第三方连接命令:" +echo "sftp -o PubkeyAuthentication=no -o PreferredAuthentications=password ${CERT_USER}@服务器IP" +echo +echo "当前 ACL:" +getfacl "$CERT_DIR" + +echo +echo "重点确认应该看到:" +echo "user:${CERT_USER}:rwx" +echo "default:user:${CERT_USER}:rwx"