Compliance como código

Quando se tenta implantar boas práticas de segurança e compliance em ambientes cloud, um dos principais problemas é a comunicação. O time de compliance fala a língua das planilhas, o de dev a língua dos códigos e o sec/ops dos comandos e siglas.

Tentando diminuir essa distância a Chef.io (a mesma do software por trás do OpsWorks) criou o Inspec.io com a proposta de automatizar algumas verificações de segurança e em uma linguagem que seja facilmente compreendida por todos os envolvidos: Compliance, dev e sec/ops,

As informações sobre instalação podem ser encontradas aqui. Caso você queira simplesmente baixar os pacotes, pode acessar esse link.

Para automatizar a instalação no seu Amazon Linux (testado com 2017.03.1 HVM, SSD), basta adicionar o código abaixo no seu script de bootstrap, sua instância já vai subir com o inspec instalado.

sudo rpm --import https://packages.chef.io/chef.asc
cat >chef-stable.repo <<EOL
[chef-stable]
name=chef-stable
baseurl=https://packages.chef.io/repos/yum/stable/el/7/\$basearch/
gpgcheck=1
enabled=1
EOL
sudo yum-config-manager --add-repo chef-stable.repo
sudo yum install inspec

O que me chamou a atenção para essa solução em primeiro lugar foi a compatibilidade. Você pode fazer uma inspeção local, remota (por SSH ou WInRM) ou em um container docker. Em segundo foi a linguagem de fácil entendimento e o fato dele suportar a checagem de alguns recursos. E por fim o suporte em diversos sistemas operacionais: Linux, BSD, MacOS e Windows.

Vamos criar alguns exemplos para ilustrar a facilidade:

Lógico que todo primeiro exemplo deve ser um Hello World. No nosso caso um Hello Compliance. Então primeiro crie um arquivo chamado hello.txt com o conteúdo “Hello Compliance”. Na mesma pasta crie um arquivo chamado hello.rb com o conteúdo abaixo:

control "hello-compliance-1.0" do
title "Hello Compliance"
desc "Text should include the words 'hello compliance'."
describe file('hello.txt') do
its('content') { should match 'Hello Compliance' }
end
end

Salve e vamos rodar o seguinte comando:

inspec exec hello.rb

O resultado será algo semelhante a isto:

Profile: tests from hello.rb
Version: (not specified)
Target: local://
✔ hello-compliance-1.0: Hello Compliance
✔ File hello.txt content should match "Hello Compliance"
Profile Summary: 1 successful control, 0 control failures, 0 controls skipped
Test Summary: 1 successful, 0 failures, 0 skipped

Ou seja nosso teste rodou com sucesso.

Vamos dar uma olhada em cada item do nosso código:

control: Este é o nome do controle. Deve ser um nome único.
title: Este é um título que deve ser simples de se entender, para ser lido por pessoas.
desc: Descrição, opcional
describe file(‘hello.txt’): Este é recurso que será testado. O File é um recurso (Resources) que o inspec possui, para abrir arquivos. Poderia ser feito de outra maneira como vamos ver no próximo exemplo.
its(‘content’) { should match ‘Hello Compliance’ }: Agora sim o teste, o conteúdo do arquivo deve conter Hello Compliance.

Percebam que é bem amigável e legível inclusive por pessoas não técnicas.

Em algumas arquiteturas eu costumo usar o EFS que é um compartilhamento de rede gerenciado pela AWS, que escala automaticamente de acordo com a quantidade de espaço que você usa. Então é importante saber se o compartilhamento está montado em cada instância. Neste caso podemos fazer de duas maneiras

A primeira usando o comando ‘mount’:

control "mount-stdout-1.0" do
title "mount stdout"
desc "Verifica se a particao esta montada usando stdout."
describe command('mount') do
its('stdout') { should match /efs/ }
end
end

A segunda usando o recurso mount:

control "mount-resource-1.0" do
title "mount resource"
desc "Verifica se a particao esta montada usando resource."
describe mount('/efs') do
it { should be_mounted }
end
end

Vamos colar os dois conteúdos em um arquivo chamado efs.rb e rodar o comando:

inspec exec efs.rb

Como na sua máquina, provavelmente, não há um compartilhamento efs, você verá um resultado como:

Profile Summary: 0 successful controls, 2 control failures, 0 controls skipped
Test Summary: 0 successful, 2 failures, 0 skipped

Entendendo a nossa receita:

describe command(‘mount’): Rodar o comando mount
its(‘stdout’) { should match /efs/ }: Procurar pela regex efs na saída

describe mount(‘/efs’): Olhar o recurso mount, procurar pelo ponto de montagem /efs
it { should be_mounted }: Verificar se está montado

Pela infinidade de recursos você pode fazer configurações em daemons como SSH, neste caso verifica se o PAM está ativo na configuração e a porta do SSH é a 22:

describe sshd_config do
its('Port') { should cmp 22 }
its('UsePAM') { should eq 'yes' }
end

Verifica se o apache está ouvindo na porta 443:

describe apache_conf do
its('Listen') { should eq '443'}
end

Todos os testes que fizemos aqui foram locais, mas temos outros alvos também. No momento podemos usar os seguintes: local, SSH, WinRM e Docker.

Nosso exemplo do efs ficaria assim por SSH:

inspec exec efs.rb -i chave.pem -t ssh://ec2-user@ip_servidor

Caso fosse um container docker:

inspec exec efs.rb -t docker://id_container

Para se aprofundar recomendo o seguinte:

Espero ter ajudado a esclarecer o assunto. Caso tenha alguma dúvida, escreve aqui em baixo. Caso esteja testando, não esqueça de remover os recursos criados.


Originally published at medium.com on September 20, 2017 by Francisco Edilton.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.