背景
Linux或者Mac上一般都是直接用的终端来连接SSH,基本上很少有类似Windows上用XShell之类的客户端。所以在终端上直接登录都必须输入密码,但是如果密码比较复杂就更难记住了。这时候可以通过SSH-Key来实现秘钥登录。
SSH-Key登录远程服务器
-
SSH-Key是一种基于密钥的安全认证,远程服务器持有公钥,本地持有私钥,在客户端向服务器发送请求之后,服务端在用户主目录下查找用户的公钥,然后对比用户发送过来的公钥,如果一致则用公钥加密”质询“并发送给客户端。客户端收到”质询“后用私钥解密,在发送给服务端,认证结束。
-
要实现这种方式的登录首先需要创建ssh-key:
ssh-keygen -t rsa
-
在交互界面中可以按默认的直接回车,最后会在
${USER_HOME}/.ssh/
下保存公钥和私钥文件:id_rsa
id_rsa.pub
-
然后需要将公钥保存到服务器上,执行以下命令即可将公钥发送到服务器上,需要输入登录密码
ssh-copy-id -i ~/.ssh/id_rsa.pub ${user}@${host}
-
以上命令会将公钥文件保存到服务器用户目录下的
.ssh/authorized_keys
中 -
配置完成之后便可以直接免密码登录了
ssh ${user}@${host}
其他扩展配置
-
有时候可能会需要创建多个不同的秘钥对,用于不同的服务器登陆,或者用于Github的免密操作
-
通过ssh-keygen创建新的文件,此时直接定义新的名字,比如:
ssh-keygen -t rsa -C '另一个服务器' -f ~/.ssh/my_id_rsa
-
然后同样的将公钥发送到需要登录的服务器
ssh-copy-id -i ~/.ssh/my_id_rsa ${user}@${host}
-
再在登录的时候指定私钥文件,或者通过
~/.ssh/config
自动带上指定的文件-
直接指定的方式:
ssh ${user}@${host} -i ~/.ssh/my_id_rsa
-
通过配置文件的方式:
# ~/.ssh/config # 指定某一服务器所使用的私钥文件 Host serverAlias # 服务器的别名,可以随便起一个 或者直接按ip也可以 HostName ${服务器的ip} User ${user} # 指定登录用户名 PreferredAuthentications publickey IdentityFile ~/.ssh/my_id_rsa # 指定私钥文件
-
另一种情况
- 但是也有些情况下,无法将本地的公钥发送到服务器上,比如登录客户的服务器,或者登录一个IP或者端口可能会变化的服务器,比如我使用了免费的内网穿透来连接我的树莓派,它的端口就会经常变化。
- 在这种情况下,登录的时候都得去手动输入密码了。当用户名或者密码很难记住的时候,往往会特别需要一个能够记住用户名密码的客户端。Mac下免费的客户端较少,比如Termius就是一个不错的客户端,但是不知为何它有时候会卡死。所以我选择了自己实现记住密码的方式,可以在登录时只记住一个密码,将不同服务器的密码通过加密保存,登录的时候通过输入解密秘钥来自动解密登录。
通过加密文件保存服务器密码实现自动登录
-
通过该方式需要依赖的工具如下:
openssl
expect
, 一般情况下 openssl都是自带了的,往往只需要安装一下expect -
Mac下可以直接
brew install expect
-
Ubuntu 下可以通过apt安装
sudo apt install expect
-
expect 是一种交互式的开源工具,用于实现自动化的功能
第一步创建加密方法,保存密码的密文
-
创建一个func.sh文件,内容如下
#!/bin/bash ## 加密方法 encrypt() { local content=$1 local pass=$2 cmd="echo $content | openssl enc -aes-256-cfb -a -e -pass pass:$pass -iter 12 -nosalt" echo $content | openssl enc -aes-256-cfb -a -e -pass pass:$pass -iter 12 -nosalt } ## 加密工具方法 create_encrypted_pass() { read -s -p "Enter origin password:" content echo '' read -s -p "Enter aes password:" pass echo '' encrypt $content $pass }
-
然后在终端中
source func.sh
加载方法,然后调用create_encrypted_pass
在交互界面中输入密码和加密秘钥,加密秘钥需要牢记于心,以后登录时只需要输入它即可 -
完成后会打印加密后的密文,将密文保存下来,比如保存到
${host}.pass
第二步创建解密方法,和自动登录的方法
-
在func.sh中补充解密和登录方法
## 解密方法 decrypt() { local encrypted=$1 local pass=$2 echo $encrypted | openssl enc -aes-256-cfb -a -d -pass pass:$pass -iter 12 -nosalt } ## 登录方法,输入参数有 加密文件路径,用户名,服务器host,(端口,解密秘钥【这两个可选】) ssh_target() { local pass_path=$1 local user=$2 local host=$3 local port=$4 local aes_pass=$5 if [ "$port" == "" ]; then port=22 fi encrypted=`cat $pass_path` pass=`decrypt $encrypted $aes_pass` # echo "decrypted pass is ${pass}" ./_ssh.exp $host $user $pass $port }
-
然后创建自动执行脚本,_ssh.exp,用于根据输入参数自动登录到服务器上
#!/usr/bin/expect ## 读取参数 set host [lindex $argv 0] set user [lindex $argv 1] set password [lindex $argv 2] set port [lindex $argv 3] set timeout 3000 spawn ssh -l $user $host -p $port expect { # 判断是否有记住hosts的交互信息 "(yes/no?" { send "yes\r" # 发送yes expect { "password:" { send "${password}\r" } # 发送密码 } } "password:" { send "${password}\r" } # 发送密码 } interact
-
然后只需要在创建一个针对某一服务器的登录脚本,在里面配置一些信息
-
比如 ssh_my_server.sh
source ./func.sh ## 用于加载预定义的方法 ssh_target ${host}.pass ${用户名} ${服务器host} ${端口}
-
然后对以上两个文件赋予可执行权限
-
chmod a+x ssh_my_server.sh _ssh.exp
第三步,登录服务器
- 此时要登录到服务器时,只需要执行 ssh_my_server.sh 即可
-
./ssh_my_server.sh
然后根据提示输入加密秘钥,这个秘钥牢记于心即可。一般不知道秘钥无法解密出具体的登录密码,所以是比较安全的,在脚本中也不会暴露密码信息。
额外实现
-
以上方式,每次执行
./ssh_my_server.sh
都需要输入一遍密码,有时候又觉得有些麻烦。可以稍微再改造一下,在当前终端中不再需要输入密码。实现方式是得到和终端相关的数据,用它作为加密密钥,将记在心里的那个秘钥保存下来。 -
在func.sh中增加有些方法,并修改ssh_target
## 根据终端的信息创建临时秘钥,该方法创建的秘钥只要在当前终端执行,得到的都是同样的内容 create_temp_pass() { local tty_info=`tty` tty_info=${tty_info#/dev/*} local ps_info=`ps -ef | grep $tty_info | awk 'NR==1{print $2,$3,$5,$6}'` local aes_pass=`echo $ps_info | md5` echo $aes_pass } ## 从加密文件中解密出明文密码 get_session_aes_pass() { local work_dir=`pwd` local temp_pass_dir="$work_dir/.pass" local aes_pass='' # 判断是否存在加密文件,不存在则返回空内容 if test -e $temp_pass_dir ; then local encrypted_aes_pass=`cat $temp_pass_dir` local temp_aes_pass=`create_temp_pass` # 判断密文解密的合法性,我在明文中加入了_123后缀,只有后缀匹配才能确定解密是成功的,否则解密失败返回空内容 local decrypted_aes_pass=`decrypt $encrypted_aes_pass $temp_aes_pass` if [ "${decrypted_aes_pass#*_}" == "123" ]; then aes_pass=${decrypted_aes_pass%_123} fi fi echo $aes_pass } ## 将明文密码保存到加密文件中 save_session_aes_pass() { local work_dir=`pwd` local temp_pass_dir="$work_dir/.pass" local aes_pass=$1 local temp_aes_pass=`create_temp_pass` # 在明文中加入_123后缀,然后加密到加密文件中 local encrypted_aes_pass=`encrypt "${aes_pass}_123" $temp_aes_pass` echo $encrypted_aes_pass > $temp_pass_dir } ssh_target() { local pass_path=$1 local user=$2 local host=$3 local port=$4 local aes_pass=$5 if [ "$port" == "" ]; then port=22 fi encrypted=`cat $pass_path` # 这里增加判断,如果传入的解密密码为空, if [ "$aes_pass" == "" ]; then aes_pass=`get_session_aes_pass` # 第二次判断,如果解密出的内容为空,则需要重新输入解密的秘钥 if [ "$aes_pass" == "" ]; then read -s -p 'please enter aes password:' aes_pass echo '' # 将秘钥明文加密保存 `save_session_aes_pass $aes_pass` fi fi pass=`decrypt $encrypted $aes_pass` # echo "decrypted pass is ${pass}" ../libs/_ssh.exp $host $user $pass $port }
-
然后在同一个终端中,只在第一次执行
ssh_my_server.sh
的时候需要输入密码,在后续的操作中不再需要输入密码。当重新打开一个终端时,才会要求再次输入密码。 -
当需要登录多个不同的服务器时,可以创建多个不同的ssh_my_server.sh文件,顺序分别是先创建登录密码的加密文件,然后在ssh_my_server.sh文件中配置加密文件位置和服务器登录名,host,端口等信息。
网友评论