Em uma situação específica, não poderíamos usar o método de publicação de chaves para automatizar tarefas com o
ssh.
Precisei fazer isso porque, além de outros problemas, a quantidade de estações era muito grande, e existia a possibilidade do arquivo que guarda a lista de chaves ser alterado, então para reduzir a possibilidade de erros, tivemos que parar pra pensar e eis a solução encontrada.
No método de publicação de chaves para usar o
ssh sem senha, são geradas o par de chaves pública e privada, sendo que a chave pública é enviada ao destino alvo da comunicação, dai em diante é criada uma relação de confiança entre a comunicação dessas estações mediante a validação automática das chaves.
No caso de uma comunicação sem o uso do par de chaves, é solicitado a senha do usuário para que a comunicação possa ocorrer, só que existem duas barreias que impedem a automatização de tarefas:
- A senha será solicitada no terminal (óbvio);
- Enquanto não for confirmado, é solicitado a adição da estação remota à lista de estações conhecidas (Know Hosts).
Para romper a barreira número 1, para passar a senha para o ssh (e seu conjunto de ferramentas como o sftp, scp, ...), pode ser usado o
sshpass, o mesmo não vem instalado por padrão, para instalar use (Debian e derivados):
sudo apt-get install sshpass
E para romper a barreira número 2, precisei pesquisar um pouco e implementar um script bem simples, que usa o
expect, o mesmo também não vem instalado por padrão, para instalar use:
sudo apt-get install expect
#!/usr/bin/expect -f
set timeout 2
set xHost $argv
spawn ssh $xHost
expect {
"*yes\/no*"
{
send "yes\r"
exp_continue
}
}
return 0
Para usá-lo, basta chamá-lo fornecendo o usuário e nome da estação:
./script usuario@estacao
Basicamente o script vai tentar acessar a estação por ssh enviando o usuário e estação fornecidos caso a estação solicite a confirmação para adicionar a estação remota na lista de hosts conhecidos, geralmente localizada em
~/.ssh/known_hosts, o expect trata de enviar a confirmação "yes", adicionando assim a estação a lista de hosts conhecidos, e essa solicitação não será mais feita em um posterior acesso.
Caso necessário aumente o tempo de espera "set timeout 2" e/ou faça o debug do script trocando:
#!/usr/bin/expect -f
Por:
#!/usr/bin/expect -D 1
Assim poderás ver passo a passo que o script faz.
Exemplo de uso
Agora um exemplo de script que usando o
scp copia todos os dados de um diretório remoto para a estação local. O que uso e bem diferente deste, pois o mesmo obtêm uma lista de estações que terão arquivos a serem transmitidos.
Script base seria:
#!/bin/bash
# Desenvolvido por: Raimundo Portela - rai3mb[at]gmail.com
#-----------------------------------------------------------
# definicao de variaveis usadas
ARQ_LOG='/tmp/log_importacao'
USUARIO='usuario' # usuario de acesso
SENHA='senha' # senha do usuario
ESTACAO='192.168.0.100' # nome ou ip da estacao
DIRETORIO='/opt/dados' # diretorio com os arquivos remotos
DIRETORIO_BKP='/opt/bkp' # diretorio local para o destino da copia
function func_pegaArquivo() {
sshpass -p "$SENHA" scp -r "$USUARIO"@"$ESTACAO":"$DIRETORIO" "$DIRETORIO_BKP" 2>/tmp/retorno_scp
export RETORNO=$?
func_case
}
function func_case() {
case ${RETORNO:-0} in
0) echo "Sucesso"
wget --post-data "status=1" http://servidor/Log.php -O /tmp/retorno_php -o "$ARQ_LOG"
;;
23) echo "Erro de permissão"
wget --post-data "status=3" http://servidor/Log.php -O /tmp/retorno_php -o "$ARQ_LOG"
;;
6) echo "Erro na confirmação do ssh"
# caso o host nao for conhecido,
# o script expect.sh trata de adicioná-los a lista de host conhecidos
./expect.sh "$USUARIO"@"$ESTACAO"
func_pegaArquivo
;;
1) echo "Arquivo nao encontrado"
wget --post-data "status=4" http://servidor/Log.php -O /tmp/retorno_php -o "$ARQ_LOG"
;;
255) # host desconhecido ou fora de rede
echo 'host desconhecido ou fora de rede'
wget --post-data "status=2" http://servidor/Log.php -O /tmp/retorno_php -o "$ARQ_LOG"
;;
esac
}
func_pegaArquivo
Obs.: Estou enviando dados via wget com --post-data para um servidor web com php e banco de dados.
Enfim, se quiser poderá aprimorar suas rotinas de backup, captura de arquivo etc, coletando alguns dados, enviados via wget e salvando em um banco de dados.
Em php o início do código poderia ser:
<?php
// obtêm o conteúdo de status enviado via POST:
$status = isset( $_POST['status'] ) ? $_POST['status'] : NULL;
if ( $status != NULL ) {
$data = date('Ymd');
$hora = date('Hi');
/*
* complemente com seu código, que registra num base de dados ou não ;-)
* um exemplo seria:
*/
$sql = "INSERT INTO tb_status(codigo, data, hora) VALUES($status, $data, $hora)";
try {
$conn = new PDO("pgsql:dbname=NOME_BASE; user=USUARIO; password=SENHA;host=SERVIDOR;port=5432");
$conn->exec( $sql );
} catch (PDOException $e) {
echo $e->getMessage();
}
}
?>
Conclusão
Mesmo tendo o problema da senha ficar exposta no script, não foi tão problemático, já que o script fica em um servidor com acesso restrito.
A grande vantagem foi o fato de não usar o método de publicação de chaves, que pelo fato de termos muitas estações e a possibilidade de em algum a lista de chaves remota ser alterada, teríamos que monitorar essas mudanças.
Espero que seja útil.
@rai3mb