SSH远程管理

一、SSH基本概念

1.什么是ssh

SSH是一个安全协议,在进行数据传输时,会对数据包进行加密处理,加密后在进行数据传输。确保了数据传输安全远程连接的类型:

Linux:

  • ssh 端口:22
  • telnet 端口:23

windows

  • rdp 端口:3389

2.SSH与Telnet区别

服务连接方式 服务数据传输 服务监听端口 服务登录用户
ssh 加密 22/tcp 默认支持root用户登录
telnet 明文 23/tcp 不支持root用户登录

3.抓包演示

SSH数据加密传输

Telnet数据明文传输

二、ssh相关命令及选项

## 注意:不加用户@,默认使用当前登录的用户

ssh:远程连接Linux服务器
-p:port指定端口
## 不连接上服务器,直接执行命令
ssh root@10.0.0.41 'ls /'

scp:远程拷贝数据(写在前面的是源文件)
-r:递归(远程拷贝目录)
-p:拷贝的时候保持属性
-P:指定端口

推:scp sersync2.5.4 root@172.16.1.7:/root
拉:scp root@172.16.1.7:/root/sersync2.5.4 /opt
[root@backup ~]# scp -P 22 root@172.16.1.7:/root/sersync2.5.4.tar.gz 

# 结论:
1.scp通过ssh协议加密方式进行文件或目录拷贝。
2.scp连接时的用户作为为拷贝文件或目录的权限。(-p保持文件属性)
3.scp支持数据推送和拉取,每次都是全量拷贝,效率较低。

三、ssh免密连接

ssh秘钥对认证流程

#### ssh-keygen:生成密钥对

[root@m01 ~]# ssh-keygen
Generating public/private rsa key pair.

## 将秘钥保存到文件中,可以指定其他路径(直接回车)
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.

## 给密钥对设置密码,不需要设置(直接回车)
Enter passphrase (empty for no passphrase):

## 重复输入设置的密码(直接回车)
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:3jXcLjP2VzFk55xY7lYGdohVQSSxxBJLDJE9MJIUWbU root@m01
The key's randomart image is:
+---[RSA 2048]----+
| .+=*O+o*=*o|
| o..o==.*+o|
| Eoo*=o|
| . o ==|
| S + o.+|
| . . . o o.|
| . . = o .|
| . = .|
| .. |
+----[SHA256]-----+

## 生成后的密钥对
[root@m01 ~]# ll /root/.ssh/
-rw------- 1 root root 1679 May 23 11:29 id_rsa
-rw-r--r-- 1 root root 390 May 23 11:29 id_rsa.pub

#### ssh-copy-id:发送公钥
-i:指定公钥位置
[root@m01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.16.1.7

#### .ssh目录中的know_host作用
[root@m01 ~]# cat ~/.ssh/known_hosts
记录连接过的服务器,如果没有连接过(第一次连接),需要输入yes

#### 生成密钥对命令:ssh-keygen
1.在当前用户的家目录下创建了一个隐藏目录 .ssh     mkdir ~/.ssh
2.将密钥对存放目录 .ssh 授权为 700              chmod 700 ~/.ssh
3.将公钥内容写入 ~/.ssh/id_rsa.pub 文件中
4.将私钥内容写入 ~/.ssh/id_rsa 文件中
5.将私钥文件授权为 600 chmod 600 ~/.ssh/id_rsa

#### 发送公钥:ssh-copy-id
[root@web01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.16.1.5
输入yes
输入密码:1
1.在远端的指定用户的家目录下创建了一个隐藏目录.ssh ssh root@172.16.1.5 'mkdir ~/.ssh'
2.将密钥对存放目录 .ssh 授权为 700 ssh root@172.16.1.5 'chmod 700 ~/.ssh'
3.先在远端~/.ssh目录下创建文件authorized_keys ssh root@172.16.1.5 'touch
~/.ssh/authorized_keys'
4.将authorized_keys文件授权为600 ssh root@172.16.1.5 'chmod 600
~/.ssh/authorized_keys'
5.将公钥内容,保存到authorized_keys文件中
ssh root@172.16.1.5 'echo ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQCgW5rliyT3oan+r4aXuOj0D0fLEFSGNJXZd5JMDYWX2pWhC5kITeVS66+EvXM
5OvqFfr40IQcMrZym0DXk7Zqf8brgWoqxR8AZR9tqAFwV3qTb9T/QB95OM16Y+N14oUOvWCzB9rQo54QU/INPgfhaRy
Sk4YOgv+oL2aXdD6qbIGAikPzayvh9hWFjvy/LeFUGLheYYlYTqVM7WgeNrTlmbpjG4QO/IYe/xWESbt20l70yTIrD/
XWSZ2lsxSWQf0KXERhshRGhNkJF87hhS2cJozOHjzhjaPjDvgycjpYajQWf4gERe3sTL9fji/bhf8bZ3dCLp4uK/e5P
6CRpgo9V root@web01' >> ~/.ssh/authorized_keys'

免密使用场景

1.批量查看服务器信息

#!/bin/bash
[ $# -ne 1 ] && echo "请输入执行的命令" && exit 1

for i in 5 7 8 31 41
do
    echo "#########172.16.1.$i#####"
    ssh root@172.16.1.$i "$1"
done

2.跳板机

[root@m01 ~]# cat jump.sh
#!/bin/bash
#jumpserver
lb01=172.16.1.5
lb02=172.16.1.6
web01=172.16.1.7
web02=172.16.1.8
web03=172.16.1.9
nfs=172.16.1.31
backup=172.16.1.41
db01=172.16.1.51
m01=172.16.1.61
zabbix=172.16.1.71
menu(){
        cat <<-EOF
        +-------------------------+
        | 1) lb01 |
        | 2) lb02 |
        | 3) web01 |
        | 4) web02 |
        | 5) web03 |
        | 6) nfs |
        | 7) backup |
        | 8) db01 |
        | 9) m01 |
        | 10) zabbix |
        | h) help |
        +-------------------------+
EOF
}
#菜单函数
menu
#连接函数
connect(){
    ping -c 1 -w 1 $1 &>/dev/null
    if [ $? -eq 0 ];then
      ssh root@$1
    else
      echo -e "\033[5;4;40;31m 别连了,我的哥,$2:$1机器都没开!!!\033[0m"
    fi
}

#控制不让输入ctrl+c,z
trap "" HUP INT TSTP
while true
do
    read -p "请输入要连接的主机编号:" num
    case $num in
        1|lb01)
          connect $lb01 lb01
                ;;
        2|lb02)
          connect $lb02 lb02
                ;;
        3|web01)
          connect $web01 web01
                ;;
        4|web02)
          connect $web02 web02
                ;;
        5|web03)
          connect $web03 web03
                ;;
        6|nfs)
          connect $nfs nfs
                ;;
        7|backup)
          connect $backup backup
                ;;
        8|db01)
          connect $db01 db01
                ;;
        9|m01)
          connect $m01 m01
                ;;
        10|zabbix)
          connect $zabbix zabbix
                ;;
        h|help)
                clear
                menu
                ;;
        close)
                break
                ;;
    esac
done

四、SSH安全优化

## ssh配置文件
[root@m01 ~]# vim /etc/ssh/sshd_config
17 Port 52022 # 修改默认端口
115 UseDNS no # 关闭反向解析
38 PermitRootLogin no # 禁止root用户登录
65 PasswordAuthentication no # 禁止使用密码登录
79 GSSAPIAuthentication no # 关闭GSSAPI认证

## 将以下内容,直接复制到文件最后一行
Port 6666 # 变更SSH服务远程连接端口
PermitRootLogin no # 禁止root用户直接远程登录
PasswordAuthentication no # 禁止使用密码直接远程登录
UseDNS no # 禁止ssh进行dns反向解析,影响ssh连接效率参数
GSSAPIAuthentication no # 禁止GSS认证,减少连接时产生的延迟

## 重启服务
[root@m01 ~]# systemctl restart sshd

## 解决方案
如果已经优化完ssh,发现服务器上出现以下问题:
1.没有普通用户
useradd lw(无法创建,进入单用户模式)
2.windows上秘钥没有推送
1)在windows上生成密钥对
- 使用windows的命令行执行 ssh-keygen
- 使用Xshell

使用Xshell生成密钥对

生成密钥对

[lw@m01 ~]$ mkdir .ssh
[lw@m01 ~]$ chmod 700 .ssh/
[lw@m01 ~]$ vim .ssh/authorized_keys
ssh-rsa
AAAAB3NzaC1yc2EAAAABIwAAAQEAuNorYRb3u0gM8RvxeZqGZJjptceVYpZz3ADLzTpF92DZlmGjEScnbmEfZaIVhER
F0nTvd79aixEP97V2l/B7p30b4+oaFugCR0ZDJRmypZUIKXnNYwbDjwT7k/4/V3DTYjS+0OkNLbKghlDDX0ntOoClGb
y2zV87draCqOQ6F4AAHWcrZi72gflKEShRKULKOGjcr3ZzDZfyKH6IxWLxa3EJO3v/tUFOatqDDj2j+aNswgbEDf0iC
Ynaw5h+SiTkVV6zY44TEEM+h9aVXjY1ufyAAePRDcuOnHBB1VzE38TMLEYl94VEUartDwE2TMnVOk5MhRYm4Q09zrPj
VM0J3w==
[lw@m01 ~]$ chmod 600 .ssh/authorized_keys

五、免交互生成密钥对

ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa &>/dev/null
-t:指定加密类型
-P:空密码
-f:秘钥生成的位置

免交互推送公钥

#ip地址一个一个写上去
#!/bin/bash
ls -l ~/.ssh/id_rsa &>/dev/null || ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa &>/dev/null
ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.16.1.31
ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.16.1.41
ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.16.1.5
ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.16.1.7
ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.16.1.8

## 循环
#!/bin/bash
ls -l ~/.ssh/id_rsa &>/dev/null || ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa &>/dev/null
for n in `cat /root/1.txt`;do
ssh-copy-id -i ~/.ssh/id_rsa.pub root@$n
done

## 解决密码交互问题
1)使用expect解决
[root@m01 ~]# yum install -y expect
#!/usr/bin/expect
set ip 172.16.1.31
set pass 1
set timeout 30
spawn ssh-keygen
expect {
        "id_rsa):" {send "\r"; exp_continue}
        "passphrase):" {send "\r"; exp_continue}
        "again:" {send "\r"}
}
expect eof

spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@$ip
expect {
        "(yes/no)" {send "yes\r"; exp_continue}
        "password:" {send "$pass\r"}
}
#expect "root@*" {send "df -h\r"}
#expect "root@*" {send "exit\r"}
expect eof

2)使用sshpass解决
[root@m01 ~]# yum install -y sshpass
## ssh不需要输入yes的选项
[root@m01 ~]# ssh -o 'StrictHostKeyChecking no' root@172.16.1.7
[root@m01 ~]# sshpass -p 1 ssh-copy-id -o 'StrictHostKeyChecking no' -i ~/.ssh/id_rsa.pub
root@172.16.1.8

[root@m01 ~]# cat send_public_key.sh
#!/bin/bash
ls -l ~/.ssh/id_rsa &>/dev/null || ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa &>/dev/null
for n in `cat /root/1.txt`;do
shpass -p 1 ssh-copy-id -o 'StrictHostKeyChecking no' -i ~/.ssh/id_rsa.pub root@$ip
done

[root@m01 ~]# cat send_public_key.sh
#!/bin/bash

ls -l ~/.ssh/id_rsa &>/dev/null || ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa &>/dev/null

for n in `cat /root/1.txt`;do
   shpass -p 1 ssh-copy-id -o 'StrictHostKeyChecking no' -i ~/.ssh/id_rsa.pub root@$ip
done

[root@m01 ~]# cat /root/1.txt
172.16.1.31
172.16.1.41
172.16.1.5
172.16.1.7
172.16.1.8

## 密码不一致的情况
#!/bin/bash

ls -l ~/.ssh/id_rsa &>/dev/null || ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa &>/dev/null

for n in `cat /root/1.txt`;do
  pass=`echo $n|awk -F ':' '{print $2}'`
  ip=`echo $n|awk -F ':' '{print $1}'`
   sshpass -p $pass ssh-copy-id -o 'StrictHostKeyChecking no' -i ~/.ssh/id_rsa.pub root@$ip
done

[root@m01 ~]# cat /root/1.txt
172.16.1.31:1
172.16.1.41:2
172.16.1.5:3
172.16.1.7:4
172.16.1.8:111

## 优化后的脚本
vim send_public_key.sh

#!/bin/bash

. /etc/init.d/functions

ls -l ~/.ssh/id_rsa &>/dev/null || ssh-keygen -t rsa -P '' -f 
~/.ssh/id_rsa &>/dev/null

for n in `cat /root/1.txt`;do
   pass=`echo $n|awk -F ':' '{print $2}'`
   ip=`echo $n|awk -F ':' '{print $1}'`
   sshpass -p $pass ssh-copy-id -o 'StrictHostKeyChecking no' -i ~/.ssh/id_rsa.pub root@$ip
&>/dev/null
   if [ $? -eq 0 ];then
    action "$ip send public key " /bin/true
   else
    action "$ip send public key " /bin/false
   fi
done

## 优化后不使用判断的脚本
#!/bin/bash

. /etc/init.d/functions

ls -l ~/.ssh/id_rsa &>/dev/null || ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa &>/dev/null

for n in `cat /root/1.txt`;do
   pass=`echo $n|awk -F ':' '{print $2}'`
   ip=`echo $n|awk -F ':' '{print $1}'`
   sshpass -p $pass ssh-copy-id -o 'StrictHostKeyChecking no' -i ~/.ssh/id_rsa.pub root@$ip
&>/dev/null && \
   action "$ip send public key " /bin/true || \
   action "$ip send public key " /bin/false
done

六、CRT的安装和配置​

Google双向认证

GoogleAuthenticator(谷歌身份验证器)介绍:

一般来说,使用ssh远程登录服务器,只需要输入账号和密码,显然这种方式不是很安全。为了安全着想,可以使用GoogleAuthenticator(谷歌身份验证器),以便在账号和密码之间再增加一个验证码,只有输入正确的验证码之后,再输入密码才能登录。这样就增强了ssh登录的安全性。账号、验证码、密码三者缺一个都不能登录,即使账号和密码正确,验证码错误,同样登录失败。其中,验证码是动态验证码,并且是通过手机客户端自动获取(默认每隔30秒失效一次)

GoogleAuthenticator安装部署:

# 1.安装依赖
[root@m01 ~]# yum -y install pam-devel libpng-devel autoconf automake libtool

# 2.下载Google apm插件
## 官方下载地址
[root@m01 opt]# wget https://github.com/google/google-authenticator-libpam/archive/1.04.tar.gz
## 个人下载地址
[root@m01 ~]# wget http://test.driverzeng.com/other/1.04.tar.gz

# 3.解压插件
[root@m01 ~]# tar xf 1.04.tar.gz

# 4.构建代码
## 进入解压开的目录
[root@m01 ~]# cd google-authenticator-libpam-1.04/
## 执行bootstrap构建
[root@m01 google-authenticator-libpam-1.04]# ./bootstrap.sh

# 5.生成
[root@m01 google-authenticator-libpam-1.04]# ./configure

# 6.编译 && 安装
[root@m01 google-authenticator-libpam-1.04]# make && make install

# 7.检查插件是否安装
[root@m01 google-authenticator-libpam-1.04]# ll /usr/local/lib/security/
-rwxr-xr-x 1 root root 1021 May 25 09:15 pam_google_authenticator.la
-rwxr-xr-x 1 root root 133552 May 25 09:15 pam_google_authenticator.so

# 8.将安装好的插件,拷贝到系统库文件目录中
[root@m01 ~]# cp /usr/local/lib/security/pam_google_authenticator.so /usr/lib64/security/

# 9.生成初始google认证识别码
[root@m01 ~]# google-authenticator
## 认证令牌是否随时间变化
Do you want authentication tokens to be time-based (y/n) y
Your new secret key is: 7WHLC4Z6LT3W4BTK6OR2AVCR7E
Your verification code is 020267
Your emergency scratch codes are:
81061642
20695747
19608008
26971435
40551289
##是否更新google_authenticator
Do you want me to update your "/root/.google_authenticator" file? (y/n) 

手机下载GoogleAuthenticator扫码生成动态认证令牌

将Goole 2FA接入SSH

# 1.修改ssh认证配置
[root@m01 ~]# vim /etc/pam.d/sshd
auth    required    pam_google_authenticator.so

# 2.修改SSH配置文件,关联Google认证
[root@m01 ~]# vim /etc/ssh/sshd_config
69 ChallengeResponseAuthentication yes

# 3.重启sshd服务
[root@m01 ~]# systemctl restart sshd

使用Python脚本登录CRT:

# $language = "python"
# $interface = "1.0"
import hmac, base64, struct, hashlib, time,re

#获取当前脚本所在的tab对象
objTab = crt.GetScriptTab()
#objTab = crt.GetActiveTab()
objTab.Screen.Synchronous = True
objTab.Screen.IgnoreEscape = True
#获取终端名字
tabName=objTab.Caption
reIp=r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
hostIp=re.findall(reIp,tabName)[0]
secretKey="NJZ4E6D7JHMIYLK5MFJLSWYDSE"

def calGoogleCode(secretKey):
    #secreKey 需要是8的倍数
    t = int(time.time())//30
    lens = len(secretKey)
    lenx = 8 - (lens % 4 if lens % 4 else 4)
    secretKey += lenx * '='
    key = base64.b32decode(secretKey)
    msg = struct.pack(">Q", t)
    googleCode = hmac.new(key, msg, hashlib.sha1).digest()
    o = ord(str(googleCode[19])) & 15
    googleCode = str((struct.unpack(">I", googleCode[o:o+4])[0] & 0x7fffffff) % 1000000)
    return googleCode.zfill(6)

def get_string(objTab,szStart,szPrompt):
    objTab.Screen.WaitForStrings(szStart)
    return objTab.Screen.ReadString(szPrompt)
def send_string(objTab,waitString,strings,selfSleepTime=20):
    objTab.Screen.WaitForStrings(waitString)
    time.sleep(0.0001)
    for i in strings:
        crt.Sleep(5)
        objTab.Screen.Send(i)
    # time.sleep(0.0001)
    objTab.Screen.WaitForStrings(strings)
    if strings[-1] != '\r':
        objTab.Screen.Send('\r')
    #msg(objTab.Screen.ReadString('[ q ]'))
    # time.sleep(0.0001)

def send_pass(objTab,waitString,strings):
    objTab.Screen.WaitForStrings(waitString)
    for i in strings:
        crt.Sleep(5)
        objTab.Screen.Send(i)
    if strings[-1] != '\r':
        objTab.Screen.Send('\r')
    time.sleep(0.01)

#发送2fa
send_pass(objTab,'Verification code:',calGoogleCode(secretKey))
## 发送密码
send_pass(objTab,'Password: ','1')   # '1' 是服务器的密码
#发送登录ip
send_string(objTab,'Opt> ',hostIp)

#objTab.Screen.WaitForStrings("[MFA auth]: ","")
#if objTab.Screen.WaitForStrings("Opt> ",1):
#    #发送登录ip  克隆会话,不需要二次验证码
#    send_string(objTab,'Opt> ',hostIp)
#else:     
#    #发送2fa
#    send_pass(objTab,'[MFA auth]: ',calGoogleCode(secretKey))
#    #发送登录ip
#    send_string(objTab,'Opt> ',hostIp)

山林不向四季起誓 荣枯随缘