Gerencie chaves públicas no servidor com gist

Imagine que você está trabalhando com sua equipe no desenvolvimento de um novo produto e te pediram para configurar um servidor de testes para rodar o projeto.

Acontece que após a instalação e configuração do server você recebe o pedido de um amigo do time para que adicione a chave pública dele no authorized_keys, não é incômodo, depois de alguns dias outro amigo faz o mesmo pedido e o primeiro que te pediu teve que reinstalar o sistema e esqueceu de fazer backup das chaves e agora a tarefa de atualizar o authorized_keys começa a virar tortura.

Pensando nesse problema podemos pedir para que o servidor leia um gist com as informações necessárias para que essa tarefa seja realizada de forma mais prática.

Para isso vamos precisar criar um gist de preferência privado com a lista das chaves públicas de quem deve ser inserido nas configurações como mostra o exemplo:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5OnYust5S9hwLb4tAtVMOVlRmszam...
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCgRjYuFXqVv1x0WHnNxz3s4doTpx7v...
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsizI0Nt5wMunAYd2t3daTxnHZBMlW...

Feito isso vamos ao servidor configurar o cron para que faça a atualização de forma automática, $ sudo crontab -e:

0 0 * * * mkdir -pm 700 ~/.ssh > /dev/null 2>&1 ; curl -s -w \%{http_code} \
https://gist.githubusercontent.com/infoslack/867c9e934af23ee8ae6e/raw/\
9cde7b86acdda0a3f4d74ee801e4a7bae57f2a08/keys_white_list --output \
~/.ssh/authorized_keys_dl 2> /dev/null | grep 200 > /dev/null && mv -f \
~/.ssh/authorized_keys_dl ~/.ssh/authorized_keys && chmod 600 \
~/.ssh/authorized_keys > /dev/null 2>&1

Ok é assustador, mas vamos entender esse comando feio primeiro, estou informando para o cron que todo dia a meia-noite ele crie o diretório ~/.ssh com a permissão 700 e caso o diretório já exista que mantenha a sua estrutura, em seguida estamos redirecionando as mensagens de erro para /dev/null depois disso o curl entra em ação pegando as informações no nosso gist e salvando em um arquivo com o nome de authorized_keys_dl onde mais uma vez as mensagens de erro são ignoradas, como passamos um argumento para o curl adicionar ao final do arquivo o status http da url, fazemos um grep para verificar se está ok, caso não venha esse código mas um outro como 404 a atualização será interrompida. Depois disso movemos o arquivo baixado para o nome correto authorized_keys e alteramos a permissão para 600, sempre enviando o stderr para /dev/null.

Agora que entendemos o que está sendo feito, vamos melhorar essa instrução. Podemos elaborar um shell script bastante reduzido e claro mais legível, mas antes vamos dar uma olhada em uma feature legal que o github oferece, que é exibir as chaves públicas dos usuários, simples assim: https://github.com/infoslack.keys.

Dessa forma em vez de manter um gist com todas as chaves públicas, podemos ter uma lista de usuários, nosso script ficaria assim:

#!/bin/sh

URL="RAW_URL_PRIVATE_GIST"
TMP="/tmp/authorized_keys_dl"
ERR="/dev/null 2>&1"

mkdir -pm 700 ~/.ssh > $ERR

for user in $(curl --silent $URL)
do
  curl -s "https://github.com/"$user".keys" -w "\n" >> $TMP
done

mv -f $TMP ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys > $ERR

E o cron ficaria assim:

0 0 * * * /opt/scripts/update_authorized_keys.sh

E o gist teria a whitelist dos usuários:

infoslack
initsec
daniel_romero
...

O tempo do cron poderia ser ajustado para intervalos menores ou você poderia até mesmo fazer algo legal com o monit.

Happy Hacking ;)

Comentários