SFTP搭建-自动检测上传文件修改权限-rsync 自动同步到其他服务器

配置SFTP服务

bash 复制代码
##sftp10.12.76.20
useradd sftps20
useradd sftpg20

# 设置密码
passwd sftps20 
# xxxxxxxxx

passwd sftpg20
# xxxxxxxx

mkdir -p /data/sftp/sftps20
mkdir -p /data/sftp/sftpg20

chmod 755 /data/sftp

chown sftps20:sftps20 /data/sftp/sftps20
chown sftpg20:sftpg20 /data/sftp/sftpg20

chmod 775 /data/sftp/sftps20 
chmod 775 /data/sftp/sftpg20

 cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.250805
 vi /etc/ssh/sshd_config

# 找到并注释掉原SFTP子系统配置
# Subsystem      sftp    /usr/libexec/openssh/sftp-server  # CentOS
# Subsystem      sftp    /usr/lib/openssh/sftp-server      # Ubuntu

# 添加新的SFTP配置(使用内置模块)
Subsystem       sftp    internal-sftp

# 添加用户限制配置(放在文件末尾)
# 限制特定用户(如sftpuser)仅能使用SFTP,不能SSH登录
Match User sftps20,sftpg20
    ChrootDirectory /data/sftp
    ForceCommand internal-sftp   
    AllowTcpForwarding no
    X11Forwarding no
 
sshd -t
systemctl restart sshd

验证 SFTP 服务

bash 复制代码
sftp sftps20@localhost
put test.txt /upload/
exit

安装inotify-tools

bash 复制代码
cd /tmp/
mv inotify-tools-3.13.tar.gz /data/
cd /data/
tar -zxvf inotify-tools-3.13.tar.gz 
cd inotify-tools-3.13/
./configure --prefix=/usr/local
cd  /usr/local/
cd /data/inotify-tools-3.13/
make 
make install
inotifywait -h

自动修改权限脚本

bash 复制代码
cd /data/zz/
vi sftpfilechmod.sh 
bash 复制代码
#!/bin/bash
#SFTP file chmod 
#MagicConch
#CreateAt:ember.zhang
#CreateTime:2025-08-05
# 配置:SFTP上传目录 
WATCH_DIR="/data/sftp"

# 配置:日志文件路径
LOG_FILE="/var/log/sftp_permissions.log"

# 检查监控目录是否存在
if [ ! -d "$WATCH_DIR" ]; then
    echo "错误:监控目录 $WATCH_DIR 不存在!"
    exit 1
fi

# 创建日志文件(如果不存在)
if [ ! -f "$LOG_FILE" ]; then
    touch "$LOG_FILE"
    chmod 644 "$LOG_FILE"
fi

# 记录启动信息
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 启动SFTP权限监控脚本,监控目录:$WATCH_DIR" >> "$LOG_FILE"

# 监控目录事件:
# - close_write:文件写入完成(确保文件上传完整)
# - create:目录创建事件
# - moved_to:文件/目录被移动到监控目录(处理通过移动方式上传的内容)
inotifywait -m -r -e close_write,create,moved_to --format "%e %w%f" "$WATCH_DIR" | while read EVENT FILE; do
    # 处理文件
    if [ -f "$FILE" ]; then
        # 设置文件权限为755
        chmod 755 "$FILE"
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 处理文件:$FILE,事件:$EVENT,设置权限为775" >> "$LOG_FILE"
    
    # 处理目录
    elif [ -d "$FILE" ]; then
        # 设置目录权限为755
        chmod 755 "$FILE"
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 处理目录:$FILE,事件:$EVENT,设置权限为775" >> "$LOG_FILE"
    fi
done

封装服务/etc/systemd/system/sftp-permissions.service

bash 复制代码
chmod 775 sftpfilechmod.sh 

# 后台运行脚本
# sudo nohup /data/zz/sftpfilechmod.sh &

# 设置开机自启(适用于systemd系统)
 tee /etc/systemd/system/sftp-permissions.service <<EOF
[Unit]
Description=SFTP Upload Permissions Controller
After=network.target

[Service]
Type=simple
ExecStart=/data/zz/sftpfilechmod.sh
Restart=always
User=root

[Install]
WantedBy=multi-user.target
EOF

# 启用并启动服务
systemctl daemon-reload
systemctl enable sftp-permissions
systemctl start sftp-permissions

自动同步远端服务器脚本

bash 复制代码
vi /data/zz/zsync.sh
bash 复制代码
#!/bin/bash
#sync
#MagicConch
#CreateAt:ember.zhang
#CreateTime:2025-09-03 
#########################################################################

# -------------------------- 1. 配置参数(需用户根据实际环境修改!)--------------------------
MONITOR_DIR="/data/sftp/sftpn21"                  # 本地监控根目录 
REMOTE_RSYNC_USER="rsync"            # 远端rsync服务端配置的用户名
REMOTE_IP="10.12.8.37"               # 远端服务器IP 
REMOTE_RSYNC_MODULE="lotus_nexchip"        # 远端rsync服务端配置的模块名 
PASSWORD_FILE="/etc/rsyncd.pass"     # rsync密码文件路径 
LOG_DIR="/var/log/sftp_sync"         # 日志存储目录
SYNC_DELETE="false"                  # 是否同步删除(false时删除操作仅记录日志)
EXCLUDE_TMP_FILES="true"             # 是否过滤临时文件 

# -------------------------- 2. 依赖检查(自动检测,无需修改)--------------------------
check_dependency() {
    local cmd=$1
    if ! command -v $cmd &> /dev/null; then
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 错误:未安装依赖工具 $cmd,请先执行:yum install -y $cmd"
        exit 1
    fi
}
# 检查核心依赖
check_dependency "inotifywait"
check_dependency "rsync"

# -------------------------- 3. 前置检查(避免同步失败,无需修改)--------------------------
# 检查监控目录是否存在
if [ ! -d "$MONITOR_DIR" ]; then
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 错误:本地监控目录 $MONITOR_DIR 不存在,请先创建!"
    exit 1
fi

# 检查密码文件是否存在且权限正确(rsync要求密码文件权限必须为600,否则报错)
if [ ! -f "$PASSWORD_FILE" ]; then
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 错误:rsync密码文件 $PASSWORD_FILE 不存在,请先创建!"
    exit 1
fi
if [ "$(stat -c %a "$PASSWORD_FILE")" -ne 600 ]; then
    chmod 600 "$PASSWORD_FILE"
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 提示:已自动将密码文件 $PASSWORD_FILE 权限设置为600(rsync要求)"
fi

# 检查远端rsync服务是否可连接(避免启动后无法同步)
check_remote_rsync() {
    # 通过rsync命令测试远端模块是否可达(--list-only仅列目录,不实际同步)
    rsync -avz --list-only "${REMOTE_RSYNC_USER}@${REMOTE_IP}::${REMOTE_RSYNC_MODULE}/" \
          --password-file="${PASSWORD_FILE}" &> /dev/null
    if [ $? -ne 0 ]; then
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 错误:远端rsync服务不可达!请检查:"
        echo "1. 远端IP(${REMOTE_IP})是否正确;2. 远端rsync模块(${REMOTE_RSYNC_MODULE})是否存在;"
        echo "3. 密码文件(${PASSWORD_FILE})中的密码是否正确;4. 远端rsync服务是否已启动"
        exit 1
    fi
}
check_remote_rsync

# 创建日志目录(不存在则创建)
if [ ! -d "$LOG_DIR" ]; then
    mkdir -p "$LOG_DIR"
    chmod 755 "$LOG_DIR"
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 提示:已自动创建日志目录 $LOG_DIR"
fi

# 日志文件(按日期拆分,避免单文件过大)
LOG_FILE="${LOG_DIR}/sftp_sync_daemon_$(date +%Y%m%d).log"

# -------------------------- 4. 日志函数(无需修改)--------------------------
write_log() {
    local level=$1    # 日志级别(INFO/ERROR/WARN)
    local message=$2  # 日志内容
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] [${level}] ${message}" >> "$LOG_FILE"
    # 终端也打印关键日志(方便调试)
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] [${level}] ${message}"
}

# -------------------------- 5. 同步函数(核心:同步触发的文件+目录)--------------------------
sync_file_or_dir() {
    local event=$1    # 触发事件(如CREATE/MODIFY/MOVED_TO/DELETE)
    local path=$2     # 触发事件的文件/目录路径

    # 逻辑1:删除事件处理(SYNC_DELETE=false时仅记录日志)
    if [ "$SYNC_DELETE" = "false" ] && [[ "$event" =~ DELETE ]]; then
        local target_type=$(if [ -d "$path" ]; then echo "目录"; else echo "文件"; fi)
        write_log "INFO" "检测到${target_type}删除操作(SYNC_DELETE=false),仅记录日志:事件=${event},路径=${path}"
        return 0
    fi

    # 逻辑2:过滤临时文件(仅过滤文件,不影响目录)
    if [ "$EXCLUDE_TMP_FILES" = "true" ] && [ -f "$path" ]; then
        if [[ "$path" =~ \.(swp|tmp|swx|bak|~)$ ]]; then
            write_log "INFO" "跳过临时文件:${path}(事件:${event})"
            return 0
        fi
    fi

    # 核心:区分文件/目录,计算相对路径和远端目标
    local target_type=$(if [ -d "$path" ]; then echo "目录"; else echo "文件"; fi)
    local relative_path="${path#$MONITOR_DIR/}"  # 本地相对路径(保留目录结构)
    local remote_target="${REMOTE_RSYNC_USER}@${REMOTE_IP}::${REMOTE_RSYNC_MODULE}/${relative_path}"

    # 构建rsync参数(目录需保留-r递归,文件无需;统一保留权限/时间配置)
    local rsync_args="-avz --progress --no-group --no-owner --no-times --no-perms"
    # 仅当SYNC_DELETE=true且为删除事件时,添加--delete(删除远端对应文件/目录)
    if [ "$SYNC_DELETE" = "true" ] && [[ "$event" =~ DELETE ]]; then
        rsync_args="${rsync_args} --delete"
        write_log "WARN" "开启同步删除模式,将删除远端${target_type}:${remote_target}"
    fi

    # 执行同步(文件/目录通用逻辑,rsync自动适配)
    write_log "INFO" "开始同步${target_type}:事件=${event},本地路径=${path}"
    write_log "INFO" "执行命令:rsync ${rsync_args} '${path}' '${remote_target}' --password-file='${PASSWORD_FILE}'"
    
    # 捕获同步日志(stdout+stderr)
    rsync ${rsync_args} "${path}" "${remote_target}" \
          --password-file="${PASSWORD_FILE}" &>> "$LOG_FILE"

    # 判断同步结果
    if [ $? -eq 0 ]; then
        write_log "INFO" "${target_type}同步成功:远端目标=${remote_target}"
    else
        write_log "ERROR" "${target_type}同步失败:请查看日志文件 ${LOG_FILE} 中的详细错误信息"
    fi
}

# -------------------------- 6. 监控主逻辑(保留目录事件,监控文件+目录)--------------------------
write_log "INFO" "==================== SFTP同步服务启动 ===================="
write_log "INFO" "本地监控目录:${MONITOR_DIR}"
write_log "INFO" "远端rsync模块:${REMOTE_RSYNC_USER}@${REMOTE_IP}::${REMOTE_RSYNC_MODULE}/"
write_log "INFO" "密码文件路径:${PASSWORD_FILE}"
write_log "INFO" "日志文件路径:${LOG_FILE}"
write_log "INFO" "同步模式:同步监控到的文件(创建/修改)和目录(创建/修改)"
write_log "INFO" "同步删除模式:$(if [ "$SYNC_DELETE" = "true" ]; then echo "开启"; else echo "关闭(删除操作仅记录日志)"; fi)"
write_log "INFO" "---------------------------------------------------------"

# 启动inotifywait监控(保留目录事件,不排除任何文件/目录)
inotifywait -mrq \
    --format '%e %w%f' \
    -e create,modify,moved_to,delete \
    "$MONITOR_DIR" | while read -r event path; do

    # 调用同步函数(处理文件+目录事件)
    sync_file_or_dir "$event" "$path"
done
 

封装服务/usr/lib/systemd/system/sftprsync.service

bash 复制代码
vi /usr/lib/systemd/system/sftprsync.service
bash 复制代码
[Unit]
# 服务描述(自定义,便于识别)
Description=SFTP Directory Sync Service  
# 服务依赖:网络启动后、fs 服务启动后再启动本服务(确保网络和依赖可用)
After=network.target local-fs.target
# 服务文档(可选,指向命令手册)
#Documentation=man:inotifywait(1) man:rsync(1)

[Service]
# 服务类型:simple(前台运行,适合持续监控的脚本)
Type=simple
# 执行服务的用户/组(建议用 root,避免目录、密码文件权限不足)
User=root
Group=root
# 工作目录(脚本所在目录,避免相对路径问题)
WorkingDirectory=/data/zz
# 核心:服务启动命令(脚本绝对路径,必须正确)
ExecStart=/data/zz/zsync.sh
# 服务意外退出时自动重启(确保稳定性,如脚本崩溃后恢复)
Restart=always
# 重启间隔(意外退出后,5秒再重启,避免频繁重启)
RestartSec=5
# 停止服务时,杀死所有子进程(避免 inotifywait 残留)
KillMode=control-group
# 输出重定向到 journalctl(可通过 journalctl 查看服务运行日志)
StandardOutput=journal
StandardError=journal

[Install]
# 服务安装目标:多用户模式下开机自启(适配服务器环境)
WantedBy=multi-user.target
相关推荐
山沐与山2 小时前
【Docker】Docker容器技术详解
运维·docker·容器
沉醉不知处2 小时前
远程连接虚拟机,设置网络后,ip不变
服务器·网络·tcp/ip
梦想的旅途22 小时前
探索界面自动化技术在企业微信外部群管理中的应用场景与实现思路
运维·自动化·企业微信
咕噜签名-铁蛋2 小时前
云服务器高速网络架构设计与实践
服务器
咕噜企业分发小米2 小时前
阿里云与华为云在基因测序数据存储上哪个更好?
服务器·阿里云·腾讯云
wanhengidc2 小时前
巨 椰 云手机 性能稳定
运维·服务器·arm开发·智能手机·云计算
爱尔兰极光2 小时前
计算机网络--数据链路层
服务器·网络·计算机网络
巴拉巴拉~~2 小时前
KMP 算法通用步进器组件:KmpStepperWidget 横向 / 纵向 + 匹配进度 + 全样式自定义
java·服务器·开发语言
weixin_307779132 小时前
赋能插件,驱动图表:Jenkins ECharts API插件详解
运维·开发语言·自动化·jenkins·echarts