欢迎转载!转载时请注明出处:http://blog.csdn.net/nfer_zhuang/article/details/42646849linux
我在工做中常常要将一些文件传输到另一个服务器上,并且都是Linux的命令行环境,那么对于我来说scp就是最直接有效的方法了,其余诸如FTP、SMB以及Winscp这些有界面的文件传输工具到反而有些多余了。算法
使用过scp的都知道须要指定远端服务器的账号并手动输入密码,那么如何避免每次都须要输入密码这个操做呢?下面就给出两种方案进行解决。shell
在这里先介绍两个概念: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
下面就分别描述一下,采用非对称算法(即便用公钥和私钥)的加密和认证各自的过程。ssh
有两个用户Alice和Bob,Alice想把一段数据加密后发送给Bob(注意这里强调的是数据的安全性),那么如何保证除了Bob以外的人即便窃取了数据也没法解密获得原始的数据?基于公钥和私钥的加密能够完成这个需求,具体流程以下:ide
仍是Alice和Bob,Alice想把一段数据发送给Bob(注意这里并不强调数据的安全性),当Bob收到数据时如何判断该数据确实是Alic发送的且传输过程当中没有被篡改?基于公钥和私钥的认证能够完成这个需求,具体流程以下:
了解了公钥/私钥以及加密/认证这些概念后,咱们就能够在scp中使用公钥/私钥来创建一个信任关系,从而在数据传输时完成自动认证而无需输入密码。
因此这里的须要作的就是:
在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算法来生成公私钥对的过程以下:
nfer@nfer-VirtualBox:~$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/nfer/.ssh/id_rsa):
Created directory '/home/nfer/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/nfer/.ssh/id_rsa.
Your public key has been saved in /home/nfer/.ssh/id_rsa.pub.
The key fingerprint is:
16:77:45:71:31:7c:67:4f:91:09:07:74:4d:30:83:48 nfer@nfer-VirtualBox
The key's randomart image is:
+--[ RSA 2048]----+
| .E..*@XO|
| . ..oBB|
| . . . o+|
| o . .|
| S |
| . |
| |
| |
| |
+-----------------+
nfer@nfer-VirtualBox:~$
将刚才建立的~/.ssh/id_rsa.pub文件中的内容拷贝添加到B主机上的~/.ssh/authorized_keys文件中(若是没有则建立一个),这个时候就创建了一条A-->B的信任关系。注意这个信任关系是有方向性的,若是要创建从B-->A的信任关系,则操做步骤和上面的相似,只不过要反过来。
创建好信任关系后,这个时候使用任何ssh相关的工具则都无需输入远端的登录密码(若是在建立公私钥对时输入了密码,那么这个时候还须要输入这个密码才能使用公私钥对)。
其实上面的创建信任关系的作法是最方便和安全的作法,可是在有些场景下(好比远端的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用于自动化地执行linux环境下的命令行交互任务,例如scp、ssh之类须要用户手动输入密码而后确认的任务。有了这个工具,定义在scp过程当中可能遇到的状况,而后编写相应的处理语句,就能够自动地完成scp操做了。
下面就是一个使用expect来完成scp时无需输入密码的脚本:
#!/usr/bin/expect
set timeout 10
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set src_file [lindex $argv 3]
set dest_file [lindex $argv 4]
spawn scp $src_file $username@$host:$dest_file
expect {
"(yes/no)?"
{
send "yes\n"
expect "*assword:" { send "$password\n"}
}
"*assword:"
{
send "$password\n"
}
}
expect "100%"
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。
使用expect须要了解的一点是:用expect速度会比较慢,由于须要等待返回的数据,而后输入命令执行,没有ssh密钥登陆的快速。
注:关于expect部分详细请参考《shell结合expect写的批量scp脚本工具》
在本文中提供了三种方法来实现scp的时候无需输入密码的需求,从安全性和速度上考虑创建信任关系都是最佳的方法,至于在具体的环境中选择什么则由你本身来决定。