如何使用scp复制需要root访问权限的文件?


167

我有一台要使用SSH连接到的Ubuntu服务器。

我需要将机器上的文件上传到/var/www/服务器上,其中的文件/var/www/归拥有root

使用PuTTY,登录后,我必须先输入sudo su和密码,才能修改中的文件/var/www/

但是,当我使用WinSCP复制文件时,无法在中创建创建/修改文件/var/www/,因为要与之连接的用户对该文件中的文件没有权限,/var/www/因此我无法sudo su像在ssh会话中那样说。

你知道我该怎么处理吗?

如果在本地计算机上工作,我会打电话给我,gksudo nautilus但在这种情况下,我只能通过终端访问该计算机。


对于您的虚拟服务器提供商,腻子或winscp开发人员来说,这似乎更像是一个问题。
dobey 2012年

7
@dobey您显然是错误的,这与ubuntu特权有关!
Dimitris Sapikas 2012年

7
为什么关闭?这是关于使用scp复制文件的完全正确的问题-每个Web开发人员都熟悉这种情况
Sergey 2012年


我有一个类似的问题。我在Windows计算机上创建了一个文件(在这种情况下为HTML),并尝试使用WinSCP将其复制到/ var / www / html / website文件夹。它说存在权限问题。因为我可以复制到我的/ home文件夹,所以我分两个步骤复制了该文件,但它不是很方便:-)我尝试将用户添加到www-data组,但没有帮助。知道为什么将用户添加到www-data仍然不允许用户将文件复制到www-data组拥有的文件夹吗?
JanezKranjski '18

Answers:


125

您是正确的,sudo与在一起时没有任何需要scp。一种解决方法是使用scp将文件上载到用户有权创建文件的目录,然后通过ssh登录并用于sudo将文件移动/复制到最终目标。

scp -r folder/ user@server.tld:/some/folder/you/dont/need/sudo
ssh user@server.tld
 $ sudo mv /some/folder /some/folder/requiring/perms 
# YOU MAY NEED TO CHANGE THE OWNER like:
# sudo chown -R user:user folder

另一种解决方案是更改将文件上传到的目录的权限/所有权,这样您的非特权用户就可以写入这些目录。

通常,在root帐户中工作应该是一个例外,而不是一个规则-措辞问题的方式使我认为您可能在滥用它,从而导致权限问题-在正常情况下,您不需要超级管理员权限来访问您自己的文件。

从技术上讲,您可以将Ubuntu配置为直接允许远程登录为root,但是由于某种原因禁用了此功能,因此强烈建议您不要这样做。


我没有第一个解决方案,能否请您更精打细算?
Dimitris Sapikas 2012年

当我说我自己的文件时,我指的是/ var / www,我将我的vps用作网络服务器....在我自己的文件夹中,我拥有完全访问权限
Dimitris Sapikas 2012年

7
回覆。第一个解决方案。1. scp -R mysite dimitris@myserver.com:/home/dimitris/2. ssh dimitris@myserver.com3.- sudo mv ~/mysite /var/www这是一个两步过程,首先scp将文件保存到主目录,然后通过ssh登录并将文件复制/移动到应放置的位置
Sergey 2012年

36

另一种方法是使用tar + ssh而不是scp复制:

tar -c -C ./my/local/dir \
  | ssh dimitris@myserver.com "sudo tar -x --no-same-owner -C /var/www"

2
这是最好的方法。
mttdbrd 2015年

2
我无法使这种方法成功工作。照我写的sudo: sorry, you must have a tty to run sudo。如果我添加“ -t”来分配TTY,那么我得到Pseudo-terminal will not be allocated because stdin is not a terminal.。如果没有无密码的sudo,我将无法正常工作。
IBBoard

1
@IBBoard:在这里使用ssh -t 尝试解决方案:ssh -t dimitris@myserver.com "sudo tar -x --no-same-owner -C /var/www"
Alexander Bird

2
@AlexanderBird虽然在很多情况下都可以使用,但是我不确定它在这里是否可以使用,因为我们正在尝试通过SSH连接传递压缩包。见serverfault.com/questions/14389/...
IBBoard

这终于对我有用。您没有要复制到本地的远程文件的权限,请执行sudo tar,存档,使用chmod和更改权限chown,然后再将其复制到本地。特别是在目录下。
论坛用户

27

快速方式

从服务器到本地计算机:

ssh user@server "sudo cat /etc/dir/file" > /home/user/file

从本地计算机到服务器:

cat /home/user/file | ssh user@server "sudo tee -a /etc/dir/file"

5
这个答案被低估了。它简单,干净,只需一个原子操作即可读取或写入根文件,并且如果您使用的是scp,则不需要保证没有任何内容。主要缺点是它不复制权限。如果需要,tar解决方案更好。这是一项强大的技术,特别是与xargs / bash魔术结合使用以遍历路径时
。.– markgo2k

我认为问题是关于将文件从本地上传到远程,而不是相反
Korayem

1
做得漂亮。这正是我一直在寻找的东西。谢谢
杰里米

毫无疑问,应该成为最佳答案。
Andrew Watson

是否可以对目录而不是文件执行此操作?
lucidbrot

26

您也可以使用它ansible来完成此任务。

使用Ansible的copy模块复制到远程主机:

ansible -i HOST, -b -m copy -a "src=SRC_FILEPATH dest=DEST_FILEPATH" all

使用Ansible的fetch模块从远程主机获取:

ansible -i HOST, -b -m fetch -a "src=SRC_FILEPATH dest=DEST_FILEPATH flat=yes" all

注意:

  • -i HOST,语法中的逗号不是错字。这是无需库存文件即可使用ansible的方法。
  • -b使服务器上的操作以root身份完成。-b扩展为--become,默认--become-user值为root,默认值为--become-methodsudo。
  • flat=yes的文件,不会复制整个远程路径通往文件
  • 这些可访问模块不支持在文件路径中使用通配符。
  • 复制目录由支持copy模块,而不是fetch模块。

此问题的特定调用

这是一个具体且完整指定的示例,假定本地主机上包含要分发的文件的目录为sourcedir,并且远程目标的主机名为hostname

cd sourcedir && \
ansible \
   --inventory-file hostname, \ 
   --become \
   --become-method sudo \
   --become-user root \
   --module-name copy \
   --args "src=. dest=/var/www/" \
   all

简洁的调用是:

cd sourcedir && \
ansible -i hostname, -b -m copy -a "src=. dest=/var/www/" all

PS,我意识到说“只安装这个很棒的工具”是一个聋哑人的答案。但我发现ansible是超级用于管理远程服务器有用的,所以在安装它必将为你带来超越部署文件的其他好处。


我喜欢这个答案,但我建议您将其直接针对所提出的问题,而不是更普遍的评论。像ansible -i "hostname," all -u user --become -m copy -a ...
Mike D

@MikeD:以上更改看起来如何?
erik.weathers 16/02/19

1
-i 'host,'是有效的语法吗?我认为在阅读命令时很容易丢失标点符号。(对于读者,我的意思是,如果不是外壳的话。)
mwfearnley

1
@mwfearnley:当然,shell将-i 'host,'-i host,或一样对待-i "host,"。总的来说,我宁愿使这些调用尽可能短,以免使它们望而却步,但是您应该随心所欲地将其变得冗长而明确,因为您认为这是必要的。
erik.weathers

2
在框外思考的方式!大量使用Ansible
jonatan

12

运行时sudo su,您创建的任何文件都将由root拥有,但是默认情况下,不可能直接使用ssh或scp作为root登录。也不能将sudo与scp一起使用,因此这些文件不可用。通过声明文件所有权来解决此问题:

假设您的用户名是dimitri,则可以使用此命令。

sudo chown -R dimitri:dimitri /home/dimitri

从那时起,如其他答案所述,“ Ubuntu”方法是使用sudo,而不是root登录。这是一个有用的范例,具有很大的安全优势。


我以任何方式使用此解决方案,但是如果我可以完全访问自己的文件系统该怎么办,我不想sudo chow ...为每个目录键入内容:S
Dimitris Sapikas

2
不建议将所有系统文件的所有权更改给用户以方便传递。它允许您可能遇到的任何用户空间错误严重危害系统的安全性。最好更改您需要由SCP更改或更新的文件的所有权,而让其他所有内容都由root拥有(如理应如此)。就是说,-Rin chown告诉它更改该目录的所有权以及所有子文件和目录的递归...因此您可以执行任何您喜欢的事情。
trognanders,2012年

嗯..看起来很好,谢谢!抱歉,我无法投票(系统不允许我这样做...)
Dimitris Sapikas 2012年

11

也许最好的方法是通过SSH 使用rsync(在Windows中为Cygwin / cwRsync)?

例如,使用owner上传文件www-data

rsync -a --rsync-path="sudo -u www-data rsync" path_to_local_data/ login@srv01.example.com:/var/www

在您的情况下,如果您需要root特权,则命令将如下所示:

rsync -a --rsync-path="sudo rsync" path_to_local_data/ login@srv01.example.com:/var/www

请参阅:使用sudo scp到远程服务器


5

如果使用OpenSSH工具而不是PuTTY,则可以通过scp使用在服务器上启动文件传输来完成此操作sudo。确保sshd本地计算机上正在运行守护程序。有了它,ssh -R您可以为服务器提供一种联系您的机器的方式。

在您的机器上:

ssh -R 11111:localhost:22 REMOTE_USERNAME@SERVERNAME

除了在服务器上登录之外,这还将转发在服务器端口11111上进行的每个连接到计算机的端口22:您sshd正在侦听的端口。

在服务器上,如下所示开始文件传输:

cd /var/www/
sudo scp -P 11111 -r LOCAL_USERNAME@localhost:FOLDERNAME .

1

您可能会使用受此主题启发而编写的脚本:

touch /tmp/justtest && scpassudo /tmp/justtest remoteuser@ssh.superserver.com:/tmp/

但这需要一些疯狂的东西(顺便说一句,由脚本自动完成)

  1. 与源计算机建立ssh连接时,发送到该文件的服务器将不再要求输入密码
  2. 由于服务器上缺少sudo提示的必要性,sudo将不再为用户要求在远程计算机上输入密码

脚本如下:

interface=wlan0
if [[ $# -ge 3 ]]; then interface=$3; fi
thisIP=$(ifconfig | grep $interface -b1 | tail -n1 | egrep -o '[0-9.]{4,}' -m1 | head -n 1)
thisUser=$(whoami)
localFilePath=/tmp/justfortest
destIP=192.168.0.2
destUser=silesia
#dest 
#destFolderOnRemoteMachine=/opt/glassfish/glassfish/
#destFolderOnRemoteMachine=/tmp/

if [[ $# -eq 0 ]]; then 
echo -e "Send file to remote server to locatoin where root permision is needed.\n\tusage: $0 local_filename [username@](ip|host):(remote_folder/|remote_filename) [optionalInterface=wlan0]"
echo -e "Example: \n\ttouch /tmp/justtest &&\n\t $0 /tmp/justtest remoteuser@ssh.superserver.com:/tmp/ "
exit 1
fi

localFilePath=$1

test -e $localFilePath 

destString=$2
usernameAndHost=$(echo $destString | cut -f1 -d':')

if [[ "$usernameAndHost" == *"@"* ]]; then
destUser=$(echo $usernameAndHost | cut -f1 -d'@')
destIP=$(echo $usernameAndHost | cut -f2 -d'@')
else
destIP=$usernameAndHost
destUser=$thisUser
fi

destFolderOnRemoteMachine=$(echo $destString | cut -f2 -d':')

set -e #stop script if there is even single error

echo 'First step: we need to be able to execute scp without any user interaction'
echo 'generating public key on machine, which will receive file'
ssh $destUser@$destIP 'test -e ~/.ssh/id_rsa.pub -a -e ~/.ssh/id_rsa || ssh-keygen -t rsa'
echo 'Done'

echo 'Second step: download public key from remote machine to this machine so this machine allows remote machine (this one receiveing file) to login without asking for password'

key=$(ssh $destUser@$destIP 'cat ~/.ssh/id_rsa.pub')
if ! grep "$key" ~/.ssh/authorized_keys; then
echo $key >> ~/.ssh/authorized_keys
echo 'Added key to authorized hosts'
else
echo "Key already exists in authorized keys"
fi

echo "We will want to execute sudo command remotely, which means turning off asking for password"
echo 'This can be done by this tutorial http://stackoverflow.com/a/10310407/781312'
echo 'This you have to do manually: '
echo -e "execute in new terminal: \n\tssh $destUser:$destIP\nPress enter when ready"
read 
echo 'run there sudo visudo'
read
echo 'change '
echo '    %sudo   ALL=(ALL:ALL) ALL'
echo 'to'
echo '    %sudo   ALL=(ALL:ALL) NOPASSWD: ALL'
echo "After this step you will be done."
read

listOfFiles=$(ssh $destUser@$destIP "sudo ls -a")

if [[ "$listOfFiles" != "" ]]; then 
echo "Sending by executing command, in fact, receiving, file on remote machine"
echo 'Note that this command (due to " instead of '', see man bash | less -p''quotes'') is filled with values from local machine'
echo -e "Executing \n\t""identy=~/.ssh/id_rsa; sudo scp -i \$identy $(whoami)@$thisIP:$(readlink -f $localFilePath) $destFolderOnRemoteMachine"" \non remote machine"
ssh $destUser@$destIP "identy=~/.ssh/id_rsa; sudo scp -i \$identy $(whoami)@$thisIP:$(readlink -f $localFilePath) $destFolderOnRemoteMachine"
ssh $destUser@$destIP "ls ${destFolderOnRemoteMachine%\\\\n}/$(basename $localFilePath)"
if [[ ! "$?" -eq 0 ]]; then echo "errror in validating"; else echo -e "SUCCESS! Successfully sent\n\t$localFilePath \nto \n\t$destString\nFind more at http://arzoxadi.tk"; fi
else
echo "something went wrong with executing sudo on remote host, failure"

fi
ENDOFSCRIPT
) | sudo tee /usr/bin/scpassudo && chmod +x /usr/bin/scpassudo

@Braiam是的,当然,抱歉,链接很长,所以脚本很长,这就是原因:)
test30 2013年

1

您可以结合使用ssh,sudo和tar等在服务器之间传输文件,而无需以root用户身份登录并且没有用户权限来访问文件。这有点奇怪,所以我写了一个脚本来帮助这个。您可以在这里找到脚本:https : //github.com/sigmunau/sudoscp

或在这里:

#!/ bin / bash
res = 0
从= $ 1
至= $ 2
转移
转移
files =“ $ @”
如果测试-z“ $ from” -o -z“ $ to” -o -z“ $ files”
然后
    echo“用法:$ 0(文件)*”
    回声“示例:$ 0 server1 server2 / usr / bin / myapp”
    1号出口
科幻

读取-s -p“输入密码:” sudopassword
回声“”
temp1 = $(mktemp)
temp2 = $(mktemp)
(echo“ $ sudopassword”; echo“ $ sudopassword” | ssh $ from sudo -S tar c -P -C / $ files 2> $ temp1)| ssh $ to sudo -S tar x -v -P -C / 2 > $ temp2
sourceres = $ {PIPESTATUS [0]}
如果[$?-ne 0 -o $ sourceres -ne 0]
然后
    回声“失败!” >&2
    回显“ $从输出:”>&2
    cat $ temp1>&2
    回声“”>&2
    回显“ $到输出:”>&2
    cat $ temp2>&2
    res = 1
科幻

rm $ temp1 $ temp2
退出$ res

欢迎来到Ask Ubuntu。您能否在回答中包含脚本?我知道这不太可能,但是如果github仓库被删除或URL被更改,那么答案将是无效的。最好直接包含该脚本,并保留github repo作为源。
Michael Lindman

0

这是Willie Wheeler答案的修改版本,该答案通过tar传输文件,但还支持将密码传递给远程主机上的sudo。

(stty -echo; read passwd; stty echo; echo $passwd; tar -cz foo.*) \
  | ssh remote_host "sudo -S bash -c \"tar -C /var/www/ -xz; echo\""

一点点额外的魔力是sudo的-S选项。在sudo手册页中:

-S,--stdin将提示写入标准错误,并从标准输入中读取密码,而不使用终端设备。密码后必须带有换行符。

现在,我们实际上希望将tar的输出通过管道传递到ssh,并将ssh的stdin重定向到tar的stdout,从而消除了将密码从交互式终端传递到sudo的任何方法。(我们可以在远端使用sudo的ASKPASS功能,但这是另一回事。)我们可以通过预先捕获密码并将其放在tar输出中(通过在子shell中执行这些操作并将其输出管道化)来将密码输入sudo中。将subshel​​l放入ssh。这还具有一个额外的优点,即不会在交互外壳中保留包含悬空密码的环境变量。

您会注意到我没有使用-p选项执行“读取”来显示提示。这是因为来自sudo的密码提示已通过ssh方便地传递回了交互式shell的stderr。您可能会想:“鉴于sudo在管道右侧的ssh中运行,sudo如何执行?” 当我们执行多个命令并将一个命令的输出传递到另一个命令时,父外壳程序(在本例中为交互式外壳程序)将在执行前一个命令后立即按顺序执行每个命令。在执行管道后面的每个命令时,父外壳程序会将左侧的stdout附加(重定向)到右侧的stdin。然后,在通过过程时,输出变为输入。

$ (stty -echo; read passwd; stty echo; echo $passwd; tar -cz foo.*) | ssh 
remote_host "sudo -S bash -c \"tar -C /var/www/ -xz; echo\""
[sudo] password for bruce: 
[1]+  Stopped                 ( stty -echo; read passwd; stty echo; echo 
$passwd; tar -cz foo.* ) | ssh remote_host "sudo -S bash -c \"tar -C 
/var/www/ -xz; echo\""

$ pstree -lap $$
bash,7168
  ├─bash,7969
  ├─pstree,7972 -lap 7168
  └─ssh,7970 remote_host sudo -S bash -c "tar -C /var/www/ -xz; echo"`

我们的交互式外壳是PID 7168,子外壳是PID 7969,而ssh进程是PID 7970。

唯一的缺点是read会在sudo有时间将其提示发送回之前接受输入。在快速连接和快速远程主机上,您不会注意到这一点,但如果两者都很慢,则可能会发现这一点。任何延迟都不会影响输入提示的能力;它可能会在您开始键入后出现。

注意,我只是将“ remote_Host”的主机文件条目添加到了用于演示的本地计算机上。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.