Desenvolvimento

30 mar, 2015

Modelando o gerenciador de pacotes de dependências no gerenciador de configurações

100 visualizações
Publicidade

A mais recente falha na glibc (CVE-2015-0235) e a vulnerabilidade heartbleed OpenSSL (CVE-2.014-0.160) deram a mim e a outros administradores de sistemas um pouco de trabalho. Isso me fez pensar: o que podemos fazer para aliviar nossas dores?

Gerenciadores de pacotes são inteligentes

Eu sou um usuário ávido e adepto do Puppet como minha ferramenta de gerenciamento de configuração preferida. Então, pensei em tornar a correção de vulnerabilidades apresentadas no CVE menos dolorosa. Aqui está o que eu estou pensando no momento, mas não acho que seja uma estratégia viável a longo prazo.

Para começar, seria muito demorado gerenciar e manter isso em sincronia com a realidade. Isso também vai deixar o Puppet insuportavelmente lento. Mas é apenas um pensamento, e vou ver até onde ele me leva – talvez isso desencadeie uma discussão ou duas e acabe por pegar o caminho certo.

Então, aqui está o meu pensamento: o gerenciador de pacotes tem dependências embutidas em seu modelo de empacotamento (nos arquivos de especificações de um RPM, por exemplo, e em arquivos Debian também). Esses gerenciadores de pacotes sabem que, para instalar, por exemplo, o httpd, o sistema operacional também precisa das bibliotecas glibc, apr, openssl (se o mod_ssl for solicitado).

(example RPM SPEC code)
...
Requires(post): openssl >= 0.9.7f-4, /bin/cat
Requires(post): systemd-units
...

O gerenciador de pacotes do sistema operacional tem todos esses detalhes. O gerenciador de configurações, não.

Teria sido muito fácil, no caso do bug Heartbleed, simplesmente colocar isso no nosso código do Puppet e disparar a atualização de tudo.

package { 'openssl':
  ensure => latest,
}

E assim tudo seria feito por ele.

Mas não é bem assim que funciona. Muitos serviços dependem desse pacote e carregam os arquivos da biblioteca na inicialização, uma vez. Se a biblioteca for atualizada, o serviço precisa reiniciar para ler essas novas atualizações.

Modelando o gerenciador de pacotes

Então, e se “reconstruirmos” os gerenciadores de pacotes Apt ou Yum em nossos gerenciadores de configuração? Se pudéssemos importar toda a árvore de dependências em nossas configurações, o cenário acima realmente funcionaria?

Isso pode ser feito.

Em uma versão simplista, seria parecida com isto:

service { 'httpd':
  ensure    => running,
  subscribe => Package [ 'openssl', 'abr', 'glibc' ],
}

service { 'mysqld':
  ensure    => running,
  subscribe => Package [ 'openssl', 'glibc' ],
}

...

Se podemos modelar o pacote de dependências dessa forma, corrigir o bug Heartbleed teria sido tão fácil como apenas atualizar a versão do OpenSSL.

# This will trigger a reload of the httpd and mysqld service,
# with the subscribes above.
package { 'openssl':
  ensure => latest,
}

Mas esse modelo simplesmente não é sustentável.

O Debian tem uma ferramenta para ajudar com isso, chamada checkrestart, no pacote debian-goodies. Ela ajuda a listar todos os serviços que ainda estão usando a versão antiga de arquivos atualizados. É basicamente uma versão mais bonita do one-liner.

$ lsof | grep libssl | awk '{print $1}' | sort | uniq

Eu achava que somente o Debian tinha uma ferramenta para ajudar a encontrar bibliotecas antigas em uso. Graças aos comentários de leitores, aprendi que o Red Hat e o CentOS têm uma ferramenta no pacote yum-utils chamada needs-restarting que lista todos os serviços que necessitam de reinicialização para carregar as bibliotecas recém-instaladas e/ou atualizadas.

E se…

Acho que existem duas soluções possíveis para esse problema – pelo menos, duas soluções nas quais posso pensar agora.

Uma delas é realmente reconstruir as dependências do gerenciador de pacotes no gestor de configurações (como no meu exemplo acima, ao realmente escrevê-lo no gerenciador de configuração DSL), mas de uma forma automatizada. Nós não seríamos capazes de definir corretamente todas as dependências manualmente, por isso teria de ser automatizado.

Mas como seria possível ajustá-lo para seus módulos atuais? Você deixa um script robotizado modificar seu código? Parece improvável.

De forma alternativa, uma ferramenta como o checkrestart pode ser portada e construída em nosso conjunto de ferramentas de gerenciamento de configuração. Claro, podemos construir soluções hack com ferramentas como mcollective, escrever agentes personalizados e tê-los executando cada tarefa para nós (antes de dizer que “essa é provavelmente a melhor solução”: considero um hack como uma limitação do CMS).

Essa solução não seria mais adequada nas próprias ferramentas de gerenciamento de configuração?

Com certeza, você não pode atualizar cegamente as bibliotecas e reiniciar as ferramentas e serviços do gerenciador de configurações sempre que quiser. Para alguns servidores, isso não é apenas uma opção. Mas se o ato de reiniciar os serviços precisa acontecer de qualquer maneira, para resolver uma vulnerabilidade, por exemplo, então por que não torná-lo um pouco mais fácil para nós?

***

Mattias Geniar faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: https://ma.ttias.be/modeling-package-manager-dependencies-config-management/