您当前的位置:首页 > 电脑百科 > 站长技术 > 服务器

巧用Python登陆远程服务器

时间:2021-06-04 10:39:50  来源:  作者:机器学习与数据分析

Python/ target=_blank class=infotextkey>Python进行远程登陆服务器

这篇文章介绍如何通过使用Paramiko和SCP Python库自动化远程服务器任务。使用Python来SSH到主机,执行任务,传输文件等。

paramiko和scp是两个Python库,我们可以一起使用它们来自动化我们想要在远程主机上运行的任务,比如重新启动服务、进行更新或获取日志文件。

设置SSH密钥

要验证SSH连接,我们需要设置一个私有的RSA SSH密钥(不要与OpenSSH混淆)。我们可以使用以下命令生成密钥:

$ ssh-keygen -t rsa

这将提示我们为密钥提供一个名称。随便你怎么说:

Generating a public/private rsa key pair. Enter the file in which you wish to save they key (i.e., /home/username/.ssh/id_rsa):

接下来,系统将提示您提供一个密码(不必填写)。

现在我们有了密钥,我们需要将其复制到远程主机。最简单的方法是使用ssh-copy-id:

$ ssh-copy-id -i ~/.ssh/mykey username@my_remote_host.org

 

验证SSH密钥

如果你想检查你已经有哪些密钥,这些可以在你的系统的.ssh目录中找到:

$ cd ~/.ssh

我们正在寻找以以下头文件开头的键:

-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
巧用Python登陆远程服务器

 

构造脚本

让我们安装库。

$ pip3 install paramiko scp

在我们编写一些有意义的Python代码之前,还有一件事要做!创建一个配置文件来保存连接到主机所需的变量。下面是我们进入服务器所需要的基本内容:

  • Host:我们试图访问的远程主机的IP地址或URL。
  • Username:这是您用于SSH到服务器的用户名。
  • Passphrase(可选):如果您在创建ssh密钥时指定了一个Passphrase,请在这里指定。请记住,您的SSH密钥密码短语与您的用户密码不同。
  • SSH Key:我们前面创建的密钥的文件路径。在OSX上,它们存在于系统的~/.ssh文件夹。我们目标的SSH密钥必须有一个附带的密钥,文件扩展名为.pub。这是我们的公钥;如果您遵循前面的步骤,那么应该已经为您生成了这个文件。

如果你试图从远程主机上传或下载文件,你需要包含两个额外的变量:

  • Remote Path:文件传输目标的远程目录的路径。我们可以上传东西到这个文件夹或者下载它的内容。
  • Local Path:与上述想法相同,但相反。为了方便起见,我们将使用的本地路径是简单的/data,并包含可爱的狐狸gif的图片。

现在我们有了创建一个config.py文件所需的一切:

"""Remote host configuration."""
from os import environ, path
from dotenv import load_dotenv
# Load environment variables from .env
basedir = path.abspath(path.dirname(__file__))
load_dotenv(path.join(basedir, '.env'))
# Read environment variables
host = environ.get('REMOTE_HOST')
user = environ.get('REMOTE_USERNAME')
ssh_key_filepath = environ.get('SSH_KEY')
remote_path = environ.get('REMOTE_PATH')
local_file_directory = 'data'

新建SSH客户端

我们将创建一个名为RemoteClient的类来处理与远程主机的交互。在我们搞得太复杂之前,让我们先用config.py中创建的变量实例化RemoteClient类:

"""Client to handle connections and actions executed against a remote host."""
class RemoteClient:
    """Client to interact with a remote host via SSH & SCP."""
    def __init__(self, host, user, ssh_key_filepath, remote_path):
        self.host = host
        self.user = user
        self.ssh_key_filepath = ssh_key_filepath
        self.remote_path = remote_path

 

到目前为止还没有什么令人印象深刻的:我们只是设置了一些变量,并将它们传递到一个无用的类中。让我们在不离开构造函数的情况下进一步讨论:

 

"""Client to handle connections and actions executed against a remote host."""
from paramiko import SSHClient, AutoAddPolicy, RSAKey
from paramiko.auth_handler import AuthenticationException, SSHException
class RemoteClient:
    """Client to interact with a remote host via SSH & SCP."""
    def __init__(self, host, user, ssh_key_filepath, remote_path):
        self.host = host
        self.user = user
        self.ssh_key_filepath = ssh_key_filepath
        self.remote_path = remote_path
        self.client = None
        self.scp = None
        self.conn = None
        self._upload_ssh_key()

我们已经添加了三个新东西来实例化我们的类:

self.client = None: self.Client最终将在我们的类中充当连接对象,类似于处理数据库库中的conn等术语。在显式连接到远程主机之前,我们的连接将为None。

self.scp = None与self.client相同,但专门处理传输文件的连接。

Self._upload_ssh_key()不是一个变量,而是一个在客户机实例化时自动运行的函数。调用_upload_ssh_key()是告诉我们的RemoteClient对象在创建时立即检查本地ssh密钥,以便我们可以尝试将它们传递到远程主机。否则,我们根本无法建立联系。

巧用Python登陆远程服务器

 

上传SSH密钥到远程主机

RemoteClient将从两个私有方法开始:_get_ssh_key()和_upload_ssh_key()。前者将获取本地存储的公钥,如果成功,后者将把这个公钥传递给我们的远程主机,作为访问的橄榄枝。一旦本地创建的公钥存在于远程机器上,该机器将永远信任我们的连接请求:不需要密码。我们将在此过程中包括适当的日志记录,以防我们遇到任何麻烦:

"""Client to handle connections and actions executed against a remote host."""
from os import system
from paramiko import SSHClient, AutoAddPolicy, RSAKey
from paramiko.auth_handler import AuthenticationException, SSHException
from scp import SCPClient, SCPException
from .log import logger
class 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_key
    def _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会捕捉到这一点,并在解释这一点之前就引发一个异常。正确地利用库的错误处理需要对“哪里出了问题”进行大量猜测,特别是在某些情况下,比如在一个我们都不会经常搞混的小空间中,可能存在许多未知的情况。

巧用Python登陆远程服务器

 

连接到客户端

我们将在客户机中添加一个名为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 error
        return 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密钥,设置超时将自动关闭我们可能忘记关闭的连接。如果选择以这种方式连接到主机,我们甚至可以传递端口和密码等变量。

 

断开连接

在使用完远程主机后,我们应该关闭与远程主机的连接。不这样做不一定是灾难性的,但是我遇到过一些实例,其中足够的挂起连接最终会使端口22的入站流量达到最大。不管您的用例是否认为重启是一场灾难或轻微的不便,让我们像成年人一样关闭我们该死的连接,就像我们在排便后擦屁股一样。不管您的连接环境如何,我提倡设置一个超时变量(如前所述)。无论如何。瞧:

class RemoteClient:
    ...
    def disconnect(self):
        """Close ssh connection."""
        if self.client:
            self.client.close()
        if self.scp:
            self.scp.close()

有趣的事实:设置self.client.close()实际上设置self。将client设置为等于None,这在您可能希望检查连接是否已经打开的情况下非常有用。

执行Unix命令

我们现在有了一个很棒的Python类,它可以找到RSA密钥、连接和断开连接。它确实缺乏做任何有用的事情的能力。

我们可以修复这个问题,并最终开始使用一个全新的方法来执行命令,我将适当地将其命名为execute_commands()(正确地说,“命令”可能不止一个,我们稍后将讨论这个问题)。所有这些工作都是由Paramiko客户端内置的exec_command()方法完成的,它接受一个字符串作为命令并执行它:

 

class RemoteClient:
    ...
    def execute_commands(self, commands):
        """
        Execute multiple commands in succession.
        :param commands: List of unix commands as strings.
        :type commands: List[str]
        """
        self.conn = self._connect()
        for cmd in commands:
            stdin, stdout, stderr = self.client.exec_command(cmd)
            stdout.channel.recv_exit_status()
            response = stdout.readlines()
            for line in response:
                logger.info(f'INPUT: {cmd} | OUTPUT: {line}')

 

我们刚刚创建的函数execute_commands()期望一个字符串列表作为命令执行。这部分是为了方便,但也因为Paramiko不会在命令之间运行任何“状态”更改(比如更改目录),所以我们传递给Paramiko的每个命令都应该假定我们是在服务器的根目录下工作的。我冒昧地说出了这样三条命令:

remote.execute_commands(['cd /var/www/ && ls',
                        'tail /var/log/Nginx/access.log',
                        'ps aux | grep node'])

我可以通过将cd path/链接到/dir && ls来查看一个目录的内容,但是运行cd path/to/dir后跟着ls会导致空无,因为ls第二次返回服务器根目录下的文件列表。

通过SCP上传(下载)文件

SCP既指用于将文件复制到远程计算机的协议(安全复制协议),也指利用此协议的Python库。我们已经安装了SCP库,所以请导入它。

SCP和Paramiko库相互补充,使得通过SCP上传非常容易。SCPClient()创建一个期望Paramiko进行“传输”的对象,我们通过self.conn.get_transport()提供了这个对象。从语法上讲,创建SCP连接依赖于我们的SSH客户机,但这些连接是独立的。关闭SSH连接而保持SCP连接打开是可能的,所以不要这样做。像这样打开一个SCP连接:

self.scp = SCPClient(self.client.get_transport())

上传单个文件很无聊,所以让我们来上传整个目录的文件。Bulk_upload()接受文件路径列表,然后调用_upload_single_file()

class RemoteClient:
    ...


    def bulk_upload(self, files):
        """
        Upload multiple files to a remote directory.


        :param files: List of paths to local files.
        :type files: List[str]
        """
        self.conn = self._connect()
        uploads = [self._upload_single_file(file) for file in files]
        logger.info(
f'Finished uploading {len(uploads)} files to {self.remote_path} on {self.host}')


    def _upload_single_file(self, file):
        """Upload a single file to a remote directory."""
        upload = None
        try:
            self.scp.put(
                file,
                recursive=True,
                remote_path=self.remote_path
            )
            upload = file
        except SCPException as error:
            logger.error(error)
            raise error
        finally:
            logger.info(f'Uploaded {file} to {self.remote_path}')
            return upload

我们的方法期望接收两个字符串:第一个是文件的本地路径,第二个是我们想要上传的远程目录的路径。

SCP的put()方法将把一个本地文件上传到远程主机。如果现有的文件恰好存在于我们指定的目标上,这将用相同的名称替换它们。这就是所有需要的!

下载文件

与SCP的put()对应的是get()方法:

class RemoteClient:
    ...
    def download_file(self, file):
        """Download file from remote host."""
        self.conn = self._connect()
        self.scp.get(file)


Tags:远程服务器   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
pom依赖 <dependency> <groupId>ch.ethz.ganymed</groupId> <artifactId>ganymed-ssh2</artifactId> <version>build210</version...【详细内容】
2022-03-17  Tags: 远程服务器  点击:(131)  评论:(0)  加入收藏
转自: https://kermsite.com/p/wt-ssh/由于格式问题,部分链接、表格可能会失效,若失效请访问原文密码登录 以及 通过密钥实现免密码登录Dec 15, 2021阅读时长: 6 分钟简介Windo...【详细内容】
2021-12-17  Tags: 远程服务器  点击:(489)  评论:(0)  加入收藏
用Python进行远程登陆服务器这篇文章介绍如何通过使用Paramiko和SCP Python库自动化远程服务器任务。使用Python来SSH到主机,执行任务,传输文件等。paramiko和scp是两个Python...【详细内容】
2021-06-04  Tags: 远程服务器  点击:(130)  评论:(0)  加入收藏
在python中使用日志最常用的方式就是在控制台和文件中输出日志了,logging模块也很好的提供的相应的类,使用起来也非常方便,但是有时我们可能会有一些需求,如还需要将日志发...【详细内容】
2020-09-25  Tags: 远程服务器  点击:(60)  评论:(0)  加入收藏
服务器维护涉及到很多内容,从服务器的硬件资源、软件资源到网络环境都需要专门维护,通过维护服务器运行,既要保证服务可用性,还要保护数据安全性,这些都离不开最基本的数据备份操...【详细内容】
2020-07-28  Tags: 远程服务器  点击:(179)  评论:(0)  加入收藏
▌简易百科推荐
工作中经常会使用nginx 配置反向代理,代理末尾的斜杠配置组合大体上可以分为8种,所以总是忘记 location 和 proxy_pass 最后面的斜杠会对代理结果有怎样的影响。今天我们就来...【详细内容】
2022-07-14  橘子泡茶    Tags:nginx   点击:(3)  评论:(0)  加入收藏
SSH 是一种广泛使用的协议,用于安全地访问 Linux 服务器。大多数用户使用默认设置的 SSH 连接来连接到远程服务器。但是,不安全的默认配置也会带来各种安全风险。具有开放 SSH...【详细内容】
2022-07-13  特斯汀软件测试    Tags:SSH服务器   点击:(6)  评论:(0)  加入收藏
问题:在Linux下访问windows ftp服务器时,出现中文乱码的问题。原因:这是由于中文编码不同的问题。Windows中文编码使用的是gbk,而linxu大多数版本默认的编码是utf8。解决方法:在...【详细内容】
2022-07-12  网站建设和维护    Tags:ftp服务器   点击:(7)  评论:(0)  加入收藏
1、RabbitMQ依赖于Erlang,需要先安装Erlang2、Erlang和RabbitMQ版本有对应关系http://www.rabbitmq.com/which-erlang.html2.3安装Erlang21.3先安装一些必要的依赖:yum -y ins...【详细内容】
2022-07-11  白驹王者报    Tags:RabbitMQ   点击:(10)  评论:(0)  加入收藏
有一部分的用户在网站建设过程中,比较过很多服务器的数据,其中争议最大的那就是云服务器和独立服务器,相比之下孰高孰低,经过长时间的积累经验传统物理服务器,也就是独立服务器技...【详细内容】
2022-07-08  华企网站建设  搜狐号  Tags:云服务器   点击:(11)  评论:(0)  加入收藏
网站进行服务器迁移前应做好哪些准备?当网站涉及从一台服务器迁移到另一台服务器时,即使是熟练的专业人员也会有各种担心疑虑,比如怕影响到网站seo优化效果、迁移失败出现服务...【详细内容】
2022-07-08  恒创科技  搜狐号  Tags:服务器迁移   点击:(11)  评论:(0)  加入收藏
【CSDN 编者按】你家是否还有堆在角落积灰的安卓旧手机?给它一个机会,让他变废为宝可好?来看看将其改造成Ruby Web服务器的攻略吧!原文链接:https://lbrito1.github.io/blog/2020...【详细内容】
2022-07-08    CSDN  Tags:Web 服务器   点击:(14)  评论:(0)  加入收藏
nginx web负载均衡配置下载nginx http://nginx.org/en/download.htmlwget http://nginx.org/download/nginx-1.20.2.tar.gz安装nginx 参考: https://www.cnblogs.com/-wei/p...【详细内容】
2022-07-08  新生代程序员    Tags:nginx   点击:(12)  评论:(0)  加入收藏
一、Tomcat 架构概览 1、Tomcat是什么?Apache Tomcat软件是Jakarta Servlet、 Jakarta Server Pages、 Jakarta Expression Language、 Jakarta WebSocket、 Jakarta Annota...【详细内容】
2022-07-08  java7794Vi    Tags:Tomcat   点击:(15)  评论:(0)  加入收藏
一、一个服务对应一个端口比如:访问网站网页http,对应80端口访问ftp下载上传文件用的,对应21端口用端口来定位服务,用地址来定位计算机;每一个服务就要占用一个端口,并且不能重复...【详细内容】
2022-07-07  一往直前的冲吧    Tags:端口   点击:(17)  评论:(0)  加入收藏
站内最新
站内热门
站内头条