self.scp = None与self.client相同,但专门处理传输文件的连接 。
Self._upload_ssh_key()不是一个变量,而是一个在客户机实例化时自动运行的函数 。调用_upload_ssh_key()是告诉我们的RemoteClient对象在创建时立即检查本地ssh密钥,以便我们可以尝试将它们传递到远程主机 。否则,我们根本无法建立联系 。

文章插图
上传SSH密钥到远程主机
RemoteClient将从两个私有方法开始:_get_ssh_key()和_upload_ssh_key() 。前者将获取本地存储的公钥,如果成功,后者将把这个公钥传递给我们的远程主机,作为访问的橄榄枝 。一旦本地创建的公钥存在于远程机器上,该机器将永远信任我们的连接请求:不需要密码 。我们将在此过程中包括适当的日志记录,以防我们遇到任何麻烦:
"""Client to handle connections and actions executed against a remote host."""from os import systemfrom paramiko import SSHClient, AutoAddPolicy, RSAKeyfrom paramiko.auth_handler import AuthenticationException, SSHExceptionfrom scp import SCPClient, SCPExceptionfrom .log import loggerclass RemoteClient:"""Client to interact with a remote host via SSH & SCP."""...def _get_ssh_key(self):"""Fetch locally stored SSH key."""try:self.ssh_key = RSAKey.from_private_key_file(self.ssh_key_filepath)logger.info(f'Found SSH key at self {self.ssh_key_filepath}')except SSHException as error:logger.error(error)return self.ssh_keydef _upload_ssh_key(self):try:system(f'ssh-copy-id -i {self.ssh_key_filepath} {self.user}@{self.host}>/dev/null 2>&1')system(f'ssh-copy-id -i {self.ssh_key_filepath}.pub {self.user}@{self.host}>/dev/null 2>&1')logger.info(f'{self.ssh_key_filepath} uploaded to {self.host}')except FileNotFoundError as error:logger.error(error) _get_ssh_key()非常简单:它验证SSH密钥是否存在于我们在配置中指定的用于连接到主机的路径上 。如果该文件确实存在,我们很乐意设置self.ssh_key变量,这样我们的客户端就可以上传和使用这个密钥了 。Paramiko为我们提供了一个名为RSAKey的子模块,可以轻松处理所有与RSA密钥相关的事情,比如将一个私钥文件解析为一个可用的连接身份验证 。这就是我们得到的:
RSAKey.from_private_key_file(self.ssh_key_filepath)如果我们的RSA密钥是不可理解的废话,而不是真正的密钥,Paramiko的SSHException会捕捉到这一点,并在解释这一点之前就引发一个异常 。正确地利用库的错误处理需要对“哪里出了问题”进行大量猜测,特别是在某些情况下,比如在一个我们都不会经常搞混的小空间中,可能存在许多未知的情况 。
文章插图
连接到客户端
我们将在客户机中添加一个名为connect()的方法来处理到主机的连接:
...class RemoteClient:"""Client to interact with a remote host via SSH & SCP."""...def _connect(self):"""Open connection to remote host."""if self.conn is None:try:self.client = SSHClient()self.client.load_system_host_keys()self.client.set_missing_host_key_policy(AutoAddPolicy())self.client.connect(self.host,username=self.user,key_filename=self.ssh_key_filepath,look_for_keys=True,timeout=5000)self.scp = SCPClient(self.client.get_transport())except AuthenticationException as error:logger.error(f'Authentication failed:did you remember to create an SSH key? {error}')raise errorreturn self.client让我们来分析一下:- SSHClient()为创建代表SSH客户机的对象奠定了基础 。以下几行将配置此对象,使其更有用 。
- load_system_host_keys()指示客户机查找我们过去连接过的所有主机,方法是查看系统的known_hosts文件并找到主机所期望的SSH密钥 。我们过去从未连接到我们的主机,所以我们需要显式地指定SSH密钥 。
- set_missing_host_key_policy()告诉Paramiko在出现未知密钥对时该怎么做 。这需要Paramiko内置一个“策略”,我们将具体到AutoAddPolicy() 。将我们的策略设置为“自动添加”意味着如果我们试图连接到一个无法识别的主机,Paramiko将自动在本地添加丢失的密钥 。
- connect()是SSHClient最重要的方法(正如您可能想象的那样) 。我们终于能够传递我们的主机、用户和SSH密钥来实现我们一直在等待的东西:到我们的服务器的一个漂亮的SSH连接!connect()方法也通过大量可选关键字参数数组提供了极大的灵活性 。我碰巧在这里传递了一些:将look_for_keys设置为True将允许Paramiko在~/中查看 。ssh文件夹发现自己的ssh密钥,设置超时将自动关闭我们可能忘记关闭的连接 。如果选择以这种方式连接到主机,我们甚至可以传递端口和密码等变量 。
推荐阅读
- Python重要知识,生成器的威力
- 怎么用python批量获取免费代理IP
- python怎么验证代理IP是否有效
- 终于把所有的 Python 库都整理出来啦
- 太强了,用Python制作动态可视化图表
- 一篇文章带你搞懂Python中的类
- 这两个Python工具真香!修改代码不会影响运行
- 如何用Python输出数学公式?
- Python实现用手机监控远程控制电脑
- Pythonic风格代码有什么好处?附12个代码实例
