服务器配置仅允许 git 用户使用 rsa 密钥登录,web 的目录及子目录和文件所有者为 www,755 权限,并且 git 已经添加到 www 组,在当前用户为 git 的情况下。
执行
rsync -avpz -e "ssh -i ~/.ssh/id_rsa -p 22" --exclude '.git' --exclude '.git/*' ./local/ git@server:/www/wwwroot/site/
提示
rsync: failed to set times on "/www/wwwroot/site/theme/images/left": Operation not permitted (1) rsync
报错的根因是:rsync 在 -a
(归档)模式下会保留时间戳(-t
),而“修改目录/文件时间戳”在 Linux 上需要“所有者”权限或特权(CAP_FOWNER
)。
远端目录/文件的 owner=www,用的是 git 用户(虽在 www
组,但不是 owner),于是在设置时间戳时就出现:
rsync: failed to set times on ".../images/left": Operation not permitted (1)
常见解决方案:
方案 A(推荐):把站点目录做成“组可写 + 继承组”
适合多人协作、持续部署,安全且不需要 root。
一步到位修正现有权限:
# 1) 目录归属给 www 组(已是则可略过)
sudo chgrp -R www /www/wwwroot
# 2) 目录:组可写 + 设置 setgid 位(2),让新建文件/目录自动继承 www 组
sudo find /www/wwwroot -type d -exec chmod 2775 {} \;
# 3) 文件:组可写
sudo find /www/wwwroot -type f -exec chmod 664 {} \;
# 4)(可选更稳)用 ACL 让默认权限继承,防止将来被 umask/工具打回去
sudo setfacl -R -m g:www:rwx /www/wwwroot
sudo setfacl -R -m d:g:www:rwx /www/wwwroot
保证 git
的 umask 不破坏组写:
# 让新建文件默认 664 / 目录 775(umask 002)
# 如果 git 通过 shell、systemd 或 CI 跑,同步设置到对应环境
umask 002
rsync 命令示例(最常用):
rsync -avz --delete \
--no-owner --no-group --chmod=Dg+w,ug+rX,Fg+w,ug+r \
-e "ssh -i ~/.ssh/id_rsa -p 22" \
./local/ git@server:/www/wwwroot/site/
说明:
--no-owner --no-group
:不去改属主属组(由目录继承www
组)。--chmod=...
:确保落地权限至少满足组可写/可执行(对目录加+x
)。--delete
:目标端多余文件清理(按需用)。- 有了目录
2775
(setgid)+ ACL,以后新文件也会自动属于 www 组且组可写,不再掉坑。
方案 B:保留 755,但用 sudo 提权并在 rsync 里改属主/权限
适合你不想改现有权限模型,但允许受控提权的场景。
- 允许
git
免密执行 rsync:
# /etc/sudoers.d/rsync (用 visudo 编辑)
git ALL=(root) NOPASSWD:/usr/bin/rsync
- rsync 命令(远端以 root 执行,落地即改属主/权限):
rsync -avz --delete \
--chown=www:www --chmod=D2755,F0644 \
--rsync-path="sudo rsync" \
-e "ssh -i ~/.ssh/id_rsa -p 22" \
./local/ git@server:/www/wwwroot/site/
说明:
--rsync-path="sudo rsync"
:让远端 rsync 以 root 跑,才能生效--chown
。--chown=www:www
:落地即属主为 www。--chmod=D2755,F0644
:目录 2755(含 setgid),文件 0644。
注意:这条路需要你信任
git
账号;请仅授予最小 sudo 权限。
额外排查/验证小抄
# 看 git 的有效身份
id git
# 逐级查看目录权限是否可写(w/x 很关键)
namei -l /www/wwwroot/site
# 看 ACL 是否就位
getfacl /www/wwwroot /www/wwwroot/site | sed -n '1,30p'
关于 --inplace
的提醒
有人会用 --inplace
来避免临时文件改名失败,但 它并不能绕过目录写权限,而且断传可能把目标文件写坏。不建议用它来“解决权限问题”。
TL;DR
- 你要么把站点目录做成 组可写 + setgid +(可选)ACL(方案 A,最稳);
- 要么在远端让 rsync 以 root 身份落地并
--chown=www:www
(方案 B)。 - 单纯把
git
加进www
组,在 755 目录上依然无法写入——因为组没有写权限,这是设计使然。
附:让远端 rsync 以 www** 身份运行**
让远端 rsync 以 www 身份跑(--rsync-path="sudo -u www rsync"
),并用 --chown
/--chmod
规范落地权限。
让 rsync 落地时就是 owner=www,这样保留时间戳完全没障碍。
- 在目标机的 sudoers 里允许 git 以 www 运行 rsync(最小权限):
# 用 visudo 创建/编辑:
/etc/sudoers.d/rsync-www
# 内容:
git ALL=(www) NOPASSWD:/usr/bin/rsync
- 本地命令这样写:
rsync -avpz --delete \
--chown=www:www --chmod=D2775,F664 \
--rsync-path="sudo -u www rsync" \
-e "ssh -i ~/.ssh/id_rsa -p 22" \
--exclude '...' \
./local/ git@server:/www/wwwroot/site/
--rsync-path="sudo -u www rsync"
:远端以 www 执行 rsync(拥有者就是 www,可设 times)。--chown=www:www
:新文件直接归属 www(与站点一致)。--chmod=D2775,F664
:目录 2775(含 setgid,继承组),文件 664,确保组可写。- 你现在的 775 目录配合 setgid(2)更利于持续协作(新建内容继续属组
www
)。