Carreira Dev

14 nov, 2016

Acesso escalável e seguro com SSH

Publicidade

Controles de segurança consistentes e de alta confiabilidade são as expectativas comuns para qualquer administrador de sistemas. Como você entrega isso em uma rede com milhares de servidores que suportam milhares de engenheiros? A maioria das soluções prontas requer um compromisso em pelo menos uma dessas áreas – e nos recusamos a aceitar isso.

A maioria dos administradores de sistemas usa o padrão da indústria Secure Shell (SSH) para acessar os sistemas, e ainda assim muitas das suas características especiais não são amplamente alavancadas. No Facebook, aproveitamos esses recursos para usar SSH de uma forma que é confiável, seguro e gerenciável. SSH, mais especificamente OpenSSH, tem uma ótima maneira de fornecer a segurança e a confiabilidade que exigimos: certificados assinados com outorgantes.

Certificados assinados

Quando os administradores de sistemas instalam um servidor Linux, é comum criar um par de contas com senhas; eventualmente, alguns usuários têm acesso sudo para escalar privilégios. Gestão de conta local funciona bem com alguns servidores, mas com o crescimento da empresa, autenticação central, como LDAP e/ou Kerberos, é frequentemente usada para evitar a gestão de contas manualmente em cada servidor. Com o crescimento contínuo, os administradores de sistemas podem vir a perceber que a autenticação central é um ponto de falha único e potencialmente devastador. Se ela cai, eles vão perder o acesso a tudo, a menos que eles permitam login raiz para ter acesso aos sistemas. Ficar trancado fora de seu próprio sistema é uma das piores coisas que podem acontecer durante um incidente. Por exemplo, se uma interrupção do serviço derrubou sua infraestrutura de autenticação, você não seria capaz de efetuar login e corrigir a falha.

Além dos riscos de falhas de gerenciamento central, optar por autenticação de chave pública sobre senhas para os usuários significa ter de gerir as suas chaves públicas em todos os seus servidores. Se a dor de cabeça administrativa não fosse suficiente, não é incomum comprometer a sua própria segurança por ter chaves públicas desconhecidas nos arquivos authorized_keys. Além disso, authorized_keys requer definir confiança de par de chaves individuais, o que não escala.

Nós reconhecemos que a autenticação com certificados assinados fornece um único ponto de confiança com nenhuma dependência de qualquer infraestrutura de terceiros. Nós configuramos nossos servidores SSH para confiar em nossa autoridade de certificação (CA) e tudo que ela assina. Nós pegamos isso emprestado de um conceito simples e amplamente utilizado que os outros usam em toda a Internet para o tráfego HTTPS, e nós a adotamos no mundo do SSH.

Ao projetar um sistema de segurança, independentemente do propósito ou do protocolo, você precisa pensar na autenticação e na autorização em separado. A autenticação verifica a segurança de que o cliente é quem ele diz ser, mas não concede todas as permissões. Após a autenticação bem-sucedida, a autorização decide se ele pode ou não executar uma ação específica. Além disso, a contabilidade acompanha todo o processo, em caso de necessidade de investigar mais tarde.

Quando você tem um grande conjunto de usuários, você precisa de um diretório central para gerenciá-los. Para evitar um ponto único de falha, os nossos sistemas de produção só têm contas locais em /etc/passwd. A conta mais comum usada é a root. Os servidores SSH só aceitam logins root se o certificado de cliente tem algumas capacidades muito específicas dadas pelo CA, e o próprio CA determina quem pode usar SSH e onde. Porém, usar contas locais introduz um problema. O padrão Unix de login (utmp, wtmp) é de nenhum uso aqui, e o comando last só pode mostrar as contas locais que tenham sido logadas. No entanto, o OpenSSH pode dar informações detalhadas sobre qual certificado foi utilizado para autenticar, fornecendo informações suficientes para uma alta prestação de contas.

Nós também temos uma infraestrutura syslog centralizada, recolhendo registros em tempo real de toda a frota. Nós coletamos uma grande quantidade de logs, e a infraestrutura necessária para suportar isso pode rapidamente se tornar complexa. Sobre os agregadores, temos analisadores que interpretam e transformam registros em dados tabulares e enviam esses logs para bancos de dados de retenção de longo prazo, como o Hive. Isso cria um lugar central para a contabilidade de login. Mesmo que a conta local que usamos para login seja a root, para qualquer outra conta local, o sistema de contabilidade nos permite identificar facilmente quem logou onde e realizar uma análise estatística desses dados.

Autenticar como root pode soar um pouco contraintuitivo a princípio. No entanto, com uma infraestrutura de contabilidade de confiança, ele realmente melhora a segurança e confiabilidade, pois você pode aplicar melhor a responsabilidade pelas ações dos usuários. Você precisa ter um sistema robusto que garanta o acesso com base em regras de negócios estabelecidas. Fazemos isso através da definição de domínios de segurança de acordo com essas regras.

Domínios de segurança

Em ambientes típicos, os engenheiros têm acesso SSH direto à produção, uma vez que eles estão em alguma rede confiável. Em nossa infraestrutura, os engenheiros não têm acesso SSH direto aos sistemas de produção, impostas por firewalls de rede. Em vez disso, usamos um host bastião para atingir a produção. Os engenheiros podem se autenticar nesses hosts bastiões apenas a partir de redes confiáveis. Esses hosts usam LDAP centralizado e instalações Kerberos para compartilhar informações da conta, e eles exigem autenticação de dois fatores para proteção contra vazamento de senha. Depois que os usuários estão devidamente autenticados e autorizados, um processo paralelo contata nosso CA interno para solicitar um certificado SSH assinado. Esse certificado contém todos as permissões para o engenheiro específico. A partir do host bastião, tendo obtido o certificado apropriado, os engenheiros podem SSH em máquinas de produção como root ou outros usuários com menores privilégios. Dessa forma, temos certeza de que nenhum engenheiro tem mais acesso do que ele ou ela requer.

Nós não colocamos certificados SSH em laptops porque é difícil controlar tudo o que um funcionário individual pode fazer com eles. Mesmo que eles sejam gerenciados centralmente, os laptops são mais propensos a vulnerabilidades que os servidores bastião. Por isso, não confiamos neles com chaves privadas SSH.

Implementação

Nós cobrimos as filosofias de autorização e autenticação. Agora vamos passar para a implementação. O que eu descrevi acima pode parecer assustador, mas não é tão complicado.

Vamos examinar alguns exemplos sobre como configurar uma autoridade de certificação rudimentar para assinar certificados e servidores de produção hipotéticos e confiar nele, aceitando apenas determinados princípios.

Primeiro, crie o seu próprio CA, que é essencialmente apenas um par de chaves normal:

$ umask 77  # you really want to protect this :-)
$ mkdir ~/my-ca && cd ~/my-ca

$ ssh-keygen -C CA -f ca

Você pode decidir se você quer ter uma senha para proteger a chave privada.

Neste ponto, você tem dois arquivos, ca (a chave privada) e ca.pub (a chave pública). Você precisa distribuir a ca.pub para toda a sua frota. Lembre: isso é feito para ser público, então bloqueio de acesso completo não é o objetivo. Para este exemplo, vamos colocá-lo em /etc/ssh/ca.pub. Você pode alterar as permissões de ca.pub para 0644.

Agora, configure seus servidores SSH para confiarem nele com esta única alteração de linha em /etc/ssh/sshd_config:

TrustedUserCAKeys /etc/ssh/ca.pub

Agora que você tem uma cadeia de confiança, você pode começar a gerar certificados. Idealmente, o CA deve ser um servidor muito seguro que apenas a equipe de segurança pode acessar. Em lugares pequenos, pode ser OK ter um ser humano assinando os pedidos de certificado. Para implementações maiores, um sistema que faz tudo isso automaticamente e de forma programática é melhor. Como você pode imaginar, nós temos o último no Facebook. Sob o capô, a nossa complexa infraestrutura de CA simplesmente recebe uma chave pública, executa todas as verificações de segurança, invoca o ssh-keygen para assinar com a chave privada da CA, e retorna o certificado assinado de volta ao cliente.

Uma prática de segurança muito importante é que as chaves privadas nunca devem deixar os sistemas onde foram geradas, não importa o quão seguro o transporte seja.

Em um terminal de usuário, não no servidor CA, vamos gerar uma chave para eles:

$ ssh-keygen -t ecdsa  # or -t rsa, up to you
Generating public/private ecdsa key pair.
Enter file in which to save the key (/home/mfdutra/.ssh/id_ecdsa):
Enter passphrase (empty for no passphrase):***
Enter same passphrase again:***
Your identification has been saved in /home/mfdutra/.ssh/id_ecdsa.
Your public key has been saved in /home/mfdutra/.ssh/id_ecdsa.pub.
...

Em seu diretório .ssh/, você verá id_ecdsa e id_ecdsa.pub. Copie o último para o servidor CA e assine-o. Como essa é uma informação pública, o transporte não é importante. Você pode copiar e colar, ou enviar por fax; só não copie id_ecdsa em qualquer lugar.

No servidor CA:

$ ssh-keygen -s ca -I mfdutra -n root -V +1w -z 1 id_ecdsa.pub

A página do manual do ssh-keygen tem uma ótima explicação para cada argumento usado. Basicamente, nós estamos assinando id_ecdsa.pub com ca. O ID do certificado será mfdutra e o único mandante que terá será o root. É válido por uma semana e tem o número de série 1. Você deve ter id_ecdsa-cert.pub agora. Copie isso de volta para o terminal de usuário e coloque sob .ssh/. Lembre-se: essa é uma informação pública, e esse certificado não funciona sem a respectiva chave privada.

Desde que não tenhamos configurado os servidores para exigir um conjunto específico de princípios, a configuração do sshd padrão permitirá a esse certificado logar como qualquer usuário em sua lista principal. Como eu usei -n root para criar o certificado, eu posso logar como root em qualquer sistema que confia no CA. Se você não tem um esquema de autorização especial em sua organização, isso provavelmente é o suficiente. No entanto, você pode querer ter pessoas logando como outro usuário local e, em seguida, você pode usar sudo para escalar, colocando qualquer outro usuário no argumento -n, que leva uma lista de autoridades separadas por vírgulas.

Você pode inspecionar um certificado com este comando:

$ ssh-keygen -Lf id_ecdsa-cert.pub
id_ecdsa-cert.pub:
  Type: ecdsa-sha2-nistp256-cert-v01@openssh.com user certificate
  Public key: ECDSA-CERT ...
  Signing CA: ECDSA ...
  Key ID: "mfdutra"
  Serial: 1
  Valid: from 2016-01-13T15:26:00 to 2016-01-20T15:27:00
  Principals:
    root
  Critical Options: (none)
  Extensions:
    permit-X11-forwarding
    permit-agent-forwarding
    permit-port-forwarding
    permit-pty
    permit-user-rc

Agora que você tem o certificado junto com a chave privada, você pode usar SSH em qualquer lugar como root.

$ ssh root@any-system-that-trusts-my-ca

Uma vez que você está logado lá, veja o registro de autenticação. Você vai ver algo como:

Accepted publickey for root from 1.2.3.4 port 123 ssh2: ECDSA-CERT ID mfdutra (serial 1) CA ECDSA fingerprint...

Você pode ver que, mesmo quando se autentica diretamente como root, o servidor ainda pode identificar o certificado utilizado para autenticar – nesse caso, com ID mfdutra. Isso significa que usar uma -I correta com ssh-keygen é muito importante, porque ela vai identificar o certificado. Usar um número de série único também é recomendado, para que você possa identificar cada certificado individual emitido. Na verdade, se você quiser usar listas de revogação, números de série originais são uma exigência.

A seguir, vamos definir zonas de segurança diferentes. Para este exemplo, vamos criar três zonas: zone-webservers, zone-databases e root-everywhere.

Agora, vamos configurar um dos nossos servidores para aceitar apenas determinados princípios. Adicione esta linha ao /etc/ssh/sshd_config:

AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u

Preencha o arquivo principals:

$ mkdir /etc/ssh/auth_principals
$ echo -e 'zone-webservers\nroot-everywhere' > /etc/ssh/auth_principals/root

Você pode controlar o acesso a qualquer usuário local, criando os arquivos em /etc/ssh/auth_principals.

Faça um reload do servidor SSH e tente SSH usando o certificado gerado antes. Você terá o acesso negado e a seguinte linha vai aparecer no log de autenticação no servidor:

error: Certificate does not contain an authorized principal

Por que criar uma zona denominada root-everywhere? Porque é conveniente quando determinados usuários precisam ser capazes de fazer login em todos os lugares. É uma opção melhor do que colocar todas as entidades possíveis no certificado de usuário, o que é difícil de gerenciar em longo prazo.

Agora você pode voltar para o servidor CA e gerar um novo certificado para esse usuário, com um diretor diferente:

$ ssh-keygen -s ca -I mfdutra -n zone-webservers -V +1w -z 2 id_ecdsa.pub

Com esse novo certificado de volta no terminal de usuário, você pode SSH para o sistema com sucesso, porque haverá pelo menos um cruzamento entre a lista de autoridades que você tem em seu certificado e a lista de autoridades aceita pelo servidor.

Obtenha outro servidor e configure-o na zona de zona zone-databases. Jogue com os certificados para ver como eles funcionam. Sua imaginação é o limite!

Próximos passos

O que está aqui abrange a abordagem que o Facebook adotou para controlar a autorização em sistemas de produção. Temos investido tempo na construção de zonas seguras e um sistema de gerenciamento de controle de acesso sofisticado. Várias organizações terão projetos diferentes, eles devem refletir as necessidades da sua organização; não há um modelo que resolva tudo para todos.

Quer saber mais? Consulte a página do manual do ssh-keygen, especialmente a seção sobre opções especiais (-O). Você pode fazer algumas coisas muito legais com certificados, tais como ter um que só permite a execução de um comando específico, ou anexar um certificado para um host específico.

Você também pode usar certificados relacionados para hospedar chaves. Com isso, os clientes SSH podem confiar automaticamente em todos os hostos com um certificado de acolhimento assinado pelo CA, eliminando a necessidade de aceitar manualmente cada novo host que você tiver no SSH.

Alguns conselhos antes de partir: quando você construir seu CA, seja ele um pequeno script ou um sistema complexo, certifique-se de manter o controle de todos os certificados que são emitidos. Se você se encontra na situação infeliz de ter um certificado comprometido (e suas respectivas chaves privadas) e não sabe como revogá-las, o seu último recurso é rotacionar todo o CA. Se você acabar tendo um CA programático, considere ter certificados de curta duração, por exemplo, 24 horas. Isso encurta a janela de oportunidade para um ataque se você tiver sido comprometido.

Acima de tudo, proteja a sua chave privada de CA e considere rotacioná-la regularmente.

***

Artigo escrito por Malon Dutra. A tradução foi feita pela Redação iMasters. Você pode conferir o original em https://code.facebook.com/posts/365787980419535/scalable-and-secure-access-with-ssh/.