当前位置:   article > 正文

ssh远程连接及python代码实现远程连接_控制台ssh正确连接代码

控制台ssh正确连接代码

一、ssh远程连接

ssh远程连接服务器两种方式:一是ssh直接通过用户名密码直连 二是做ssh免密登录(私钥和公钥的匹配)

1、首先安装ssh服务:(这里以centos7为例)

首先,yum list installed | grep ssh 查询是否安装了openssh-server;
安装,yum install openssh-server
配置,vim /etc/ssh/sshd_config
在这里插入图片描述
启动服务,systemctl start sshd.service
检查服务是否启动,ps -ef|grep sshd
检查开放端口,netstat -ntlp | grep sshd

2、.ssh目录

在/root下的.ssh目录下有:

  • authorized_keys(存放其他机器的公钥)
  • id-rsa(本机私钥)
  • id_rsa.pub(本机公钥)
  • knows_hosts(本机已建立过互信的记录)
3、配置互信:

(1)确保网络可通,确保脚本执行机器和目标机器网络可通;
(2)单向互信:将执行机器的公钥id_rsa.pub,放入到目标机器的authorized_keys (在目标机器上要起sshd服务,暴露22端口;在执行机器生成一对密钥ssh-keygen)
可以手动copy公钥,然后vim打开authorized_keys 复制进去;
也可以在执行机器上执行命令:ssh-copy-id 目标机器 (回车之后需要输入一次密码)
注意的是,knows_hosts存放了已经建立过互信的记录,如果其他机器的公钥变了,要先删除knows_hosts中的记录,再做互信,否则登录不上;
(可将公钥id_rsa.pub都已经打包到镜像里面了,这样创建好的虚机自动带着执行机器的公钥,自动互信)
在这里插入图片描述
4、免密登录原理及过程
(1)服务器A向服务器B发送一个连接请求,信息包括服务器A此时登录的用户名、服务器A的ip以及要免密登录的服务器B的用户名;
(2)服务器B收到请求,会从服务器A要免密登录服务器B的用户名家目录下 authorized_keys 中查找是否有相同的服务器A用户名、服务器A的ip;
如果有,服务器B会随机生成一个字符串,然后使用服务器A的公钥进行加密,再发送给服务器A;
服务器A接到服务器B发来的信息后,会使用私钥进行解密,然后将解密后的字符串发送给服务器B;
服务器B接到服务器A发来的信息后,会和之前生成的字符串进行比对,如果一致,则允许免密登录。

二、known_hosts文件

A通过ssh首次连接到B,B会将公钥1(host key)传递给A,A将公钥1存入known_hosts文件中,以后A再连接B时,B依然会传递给A一个公钥2,OpenSSH会核对公钥,通过对比公钥1与公钥2 是否相同来进行简单的验证,如果公钥不同,OpenSSH会发出警告,并且需要用户交互式的输入yes/no, 避免受到DNS Hijack之类的攻击;如果公钥相同,则不会发出警告。
例如:
A服务器首次ssh登录B服务器,由于A服务器的known_hosts文件中没有B服务器的公钥,因此控制台会发出警告,并且需要用户输入yes/no
当我们输入yes之后,A服务器的known_hosts文件会增加一条记录(B服务器的公钥);再输入B服务器的密码,ssh登录成功
当A服务器再次ssh登录B服务器,B依然会传递给A一个公钥,OpenSSH会核对公钥,将该公钥和A服务器known_hosts文件保存的B服务器的公钥进行对比,由于相同,则不会发出警告,直接让用户输入密码
如果我们不希望在进行ssh登录时进行公钥检查,可以加上 “StrictHostKeyChecking no” 跳过公钥检查,就不需要用户手动输入yes/no(这在一些自动化场景经常使用到)
备注:如果A通过ssh登陆B时提示 Host key verification failed.;原因是A的known_hosts文件中记录的B的公钥1与连接时B传过来的公钥2不匹配。
解决方法:删除A的known_hosts文件中记录的B的公钥,当再次ssh登录B时,重新写入B服务器最新的公钥即可。

三、python代码 远程连接

class Remote:
    def __init__(self,ip,port,password=None,timeout=5):
        self.ip = ip
        self.port = port
        self.password = password
        self.timeout = timeout
        # 私钥方式用于身份验证
        self._pkey = paramiko.RSAKey.from_private_key_file("/root/.ssh/id_rsa")

        sshclient = paramiko.SSHClient()
        """
        set_missing_host_key_policy():设置远程服务器没有在know_hosts文件中记录时的应对策略。目前支持三种策略:
设置连接的远程主机没有本地主机密钥或HostKeys对象时的策略,目前支持三种:
AutoAddPolicy 自动添加主机名及主机密钥到本地HostKeys对象,不依赖load_system_host_key的配置。即新建立ssh连接时不需要再输入yes或no进行确认
WarningPolicy 用于记录一个未知的主机密钥的python警告。并接受,功能上和AutoAddPolicy类似,但是会提示是新连接
RejectPolicy 自动拒绝未知的主机名和密钥,依赖load_system_host_key的配置。此为默认选项
        """
        sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            if self.password is None:
                sshclient.connect(hostname=self.ip,port=self.port,username="root",pkey=self._pkey, timeout=self.timeout)
            else:
                sshclient.connect(hostname=self.ip,port=self.port,username="root",password=self.password, timeout=self.timeout)
            self.sshclient = sshclient
        except Exception:
            self.sshclient = None

        try:
            transport = paramiko.Transport((self.ip,self.port))
            if self.password is None:
                transport.connect(username="root",pkey=self._pkey)
            else:
                transport.connect(username="root",password=self.password)
            sftpclient = paramiko.SFTPClient.from_transport(transport)
            self.sftpclient = sftpclient
        except Exception:
            self.sftpclient = None

    def genclient(self):
        sshclient = paramiko.SSHClient()
        sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            if self.password is None:
                sshclient.connect(hostname=self.ip,port=self.port,username="root",pkey=self._pkey, timeout=self.timeout)
            else:
                sshclient.connect(hostname=self.ip,port=self.port,username="root",password=self.password, timeout=self.timeout)
            self.sshclient = sshclient
        except Exception:
            self.sshclient = None

        try:
            transport = paramiko.Transport((self.ip,self.port))
            if self.password is None:
                transport.connect(username="root",pkey=self._pkey)
            else:
                transport.connect(username="root",password=self.password)
            sftpclient = paramiko.SFTPClient.from_transport(transport)
            self.sftpclient = sftpclient
        except Exception:
            self.sftpclient = None
        
    def command(self,cmd, timeout=10):
        """
        执行远程命令并返回执行结果和错误结果
        params:
          cmd 远程要执行的命令
          timeout 超时时间(s)
        return:
          执行成功 [标准输出,错误输出]
          执行失败 None
        """
        try:
            if self.sshclient is None:
                self.genclient()
            _,stdout,stderr = self.sshclient.exec_command(cmd,timeout=timeout)
            cmd_out = stdout.read().decode(errors='ignore')
            cmd_err = stderr.read().decode(errors='ignore')
            print(f"{cmd}, out:{cmd_out}, err:{cmd_err}")
            return [cmd_out,cmd_err]
        except paramiko.SSHException:
            return None

    #remotefile 使用绝对路径
    #ex: put("test.txt","/home/test.txt")
    def put(self,localfile, remotefile):
        """
        上传文件到远程服务器
        params:
          localfile  本地要上传文件
          remotefile 上传到远程的文件,使用绝对路径
        ex: 
          put("test.txt","/home/test.txt")
        return:
          True为成功,False为失败
        except:
          没有做异常处理,使用时需要做异常处理
        """
        if self.sshclient is None:
            self.genclient()
        if isinstance(self.sftpclient.put(localfile, remotefile),paramiko.SFTPAttributes):
            return True
        else:
            return False

    def get(self,remotefile, localfile):
        """
        从远程服务器获取文件到本地
        params:
          remotefile 远程的文件,使用绝对路径
          localfile  本地要保存的文件
        ex: 
          put("/home/test.txt","test.txt")
        return:
          True为成功,False为失败
        except:
          没有做异常处理,使用时需要做异常处理
        """
        if self.sshclient is None:
            self.genclient()
        if isinstance(self.sftpclient.get(remotefile, localfile),paramiko.SFTPAttributes):
            return True
        else:
            return False

    def close(self):
        """
        关闭连接,防止文件句柄泄露
        """
        if self.sshclient is not None:
            self.sshclient.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/530246
推荐阅读
相关标签
  

闽ICP备14008679号