几种方法来实现scp拷贝时无需输入密码

几种方法来实现scp拷贝时无需输入密码

2015年01月15日 09:05:04 nfer_cn 阅读数:52152 标签: scp sshpass 不须要输入密码 expect ssh-keygen 更多
我的分类: shell
 

欢迎转载!转载时请注明出处:http://blog.csdn.net/nfer_zhuang/article/details/42646849linux

前言

我在工做中常常要将一些文件传输到另一个服务器上,并且都是Linux的命令行环境,那么对于我来说scp就是最直接有效的方法了,其余诸如FTP、SMB以及Winscp这些有界面的文件传输工具到反而有些多余了。算法

使用过scp的都知道须要指定远端服务器的账号并手动输入密码,那么如何避免每次都须要输入密码这个操做呢?下面就给出两种方案进行解决。shell

方法一:创建SSH的信任关系

在这里先介绍两个概念:SSH公钥(~/.ssh/id_rsa.pub)和公钥受权文件(~/.ssh/authorized_keys),这两个文件的做用具体能够参考ssh的man手册:安全

 ~/.ssh/id_rsa.pub
         Contains the public key for authentication.  These files are not sensitive and can (but need not) be readable by anyone.

 ~/.ssh/authorized_keys
         Lists the public keys (DSA/ECDSA/RSA) that can be used for logging in as this user.  The format of this file is described in the sshd(8) manual page.  This file is not highly sensitive, but the recommended permissions are read/write for the user, and not accessible by others.
ruby

从描述中咱们能够知道,~/.ssh/id_rsa.pub文件中包含了认证的公钥信息,并且该文件能够被任何人读取;而~/.ssh/authorized_keys文件中则列举了登陆用户的公钥信息(换句话说就是使用这些公钥信息能够登陆当前设备),而为了安全考虑,该文件通常建议只有本用户能够有读写权限。bash

公钥和私钥

提到公钥对应的就会有私钥,在谈这两个概念以前咱们先了解另一组概念:加密认证服务器

  • 加密是对数据进行处理,添加保护信息,若是非法用户获得被加密过的数据,也没法获取到原始的有效数据内容,因此加密的重点在于数据的安全性。
  • 认证是对数据进行处理,添加鉴权信息,若是在传输的过程当中被非法篡改,接收方就会校验失败并丢弃该非法数据,因此认证的重点在于数据的合法性。

上面是从业务概念来上描述了加密和认证的区别,可是从具体技术实现上,认证使用的是加密中的非对称加密算法来实现鉴权和认证的操做。dom

  • 对称加密算法在加密和解密时使用的是同一个秘钥;
  • 非对称加密算法须要两个密钥来进行加密和解密,这两个秘钥是公开密钥(public key,简称公钥)和私有密钥(private key,简称私钥)。

下面就分别描述一下,采用非对称算法(即便用公钥和私钥)的加密和认证各自的过程。ssh

基于公钥和私钥的加密过程

有两个用户Alice和Bob,Alice想把一段数据加密后发送给Bob(注意这里强调的是数据的安全性),那么如何保证除了Bob以外的人即便窃取了数据也没法解密获得原始的数据?基于公钥和私钥的加密能够完成这个需求,具体流程以下:ide

  1. Bob将他的公钥发送给Alice
  2. Alice用Bob的公钥加密须要传输的数据而后发送给Bob
  3. Bob用他的私钥解密Alice的消息

 

基于公钥和私钥的认证过程

仍是Alice和Bob,Alice想把一段数据发送给Bob(注意这里并不强调数据的安全性),当Bob收到数据时如何判断该数据确实是Alic发送的且传输过程当中没有被篡改?基于公钥和私钥的认证能够完成这个需求,具体流程以下:

  1. Alice用她的私钥对数据加密(或者对于数据的哈希值进行加密做为签名)
  2. Alice将加密后的数据(或者明文数据+签名)发送给Bob
  3. Bob用Alice的公钥解密数据(或校验签名),若是解密(校验)成功则能够保证数据的发送方确定是Alice

基于公钥和私钥的信任关系

了解了公钥/私钥以及加密/认证这些概念后,咱们就能够在scp中使用公钥/私钥来创建一个信任关系,从而在数据传输时完成自动认证而无需输入密码。

  1. User在A主机将SCP请求使用本身的私钥进行加密,而后传输给B主机
  2. B主机在收到SCP请求时,使用User的公钥进行解密
  3. 若是解密成功,则表示该请求确实是User发送的请求,则容许操做;若是解密失败,则表示该请求无效,直接丢弃

因此这里的须要作的就是:

  • User在A主机上须要建立一个公私钥对
  • 在B主机上,将User在A主机的公钥加入到ssh的信任公钥列表中(即公钥受权文件)

建立公私钥对

在Linux上使用ssh-keygen工具来生成公私钥对,在man手册中关于ssh-keygen工具的说明以下:

ssh-keygen : authentication key generation, management and conversion
 -t    type
     Specifies the type    of key to create.  The possible    values are 'rsa1' for protocol version 1 and 'dsa', 'ecdsa', 'ed25519', or 'rsa' for protocol version 2.

在上面提到了ssh-keygen支持rsa一、dsa、ecdsa、ed25519和rsa这几种非对称加密算法,其中最经常使用的就是RSA和DSA,好比使用RSA算法来生成公私钥对的过程以下:

 

 
  1.  
    nfer@nfer-VirtualBox:~$ ssh-keygen -t rsa
  2.  
    Generating public/private rsa key pair.
  3.  
    Enter file in which to save the key (/home/nfer/.ssh/id_rsa):
  4.  
    Created directory '/home/nfer/.ssh'.
  5.  
    Enter passphrase (empty for no passphrase):
  6.  
    Enter same passphrase again:
  7.  
    Your identification has been saved in /home/nfer/.ssh/id_rsa.
  8.  
    Your public key has been saved in /home/nfer/.ssh/id_rsa.pub.
  9.  
    The key fingerprint is:
  10.  
    16:77:45:71:31:7c:67:4f:91:09:07:74:4d:30:83:48 nfer@nfer-VirtualBox
  11.  
    The key's randomart image is:
  12.  
    +--[ RSA 2048]----+
  13.  
    | .E..*@XO|
  14.  
    | . ..oBB|
  15.  
    | . . . o+|
  16.  
    | o . .|
  17.  
    | S |
  18.  
    | . |
  19.  
    | |
  20.  
    | |
  21.  
    | |
  22.  
    +-----------------+
  23.  
    nfer@nfer-VirtualBox:~$
注意上面有三处须要输入信息,分别是:

 

  1. 存储公私钥的文件夹位置,若是不输入,则默认为~/.ssh/,文件名则默认是id_rsa和id_rsa.pub
  2. 使用该公私钥时是否须要密码,若是不输入则表示不须要密码
  3. 再次确认是否须要密码

将User在A主机的公钥加入到ssh的信任公钥列表中

将刚才建立的~/.ssh/id_rsa.pub文件中的内容拷贝添加到B主机上的~/.ssh/authorized_keys文件中(若是没有则建立一个),这个时候就创建了一条A-->B的信任关系。注意这个信任关系是有方向性的,若是要创建从B-->A的信任关系,则操做步骤和上面的相似,只不过要反过来。

创建好信任关系后,这个时候使用任何ssh相关的工具则都无需输入远端的登录密码(若是在建立公私钥对时输入了密码,那么这个时候还须要输入这个密码才能使用公私钥对)。

方法二:使用sshpass工具来自动输入密码

其实上面的创建信任关系的作法是最方便和安全的作法,可是在有些场景下(好比远端的authorized_keys是不能随意更改的),那么这个时候咱们就能够借助sshpass这个第三方工具来完成ssh链接时的密码输入。先看一下sshpass的man手册中是如何描述的:

       sshpass - noninteractive ssh password provider

从描述上就能够清晰的了解到,sshpass的设计就是为了使用非交互的场景下输入ssh链接的密码。

sshpass的使用比较简单,先看一下帮助文档:

nfer@nfer-VirtualBox:~$ sshpass
Usage: sshpass [-f|-d|-p|-e] [-hV] command parameters
   -f filename   Take password to use from file
   -d number     Use number as file descriptor for getting password
   -p password   Provide password as argument (security unwise)
   -e            Password is passed as env-var "SSHPASS"
   With no parameters - password will be taken from stdin

   -h            Show help (this screen)
   -V            Print version information
At most one of -f, -d, -p or -e should be used

其中-p是直接指定密码,-f是从文件中读取密码。那么一个使用sshpass的简单例子就是:

 

sshpass -p nferzhuang scp a.txt nferzhuang@192.168.1.101:/home/nferzhuang/a.txt

 

使用sshpass的好处就是方便直接,无需了解公私钥、加密认证等相关知识,简单易懂;可是使用sshpass最大的坏处就是再使用时会涉及到明文密码,大大下降了安全性。

方法三:使用expect脚原本自动输入密码

expect用于自动化地执行linux环境下的命令行交互任务,例如scp、ssh之类须要用户手动输入密码而后确认的任务。有了这个工具,定义在scp过程当中可能遇到的状况,而后编写相应的处理语句,就能够自动地完成scp操做了。

下面就是一个使用expect来完成scp时无需输入密码的脚本:

 

 
  1.  
    #!/usr/bin/expect
  2.  
    set timeout 10
  3.  
    set host [lindex $argv 0]
  4.  
    set username [lindex $argv 1]
  5.  
    set password [lindex $argv 2]
  6.  
    set src_file [lindex $argv 3]
  7.  
    set dest_file [lindex $argv 4]
  8.  
    spawn scp $src_file $username@$host:$dest_file
  9.  
    expect {
  10.  
    "(yes/no)?"
  11.  
    {
  12.  
    send "yes\n"
  13.  
    expect "*assword:" { send "$password\n"}
  14.  
    }
  15.  
    "*assword:"
  16.  
    {
  17.  
    send "$password\n"
  18.  
    }
  19.  
    }
  20.  
    expect "100%"
  21.  
    expect eof

 

注意代码刚开始的第一行,指定了expect的路径,与shell脚本相同,这一句指定了程序在执行时到哪里去寻找相应的启动程序。代码刚开始还设定了timeout的时间为10秒,若是在执行scp任务时遇到了代码中没有指定的异常,则在等待10秒后该脚本的执行会自动终止。

从以上代码刚开始的几行能够看出,我为这个脚本设置了5个须要手动输入的参数,分别为:目标主机的IP、用户名、密码、本地文件路径、目标主机中的文件路径。若是将以上脚本保存为expect_scp文件,则在shell下执行时须要按如下的规范来输入命令:

./expect_scp 192.168.75.130 root 123456 /root/src_file /root/dest_file
以上的命令执行后,将把本地/root目录下的src_file文件拷贝到用户名为root,密码为123456的主机192.168.75.130中的/root下,同时还将这个源文件重命名为dest_file。

spawn表明在本地终端执行的语句,在该语句开始执行后,expect开始捕获终端的输出信息,而后作出对应的操做。expect代码中的捕获的(yes/no)内容用于完成第一次访问目标主机时保存密钥的操做。有了这一句,scp的任务减小了中断的状况。代码结尾的expect eof与spawn对应,表示捕获终端输出信息的终止。

 

使用expect须要了解的一点是:用expect速度会比较慢,由于须要等待返回的数据,而后输入命令执行,没有ssh密钥登陆的快速。

注:关于expect部分详细请参考《shell结合expect写的批量scp脚本工具

总结

在本文中提供了三种方法来实现scp的时候无需输入密码的需求,从安全性和速度上考虑创建信任关系都是最佳的方法,至于在具体的环境中选择什么则由你本身来决定。

相关文章
相关标签/搜索