Telnet和OpenSSH服务

Telnet和OpenSSH服务

微信搜索 zze_coding 或扫描 👉 二维码关注我的微信公众号获取更多资源推送:

Telnet

介绍

Telnet 协议是 TCP/IP 协议族中的一员,是 Internet 远程登录服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用 telnet 程序,用它连接到服务器。终端使用者可以在 telne t程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个 telnet 会话,必须输入用户名和密码来登录服务器。Telnet 是常用的远程控制 Web 服务器的方法。

注意,telnet 不支持管理员 root 用户登录。

使用

服务端

1、安装 telnet 的服务端:

$ yum install telnet-server.x86_64 -y

2、设置开机启动:

$ systemctl enable telnet.socket

3、启动服务:

$ systemctl start telnet.socket

对于 CentOS 6 来说,telnet 服务是由 xinetd 守护,所以要启动 telnet 服务只需要启动 xinetd 即可,修改 /etc/xinetd.d/telnet 就可启用 telnet 服务的监听。
在 CentOS 7 中直接启动 telnet 服务即可。
telnet 服务默认监听 23 端口;

客户端

直接使用 XShell 通过 telnet 命令连接,如下:

image.png

OpenSSH

OpenSSH 是什么

OpenSSH 是 SSH(Secure SHell)协议的免费开源实现。SSH 协议族可以用来进行远程控制,或在计算机之间传送文件。而实现此功能的传统方式,如 telnet(终端仿真协议)、rcp ftp、rlogin、rsh 都是极为不安全的,并且会使用明文传送密码。OpenSSH 提供了服务端后台程序和客户端工具,用来加密远程控制和文件传输过程中的数据,并由此来代替原来的类似服务。

OpenSSH 只是 SSH 协议的其中一个实现者,还有 dropbear 也是 SSH 协议的实现。

OpenSSH 的组成

OpenSSH 主要包括了如下几个部分:

  • ssh:rlogin 与 Telnet 的替代方案;
  • scpsftp:rcp 的替代方案,将文件复制到其他电脑上;
  • sshd:SSH 服务器,默认监听 22 端口;
  • ssh-keygen:产生 RSA 或 DSA 密钥,用来认证用;
  • ssh-agentssh-add:帮助用户不需要每次都要输入密钥密码的工具;
  • ssh-keyscan:扫描一群机器,并记录其公钥;

客户端组件

ssh 是 OpenSSH 的客户端组件,对应的配置文件为 /etc/ssh/ssh_config
使用格式如下:

ssh [user@]host  [command]
    -p:指定远程主机监听的端口;

不指定用户时默认是以当前系统的登录用户的身份来登录远程主机,例:

$ ssh 10.0.1.202

ssh 命令还可用来直接在指定主机上执行给定的命令,例:

$ ssh root@10.0.1.202 ifconfig

查看 /etc/ssh/ssh_config 配置文件会看到以下内容:

# 下面内容在配置文件中是注释内容,也是默认值,如果想要启用则需要修改对应值并取消注释。
Host * # 限定生效的主机,* 表示对所有主机都使用以下配置
  StrictHostKeyChecking ask # 是否询问校验主机 Key
  Port 22 # 默认连接使用的端口
  Protocol 2 # 协议版本
  ...

该内容的作用是用来对指定的主机使用特定的配置,* 表示所有主机。

使用基于密钥的认证

在上述我们使用 ssh 命令连接远程主机时使用的时密码方式进行认证,除此之外我们还可以使用密钥的方式进行认证。
ssh 要想使用基于密钥的认证,需要先在客户端生成一对密钥,把公钥部分上传到要连接的远程主机对应的账户的家目录下的一个特定配置文件中,这个特定文件的位置一般是在服务器端的配置文件中指明的,这个配置文件通常是在用户家目录下的名为 .ssh 的隐藏目录,如:

$ ls ~/.ssh/
known_hosts

在上述我们使用 ssh 连接远程主机时,通常第一次连接确认认证通过后第二次连接就不需要密码验证登录了,这是因为 ssh 客户端和远程主机服务端自动完成了密钥认证,查看该文件:

$ cat known_hosts
10.0.1.200 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMQwK+G3Gq5FHYsNnNv5W6LCzKIlKPrKRbnZUXkxd15aSm+1VUgRLbm6JXFZChXYbS+mSTxRTNCUsicqyS1DHC0=

这里执行 cat known_hosts 的主机是 10.0.1.202,在上述演示客户端组件时我使用 10.0.1.200 这台主机连接了 10.0.1.202,并确认通过了认证,然后 10.0.1.200 这台主机生成的公钥信息就被保存在 10.0.1.202 这台主机上了,所以后续连接就不需要密码了。


现在以 10.0.1.202 为 ssh 客户端生成密钥对,以密钥方式认证连接 10.0.1.200 为例,做如下步骤。
1、10.0.1.202 主机以指定加密方式生成密钥对。

$ ssh-keygen -t rsa

密钥对默认会保存在当前用户家目录下的 .ssh 目录中,id_rsa 是私钥,id_rsa.pub 是公钥。

2、将公钥传送到远程主机 10.0.1.200 对应用户的家目录。

$ ssh-copy-id -i /root/.ssh/id_rsa.pub root@10.0.1.200

传送成功后对应用户的加目录下的 .ssh 目录中会生成一个 authorized_keys 文件。

3、此时使用 10.0.1.202 连接 10.0.1.200 就不需要密码了。

$ ssh root@10.0.1.200

XShell 使用密钥方式认证

上述是直接使用两台 Linux 主机来完成 ssh 密钥认证,还可以直接在 Windows 上使用 XShell 与主机完成密钥认证。
1、选择工具菜单下的”新建用户密钥生成向导“:

image.png

2、直接使用默认的”RSA“方式生成 2048 位密钥即可:

image.png

3、继续下一步:

image.png

4、密钥名称随意,如果要给私钥加密就可以输入密码,不需要就直接下一步:

image.png

5、此时公钥已生成成功,选择将其保存为文件:

image.png

6、进入要使用密钥认证的主机对应用户的家目录下的 .ssh 目录,编辑 authorized_keys 文件,上一步保存的文件中的密钥信息添加到 authorized_keys 的尾部,要注意的是 authorized_keys 的属主位必须拥有读写权限(600)。
7、再次使用 XShell 连接这个主机,身份认证时选择”Public Key“方式,在上面我没有对密钥加密,所以我不输入密码直接登录即可:

image.png

8、然后就会发现成功登录了。

注意:用户家目录下的 .ssh 目录的权限需要是 700,其下的 authorized_keys 文件权限应该是 600,否则会登陆失败。

使用 scp 传送文件

scp 全称 ”secure copy“,译为”安全复制“的意思,它是一个跨主机的复制命令,可以将本机上的文件复制到远程主机,也可以将远程主机上的文件复制到本机。
scp 命令有两种模式:

  • pull 模式:将远程文件拉取到本地;
  • push 模式:将本地文件推送到远程主机;

pull 模式的使用格式如下:

scp [options] [user@]<host>:<from_file> <local_path>
    以 user 的身份将登录远程主机 host 将 from_file 文件复制到本地的 local_path,如果复制的文件是多个,local_path 应该是一个目录。

push 模式的使用格式如下:

scp [options]  <from_file> [user@]<host>:<to_path>
    以 user 的身份登录远程主机复制本地 from_file 文件到远程主机 host 下的 to_path 路径下,如果复制的文件是多个,to_path 应该是一个目录。

常用选项:

  • -r:递归复制,复制目录时指定;
  • -p:保持原文件的属性信息;
  • -q:静默模式;
  • -P <port>:指定远程主机监听端口;

看如下示例。

1、登录 10.0.1.200,以 root 用户的身份拉取 10.0.1.202 中的 /etc/fstab 到本地家目录下。

$ scp root@10.0.1.202:/etc/fstab .
root@10.0.1.202's password: 
fstab

2、登录 10.0.1.200,以 root 用户的身份复制本地 /etc/fstab10.0.1.202 的家目录下。

$ scp /etc/fstab root@10.0.1.202:/root/
root@10.0.1.202's password: 
fstab 

如果两台主机完成了密钥认证,那么上述两个示例过程中是不需要输入密码的。

使用 sftp 访问远程主机文件系统

OpenSSH 还支持以类似 ftp 的方式访问远程主机的文件系统,这里就要用到 sftp 命令,以 10.0.1.200 主机访问 10.0.1.202 为例:

$ sftp root@10.0.1.202
Connected to 10.0.1.202.
sftp> 

由于 10.0.1.20010.0.1.202 这两台主机已经完成了密钥认证,所以使用 sftp 是不需要密码的。

sftp 命令提示符下的子命令与使用 ftplftp 命令没有太大区别,这里就不再多说。

服务器端组件

sshd 是 OpenSSH 的服务端组件,服务名也是 sshd,CentOS 7 中可通过执行 systemctl status sshd 查看 sshd 服务状态,对应的配置文件为 /etc/ssh/sshd_config

在该配置文件中,以 # 开头的行为纯注释,只以 # 开头的行表示可启用的选项。

配置文件中常用的设置字段有如下:

  • Port:sshd 服务监听的端口;
  • ListenAddress:指明监听的地址;
  • AddressFamily:指定使用 IPv4 还是 IPv6 协议家族,any 表示都监听,4 对应 IPv4,6 对应 IPv6;
  • SyslogFacility:指明记录日志的方式,默认使用 AUTHPRIV 记录日志,保存在 /var/log/secure 中;
  • LoginGraceTime:登录宽限时间,默认为 2m,表示在登录提示符下 2 分钟之内不输入密码则超时;
  • PermitRootLogin:是否允许管理员用户(root)登录,默认 yes 表示允许;
  • StrictModes:是否使用严格检查模式;
  • MaxAuthTries:最大认证尝试次数;
  • MaxSessions:指定 OpenSSH 最多允许多少个会话并行登录;
  • RSAAuthentication:是否支持 RSA 安全认证;
  • PubkeyAuthentication:是否支持公钥认证;
  • AuthorizedKeysFile:指定使用公钥认证时的公钥的存放位置(相对对应用户家目录),默认值为 .ssh/authorized_key
  • PasswordAuthentication:是否允许密码(口令)认证;
  • X11Forwarding:是否允许 X11 转发(远程打开图形化窗口);
  • Subsystem:对 sftp 的支持,注释则表示不支持 sftp 远程连接;
  • UseDNS:是否使用 DNS 反解;
  • AllowUsers:指定可登录的用户,多个用户用空格分开;
  • AllowGroups:指定可登录的组的用户,多个组用空格分开;
  • DenyUsers:指定拒绝登录的用户,多个用户用空格分开;
  • DenyGroup:指定拒绝登录的组的用户,多个组用空格分开;
  • GSSAPIAuthentication:GSS 认证;

关于 sshd_config 更详细的描述信息可使用 man sshd_config 查看。

通常情况下我们会修改如下这几项:

Port			6666 	# 变更 SSH 服务远程连接端口;
PermitRootLogin		no	# 不允许 root 远程登录
PasswordAuthentication	no	# 禁止使用密码直接远程登录
UseDNS			no	# 禁止 SSH 进行 DNS 反向解析,影响 SSH 连接效率
GSSAPIAuthentication 	no	# 禁止 GSS 认证,减少连接延迟

ssh 服务的最佳实践

  1. 不要使用默认端口;
  2. 禁止使用 protocol version 1
  3. 使用白名单(AllowUsers)限制可登录用户;
  4. 设置空闲会话超时时长;
  5. 利用防火墙设置 ssh 的访问策略;
  6. 仅监听特定的 IP 地址;
  7. 基于口令认证时,使用强密码策略;
  8. 使用基于密钥的认证;
  9. 禁止使用空密码;
  10. 禁止 root 用户直接登录;
  11. 限制 ssh 的访问频度和并发在线数;
  12. 保存好日志,经常分析;

扩展

免交互 SSH

1、expect 免交互:

# 安装
$ yum install expect -y
# 编写脚本
$ cat expect.ex
#!/usr/bin/expect
set ip 10.0.1.7
set pass 1
set timeout 30
spawn ssh root@$ip
expect {
"(yes/no)" {send "yes\r"; exp_continue}
"password:" {send "$pass\r"}
}
expect "root@*" {send "ls -l\r"}
expect "root@*" {send "exit\r"}
expect eof
# 连接
$ expect expect.ex

2、sshpass 免交互

# 安装
$ yum -y install sshpass
# 连接
$ sshpass -p<pass> ssh <host>
# 执行命令
$ sshpass -p<pass> ssh <host> <command>

跳板机脚本

#!/bin/bash
lb01=10.0.1.5
lb02=10.0.1.6
web01=10.0.1.7
web02=10.0.1.8
web03=10.0.1.9
nfs=10.0.1.31
backup=10.0.1.41
db01=10.0.1.51
db02=10.0.1.52
m01=10.0.1.61
zabbix=10.0.1.71
title="欢迎进入跳板机-$m01"
#跳板机函数
JUMP(){
                OPTION=$(whiptail --title "$title"  --menu "请选择你要连接的服务器:" 25 60 11 \
                 "1" "连接 lb01" \
                 "2" "连接 lb02" \
                 "3" "连接 web01" \
                 "4" "连接 web02" \
                 "5" "连接 web03" \
                 "6" "连接 nfs" \
                 "7" "连接 backup" \
                 "8" "连接 db01" \
                 "9" "连接 db02" \
                 "10" "连接 m01" \
                 "11" "连接 zabbix"  3>&1 1>&2 2>&3)
         exitstatus=$?
		 if [ $exitstatus = 0 ]; then
            case $OPTION in
            		    1)
            		    connect $lb01
                        ;;
                        2)
                        connect $lb02
            		    ;;
            		    3)
            		    connect $web01
                        ;;
                        4)
                        connect $web02
            		    ;;
            		    5)
            		    connect $web03
                        ;;
                        6)
                        connect $nfs
            		    ;;
            		    7)
            		    connect $backup
                        ;;
                        8)
                        connect $db01
            		    ;;
            		    9)
            		    connect $db02
                        ;;
                        10)
                        connect $m01
            		    ;;
            		    11)
            		    connect $zabbix
                        ;;
                        12)
                        connect $te
            		    ;;
            		    13)
            		    connect $te_web
                        ;;
                        esac
        fi
}
#主机函数
HOST_INFO (){
            HOST=$(whiptail --title "$title" --checklist \
                "请选择要发送的主机名:" 25 60 11 \
                "$lb01" "发送给lb01" OFF \
                "$lb02" "发送给lb01" OFF \
                "$web01" "发送给web01" OFF \
                "$web02" "发送给web02" OFF \
                "$web03" "发送给web03" OFF \
                "$nfs" "发送给nfs" OFF \
                "$backup" "发送给backup" OFF \
                "$db01" "发送给db01" OFF \
                "$db02" "发送给db02" OFF \
                "$m01" "发送给m01" OFF \
                "$zabbix" "发送给zabbix" OFF 3>&1 1>&2 2>&3)
}
SER_INFO(){
                SER=$(whiptail --title "$title" --checklist \
                "请选择要检查的服务:" 25 60 10 \
                "nginx" "检查nginx" OFF \
                "mysqld" "检查mysqld" OFF \
                "php" "检查php" OFF \
                "tomcat" "检查tomcat" OFF \
                "sshd" "检查sshd" OFF \
                "httpd" "检查httpd" OFF \
                "vsftpd" "检查vsftpd" OFF \
                "docker" "检查docker" OFF \
                "saltstack" "检查saltstack" OFF \
                "rsyncd" "检查rsyncd" OFF 3>&1 1>&2 2>&3)
}
#连接函数
connect(){
  whiptail --title "$title" --yesno "你确定要连接这台机器么?想好了啊!!!" 10 60
  if [ $? -eq 0 ];then
            {
            ping -c 1 -w 1 $1 >/tmp/ping.txt 2>/dev/null
            if [ $? -ne 0 ];then
                for ((i = 0 ; i <= 100 ; i+=30)); do
                        sleep 1
                        echo $i
                done
            fi
            } | whiptail --gauge "emmmm...我正在连接$1,检测网络中,等我一哈子..." 6 60 0
            grep 'ttl' /tmp/ping.txt &>/dev/null
            if [ $? -eq 0 ];then
                ssh root@$1
            else
                whiptail --title "$title" --msgbox "网络检测失败,别连了,我的哥,$1机器都没开" 10 60
            fi
  fi
}
#推送文件函数
SCP (){
        HOST_INFO
                PA=$(whiptail --title "$title" --inputbox "请输入需要推送的文件本地路径:" 10 60 /etc/passwd 3>&1 1>&2 2>&3)
                DEST=$(whiptail --title "$title" --inputbox "请输入需要分发到主机的哪个目录:" 10 60 /tmp 3>&1 1>&2 2>&3)
                {
                for H in ${HOST};do
                        echo "scp $PA ${H}:${DEST}"|bash &>/dev/null
                        for ((i = 0 ; i <= 100 ; i+=50));do
                                sleep 1
                                echo $i
                        done
                done
                } | whiptail --gauge "别着急,正在传送中..." 6 60 0
}
#检查磁盘函数
CHECK_DISK (){
        HOST_INFO
                for H in ${HOST};do
			echo "ssh $H 'df -h' "|bash > /tmp/disk
            whiptail --title "$(date +%F-%T) | $H 磁盘信息" --msgbox "$(cat /tmp/disk)" 10 60
                done
}
#检查内存函数
CHECK_MEM (){
                HOST_INFO
                for H in ${HOST};do
                        echo "ssh $H 'free -m' "|bash > /tmp/meminfo
                        whiptail --title "$(date +%F-%T) | $H 内存信息" --msgbox "$(cat /tmp/meminfo)" 30 80
                done
}
#查看服务状态
CHECK_SER (){
        HOST_INFO
        SER_INFO
        for H in ${HOST};do
			for S in ${SER};do
                HO=`echo "$H"|awk -F \" '{print $2}'`
                PROC=`ssh $HO "ps -ef|grep $S|wc -l"`
                NUM=$( expr $PROC - 3 )
                if [[ $PROC > 3 ]];then
					whiptail --title "$(date +%F-%T) | $H 服务信息" --msgbox "${S} 存活 | 进程数:$NUM" 10 60
				else
					whiptail --title "$(date +%F-%T) | $H 服务信息" --msgbox "${S} 没有存活| 进程数:$NUM" 10 60
                fi
            done
        done
}
#批量执行命令函数
EXEC_CMD(){
        HOST_INFO
        CMD=$(whiptail --title "$title" --inputbox "请输入要执行的命令:" 10 60 ifconfig 3>&1 1>&2 2>&3)
                for H in ${HOST};do
			HO=`echo "$H"|awk -F \" '{print $2}'`
			RES=`ssh $HO "$CMD"`
			whiptail --title "$(date +%F-%T) | $H 命令执行结果" --msgbox "$RES" 40 80
        done
}
#退出函数
EXIT(){
                pass=$(whiptail --title "$title" --passwordbox "请输入你的退出密码" 10 60 3>&1 1>&2 2>&3)
                if [ $pass == '123456' ];then
                        exit
                else
                        whiptail --title "$title" --msgbox "密码错误,你不是运维,小样的~~~" 10 60
                        continue
                fi
}
 whiptail --title "$title" --msgbox "lnb跳板机,给你全新不一样的feel,进去了,就不想出来" 10 60
#抓取键盘信号
trap "" HUP INT TSTP
while true;do
    OPTION=$(whiptail --title "$title" --menu "请选择你的动作(轻点...)" 15 60 8 \
    "1" "SSH远程连接" \
    "2" "推送文件" \
    "3" "查看磁盘空间" \
    "4" "查看内存空间" \
    "5" "查看服务状态" \
    "6" "批量执行命令" \
    "7" "轻松一下(game)" \
    "8" "退出"  3>&1 1>&2 2>&3)
    exitstatus=$?
	if [ $exitstatus = 0 ]; then
        case $OPTION in
        1)
        JUMP
        ;;
        2)
        SCP
        ;;
        3)
        CHECK_DISK
        ;;
        4)
        CHECK_MEM
        ;;
        5)
        CHECK_SER
        ;;
        6)
        EXEC_CMD
        ;;
        7)
        sh /root/test.sh
        ;;
        8)
        EXIT
        ;;
        esac
    fi
done

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.zze.xyz/archives/linux-service-telnet-openssh.html

Buy me a cup of coffee ☕.