Me deparei esses dias com o seguinte problema: alterar as senhas de um grupo de servidores ao mesmo tempo. De inicio parece um problema simples, mas depois vimos que havia o problema dos certificados de autenticação com o que alguns usuários entravam nos servidores e para piorar, alguns servidores precisam se comunicar entre eles e por se fosse pouco, o gestor queria aproveitar para limpar os certificados de acesso para deixar só o mínimo necessários.
Requisitos mínimos
Bom, se há um mínimo necessário -pensei- é por ai que começaremos. Desenvolvi um script que faço aqui publico tendo como ponto de partida duas coisas: Uma lista de servidores e um conjunto dos certificados mínimos que é necessário para garantir que certos usuários possam acessar o conjunto de servidores.
Para poder rodar o script, você precisa ter o netcat instalado em sua máquina assim como acesso certificado aos servidores em questão (no nosso caso mais de 7 servidores usando CentOS, Redhat, Debian e Ubuntu).
Instalar o netcat se resume a puxar ele do repositório. Por exemplo, em Ubuntu fica assim:
sudo apt-get install nc
Para certificar-se perante os servidores, você primeiro cria um par de chaves assim:
ssh-key-gen
E mande enter em tudo
Depois você copia sua chave para os servidores como no seguinte exemplo:
ssh-copy-id root@192.168.0.9
Isso supondo que se trata do servidor com ip 192.168.0.9 e o usuário root. Falando nisso, sem acesso root aos servidores, nada feito.
Repita esta última operação para cada servidor da sua rede.
Depois crie uma pasta na qual colocará o script que está no final deste post.
Além do script, crie um arquivo chamado newPassword dentro da pasta contendo unicamente a nova senha.
Também crie um arquivo chamado servers.txt contendo a lista dos IPs dos seus servidores.
Crie um arquivo authorized_keys nessa pasta contendo as chaves públicas que você deseja permitir acesso em todos os servidores da lista. Ou seja, em cada linha deste arquivo vá sua chave pública, a chave do seu colega, do seu chefe, e assim por diante.
Garanta direitos de execução ao script assim:
chmod +x updateServerPasswords.sh
E finalmente rode o script.
./updateServerPasswords.sh
Como funciona
O scrips percorrera a lista de IPs, verificará o tipo de servidor que é (a linhagem, na realidade) e se lhe for possível (redhat e debian) alterará a senha do root para a indicada. Recolherá a chave pública desse servidor para disponibilizá-la ao grupo de servidores. Dessa forma, eles passam a se conhecer e não precisam de senha.
Cria um arquivo temporário authorized_keys contendo os certificados liberados para os usuários mais os certificados de cada servidor da lista.
Finalmente. Coloca o novo authorized_keys em cada servidor. Limpa a entrada correspondente a cada servidor substituindo-as pelas novas.
O script
#!/bin/bash
#
# The MIT License (MIT)
#
# Copyright (c) 2015 Esteban Daniel Dortta
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
function checkPort
{
nc -z $1 $2
if [ $? -lt 1 ]; then
echo 1
else
echo 0
fi
}
if [ ! -d ssh-backup ]; then
mkdir ssh-backup
fi
echo "~/.ssh/known_hosts" > /tmp/remove_known_hosts.sh
known=''
winHosts=''
if [ $# -lt 1 ]; then
serverList=`(cat servers.txt)`
else
if [ -f "$1" ]; then
serverList=`(cat "$1")`
else
serverList=$1
fi
fi
for ip in $serverList; do
ping -c 1 $ip > /dev/null
ret=$?
ret=`(echo $ret | awk '{ print $1 }')`
if [ $ret -gt 0 ]; then
ret=""
else
ret="OK"
fi
port=''
if [ "$ret" == "OK" ]; then
ok22=$(checkPort $ip 22)
if [ $ok22 -lt 1 ]; then
ok2002=$(checkPort $ip 2002)
if [ $ok2002 -lt 1 ]; then
ok135=$(checkPort $ip 135)
if [ $ok135 -gt 0 ]; then
port=135
fi
else
port=2002
fi
else
port=22
fi
unknownDistro="UNKNOWN"
RHDistro="Redhat"
DebianDistro="Debian"
if [ "$port" == "22" ] || [ "$port" == "2002" ]; then
redhat=`(ssh -p $port root@$ip [[ -f /etc/redhat-release ]] && echo $?)`
debian=`(ssh -p $port root@$ip [[ -f /etc/debian_version ]] && echo $?)`
distro="$unknownDistro"
if [ $redhat -eq 0 ]; then
distro="$RHDistro"
else
if [ $debian -eq 0 ]; then
disto="$DebianDistro"
fi
fi
echo -e "$port\t$ip\t$distro ---------------------------------------------";
if [ $distro != "$unknownDistro" ]; then
scp -P $port newPassword root@$ip:/tmp/newPassword
if [ "$distro" == "$RHDistro" ]; then
echo "Changing Redhat style password"
ssh -p $port root@$ip "passwd root --stdin < /tmp/newPassword"
else
if [ "$distro" == "$DebianDistro" ]; then
echo "Changing Debian style password"
ssh -p $port root@$ip "sed 's/^/root\:/' /tmp/newPasswd | chpasswd"
fi
fi
ssh -p $port root@$ip rm /tmp/newPassword
fi
scp -P $port root@$ip:/root/.ssh/id_rsa.pub ssh-backup/id_rsa_$ip.pub
scp -P $port root@$ip:/root/.ssh/authorized_keys ssh-backup/authorized_keys.$ip
echo "ssh-keygen -R [$ip]:$port" >> /tmp/remove_known_hosts.sh
echo "ssh-keyscan -p $port -H $ip >> ~/.ssh/known_hosts" >> /tmp/remove_known_hosts.sh
known="$known$ip:$port ";
fi
if [ "$port" == "135" ]; then
winHosts="$winHosts ";
fi
fi
if [ "$port" == "" ]; then
echo -e "(n/a)\t$ip ---------------------------------------------"
fi
done
cat authorized_keys > /tmp/authorized_keys
for server in $known; do
ip=`echo $server | awk -F: '{ print $1 }'`
cat ssh-backup/id_rsa_$ip.pub >> /tmp/authorized_keys
done
for server in $known; do
ip=`echo $server | awk -F: '{ print $1 }'`
port=`echo $server | awk -F: '{ print $2 }'`
echo "Copying to $ip:$port ---------------------------------------"
scp -P $port /tmp/remove_known_hosts.sh root@$ip:/tmp/remove_known_hosts.sh
ssh -p $port root@$ip chmod +x /tmp/remove_known_hosts.sh
ssh -p $port root@$ip /tmp/remove_known_hosts.sh
scp -P $port /tmp/authorized_keys root@$ip:/root/.ssh/authorized_keys
ssh -p $port root@$ip chmod 0600 /root/.ssh/authorized_keys
done
echo "Windows hosts: $winHosts"