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

我有一个Ubuntu服务器,我使用SSH连接到该服务器。

我需要将文件从我的机器上传到服务器上的/var/www//var/www/中的文件由root拥有。

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

但是当我使用WinSCP复制文件时,我无法在/var/www/创建创建/修改文件,因为我正在连接的用户对/var/www/文件没有权限而且我不能在ssh会话的情况下,像我一样说sudo su

你知道我怎么处理这件事吗?

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

你是对的,使用scp时没有sudo 。 解决方法是使用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身份直接进行远程登录,但出于某种原因禁用此function,因此我强烈建议您不要这样做。

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

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

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

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

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

使用ansible的fetch模块从远程主机fetch

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

注意:

  • -i HOST,的逗号-i HOST,语法不是拼写错误。 这是在不需要库存文件的情况下使用ansible的方法。
  • -b导致服务器上的操作以root身份完成。 -b扩展为--become ,默认的--become-user是root,默认的--become-method是sudo。
  • flat=yes 复制文件,不复制通向文件的整个远程路径
  • 这些ansible模块不支持在文件路径中使用通配符。
  • 复制模块支持复制目录,但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对于管理远程服务器非常有用,因此安装它肯定会带来除部署文件之外的其他好处。

运行sudo su ,您创建的任何文件都将由root拥有,但默认情况下不能直接以root身份使用ssh或scp登录。 也不可能在scp中使用sudo,因此文件不可用。 通过声明对文件的所有权来解决此问题:

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

 sudo chown -R dimitri:dimitri /home/dimitri 

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

可能最好的方法是通过SSH使用rsync (Windows中的Cygwin / cwRsync )?

例如,要上传拥有所有者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 

请参阅: scp到sudo的远程服务器 。

快速方式:

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

如果您使用OpenSSH工具而不是PuTTY,则可以通过使用sudo在服务器上启动scp文件传输来实现此目的。 确保在本地计算机上运行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 . 

您可以使用我写过的脚本受到此主题的启发:

 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 

您可以组合使用ssh,sudo和tar来在服务器之间传输文件,而无法以root用户身份登录,也无权使用您的用户访问文件。 这有点繁琐,所以我写了一个脚本来帮助这个。 您可以在此处找到该脚本: https : //github.com/sigmunau/sudoscp

或者在这里:

 #!  /斌/庆典
 RES = 0
从= $ 1
到= $ 16
转移
转移
文件= “$ @”
如果测试-z“$ from”-o -z“$ to”-o -z“$ files”
然后
     echo“用法:$ 0(文件)*”
     echo“示例:$ 0 server1 server2 / usr / bin / myapp”
     1号出口
科幻

 read -s -p“输入密码:”sudopassword
回声“”
 temp1目录= $(mktemp的)
 TEMP2 = $(mktemp的)
 (echo“$ sudopassword”; echo“$ sudopassword”| ssh $来自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
     echo“$ from output:”>&2
     cat $ temp1>&2
     echo“”>&2
     echo“$ to output:”>&2
     cat $ temp2>&2
     RES = 1
科幻

 rm $ temp1 $ temp2
退出$ res

这是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的ASKPASSfunction,但这是另一个故事。)我们可以通过预先捕获它来获取密码到sudo并通过在子shell中执行这些操作并将管道输出子shell到ssh。 这还有一个额外的好处,就是不要在我们的交互式shell中留下包含我们密码的环境变量。

您会注意到我没有使用-p选项执行’read’来打印提示。 这是因为来自sudo的密码提示通过ssh方便地传递回交互式shell的stderr。 您可能想知道“如果sudo在我们的管道右侧的ssh内运行,它是如何执行的?” 当我们执行多个命令并将一个输出传递到另一个命令时,父shell(在本例中为交互式shell)在执行前一个命令后立即执行序列中的每个命令。 当执行管道后面的每个命令时,父shell将左侧的stdout附加(重定向)到右侧的stdin。 然后输出在通过进程时变为输入。 我们可以通过执行整个命令并在键入密码之前对进程组(Ctrl-z)进行后台处理,然后查看进程树来查看此操作。

 $ (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"` 

我们的交互式shell是PID 7168,我们的子shell是PID 7969,我们的ssh过程是PID 7970。

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

注意我只是将“remote_Host”的主机文件条目添加到我的本地机器上进行演示。