赞
踩
ssh远程连接服务器两种方式:一是ssh直接通过用户名密码直连 二是做ssh免密登录(私钥和公钥的匹配)
首先,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
在/root下的.ssh目录下有:
(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发来的信息后,会和之前生成的字符串进行比对,如果一致,则允许免密登录。
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服务器最新的公钥即可。
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()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。