Python e SSH

Uma tarefa muito comum dos administradores de sistemas linux é executar o mesmo comando em vários servidores distintos, isso com o objetivo de aplicar um patch de segurança, instalar um novo pacote, efetuar alguma configuração, até mesmo padronizar configurações.

Para isso utilizamos ferramentas como:
– Puppet
– Ansible
– Chef
– Fabric

E entre outras, essas são as mais conhecidas.

Mas é possível também fazer essas configurações através do Python, existe um módulo chamado paramiko que foi criado justamente para fazer conexões via SSH, então nesse post vou mostrar a vocês como se usa esse módulo.

O primeiro passo é instalar o paramiko

1
pip install paramiko

Agora segue o script completo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python
 
from paramiko import SSHClient
import paramiko
 
class SSH:
    def __init__(self):
        self.ssh = SSHClient()
        self.ssh.load_system_host_keys()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.ssh.connect(hostname='127.0.0.1',username='root',password='SENHA_DE_ROOT')
 
    def exec_cmd(self,cmd):
        stdin,stdout,stderr = self.ssh.exec_command(cmd)
        if stderr.channel.recv_exit_status() != 0:
            print stderr.read()
        else:
            print stdout.read()
 
if __name__ == '__main__':
    ssh = SSH()
    ssh.exec_cmd("apt-get update")

Agora vamos entender o que faz esse script.

A primeira coisa a se entender é que foi criada uma classe chamada SSH, assim é possível facilitar algumas coisas, pois no método construtor, definido por def __init__(self): tem uma sequencia de instruções para efetuar a conexão com um determinador servidor.

1
        self.ssh = SSHClient()

Essa linha faz a instância da classe SSHClient que foi importada do módulo paramiko logo no topo do script.

1
2
from paramiko import SSHClient
import paramiko

Na sequencia do construtor, foi definida a instrução:

1
        self.ssh.load_system_host_keys()

Essa instrução define que o paramiko vai ler todas as chaves cadastrados no arquivo ~/.ssh/known_hosts, assim evitamos ter que ficar dando um yes ou no na hora de conectar em um servidor via ssh.

1
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

Nessa linha é definido o que fazer quando a chave de um servidor não é encontrada no ~/.ssh/known_hosts, então foi definido o método paramiko.AutoAddPolicy(), assim quando um servidor for acessado pela primeira vez o paramiko automáticamente vai aceitar a chave desse servidor e cadastrar no arquivo known_hosts.

1
        self.ssh.connect(hostname='127.0.0.1',username='root',password='SENHA_DE_ROOT')

Nessa linha definimos o servidor em que vamos conectar, o usuário e a senha, caso o seu acesso seja feito através de chaves, é possível omitir o username e o password, deixando somente a chave hostname com o IP do servidor que você quer conectar.

Por que todas essas instruções foram colocadas no método construtor? Dessa maneira assim que for instanciado um objeto dessa classe SSH a conexão já é feita automaticamente, então o método exec_cmd pode executar os comandos diretamente, sem a necessidade de ficar efetuando a conexão antes de executar qualquer comando.

Agora vamos analisar o método exec_cmd.

O método exec_cmd tem apenas um parâmetro obrigatório, que é a variável cmd, ela deve receber uma string com o comando que deve ser executado via ssh no servidor.

1
        stdin,stdout,stderr = self.ssh.exec_command(cmd)

A linha acima usa o próprio atributo ssh da classe SSH, que contém uma instância do SSHClient do paramiko que já está conectada ao servidor, tudo isso foi realizado no construtor dessa classe, essa instância possui um método chamado exec_command que recebe uma string como parâmetro, que será o comando executado no servidor.

Quando o comando é executado esse método retorna uma tupla com 3 valores.
– Standard Input (Entrada padrão, normalmente uma entrada do teclado)
– Standard Output ( Saída padrão, o que aparece na tela )
– Stander Error ( Saída de Error, mensagem de erro mostrada na tela )

Todos foram abreviados como stdin,sdout,sdterr.

1
2
3
4
        if stderr.channel.recv_exit_status() != 0:
            print stderr.read()
        else:
            print stdout.read()

Depois de recebidos os valores retornados pelo exec_command, precisamos saber se o comando deu erro ou não, para isso foi feito esse if.

Na instrução stderr.channel.recv_exit_status(), é verificado se o valor retornado da saída de erro é diferente de 0, se esse valor for diferente de zero significa que um erro aconteceu. Por exemplo:
– erro 127 ( Command not found )
– erro 1 ( Erro ao executar o comando, pode ser um parâmetro invalido por exemplo)

Esses são os erros mais comuns.

Então se for retornado um erro a condição entra no primeiro bloco de instruções fazendo um print do erro do comando, caso contrário a saída padrão do comando será retornada na tela.

1
2
3
if __name__ == '__main__':
    ssh = SSH()
    ssh.exec_cmd("apt-get update")

Essas ultimas instruções são para testar o nosso script, a linha if __name__ == ‘__main__’: diz que o bloco abaixo só será executado se o script for executado via linha de comando, caso você faça um import desse arquivo essas instruções não serão executadas.

Na sequência é instanciada a nossa classe SSH dentro da variável ssh que acaba se tornando um objeto, nesse momento é feita a conexão com o servidor, uma vez que tudo foi definido no construtor da classe, então logo abaixo em ssh.exec_cmd(“apt-get update”) é enviado o comando apt-get update para atualizar a base de dados do apt-get no servidor que quisermos, o script irá demorar um pouco e se o comando for executado com sucesso na sua tela irá aparecer o resultado do comando executado.

13
Deixe um comentário

avatar
7 Comment threads
6 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
7 Comment authors
Alisson Machadorodrigo buchVagnerJosé CarlosDiego Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Lucas Oliveira
Visitante
Lucas Oliveira

Eu preciso rodar três scripts python no meu servidor ssh. Isso eu ja consegui fazer, abri três conexões diferentes e funcionou, porém qaunto eu fecho a conexão o script pra de rodar. Como posso solucionar isso?

Ricardo
Visitante
Ricardo

quando eu executo ‘apt-get install’, ele diz que falta permissão de sudo. Como faço para dar permissão?

Diego
Visitante
Diego

Boa noite, estou tendo dificuldades em executar outros comandos dentro desta mesma sessão, eu teria de colocar todos os comandos dentro da mesma linha?

José Carlos
Visitante
José Carlos

Bom dia, poderia me ajudar ?

Usando a lib paramiko existe alguma forma de ver o que o host remoto echoou no ssh.connect, tipo um print do stdout ?

Obrigado

Vagner
Visitante
Vagner

Tudo bem, Alysson?

Estou utilizando o Paramiko com autenticação via Kerberos e consigo executar comandos no shell remoto. Porém, eu gostaria de realizar forwarding dos tickets Kerberos, no servidor conectado via SSH, para autenticar no HDFS.

Você já fez algo do tipo?

Um abraço!

rodrigo buch
Visitante
rodrigo buch

Olá tenho um script parecido que aplica permissões em 3 nodes mas somente 1 parou de funcionar apresenta esse erro:
paramiko.transport: Adding ssh-ed25519 host key for 10.0.213.5: b’bcd2e970000dd4cc4be057fd45e88590′
paramiko.transport: Trying discovered key b’24a492d3c28ebd6eb6cf2a25778e153a’ in /root/.ssh/id_rsa
paramiko.transport: userauth is OK
paramiko.transport: Authentication (publickey) failed.