Falco Security — Seus containers estão seguros? Saiba detectar atividades suspeitas em tempo de execução.

Iluiz Sousa
11 min readMar 27, 2022

--

A adoção de novas metodologias, arquiteturas e frameworks para a entrega de software acabou por difundir cada vez mais o uso de containers, no entanto, por mais abstração que a utilização de containers possa trazer, as recentes notícias de ataques cibernéticos mostram que containers também acabam sendo um dos principais vetores desses ataques.

Em outra oportunidade já falei sobre identificação e tratamento de vulnerabilidades de containers desde a sua concepção. Você pode ver mais detalhes sobre isso no postQuem tem medo de vulnerabilidades de containers?”.

A ideia desse post é falar um pouco sobre segurança de containers em tempo de execução (Runtime Security).

Segurança de contêineres em tempo de execução significa analisar as atividades no ecossistema do aplicativo do contêiner, desde a análise da atividade do contêiner em si, quanto a atividade do host até o monitoramento dos protocolos e conexões de rede.

Os containers atendem ativamente uma quantidade massiva de requisições da Internet e/ou de microsserviços internos e são constantemente escaneados, mapeados e consequentemente sofrem tentativas de ataques e exfiltração de dados.

Nesse cenário, como então podemos ter conhecimente sobre o que está acontecendo dentro dos nossos containers?

O projeto Falco

O projeto Falco, trata-se de uma ferramena de segurança de código aberto originalmente desenvolvida pela Sysdig, Inc. A ferramenta se propõe a fazer justamente a segurança em tempo de execução nos mais diversos workloads: containers, instancias, ambientes cloud, dentre outros.

Atualmente o Falco faz parte dos projetos nativos da CNCF ( Cloud Native Computing Foundation), se tornando assim uma ferramenta praticamente mandatória quando o assunto é segurança de containers.

Como o Falco funciona?

O Falco se utiliza das chamadas de sistema (system calls) para monitorar e garantir a segurança do ecossistema onde sua carga de trabalho está sendo executada.

Além das chamadas de sistemas, o Falco pode utilizar também como uma fonte de dados os eventos de audit do Kubernetes, se tornando uma ferramenta ainda mais poderosa com a possibilidade de analisar os eventos envolvendo o ecossistema do kubernetes também (veremos isso mais à frente ainda nesse post).

Uma vez que o Falco tenha as suas fontes de dados configuradas, ele analisa os eventos em tempo de execução e através de um conjunto de regras, o mecanismo do Falco envia alertas quando essas regras são acionadas (também iremos detalhar mais a frente as regras do Falco).

Diagrama do Falco

Regras do Falco

As regras do Falco são os itens do qual a ferramenta utiliza para analisar as chamadas de sistemas. Caso um chamada de sistema “dê match” com uma regra existente e ativa, o Falco envia um alerta sobre esse evento, classificando de acordo com nível de crticidade especificado.

Vamos detalhar melhor uma regra do Falco:

rule: shell_in_container
desc: notice shell activity within a container
condition: evt.type = execve and evt.dir=< and container.id != host and proc.name = bash
output: shell in a container (user=%user.name container_id=%container.id container_name=%container.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)
priority: WARNING

Esses são os cinco campos obrigatórios de uma regra do Falco:

  • rule: Um nome objetivo para regra
  • desc: Breve descrição da regra
  • condition: Uma expressão para verificar se na chamada de sistema existe algo que bata com o filtro especificado.
  • output: Mensagem que será apresentanda quando a regra for acionada
  • priority: É o nível de criticidade que determinada regra esta associada

O Falco possui mais de 60 regras ativas por default, essas regras podem e devem ser customizadas afim de representar o comportamento do ambiente.

Você pode obter mais detalhes a respeito das regras do Falco aqui.

Instalando o Falco no ambiente

É possível instalar o Falco de diversas maneiras: na máquina local, cloud provider, num cluster kubernetes, num cluster k3s, dentre outras formas.

Nesse post iremos instalar o Falco num ambiente kubernetes gerenciado, no caso, EKS da AWS.

A forma mais segura de instalar o Falco é diretamente no nodes do cluster, pois dessa maneira, garante-se o isolamento da ferramenta de qualquer comprometimento do ecossistema do Kubernetes.

Também existe a possiblidade de rodar o Falco como um DaemonSet por meio do deploy via helm chart (que será o modelo de deploy que adotaremos aqui no post).

Maõs à obra!

Antes de inciarmos, uma breve descrição do cenário de implementação:

O deploy do Falco será feito via Helm Chart num ambiente Kubernetes gerenciado (EKS).

Além do deploy do Falco, iremos também fazer o deploy do falco-exporter, um daemonset para que possamos expor as metricas do Falco para o Prometheus.

Por fim, faremos o deploy do falcosidekick, que é uma extensão do Falco que permite a visualização dos alertas através de um dashboard customizado pela comunidade!

Diagrama do cenário desse post

Pré-requisitos:

Caso você queira reproduzir o cenário acima será necessário:

  • Um cluster EKS em execução
  • Helm 3.0 instalado e configurado
  • Uma instância Prometheus em execução no ambiente (caso queira expor as métricas).
  • Conhecer minimamente o ecossistema do Kubernetes

Deploy do Falco

Vamos criar o namespace para o Falco e para a uma aplicação de teste:

$ kubectl create ns falco && kubectl create ns teste-nginx

Aplicação de teste para o Falco analisar o comportamento:

$ kubectl create deploy \
go-app-test --image=docker.io/igoritosousa22/app-test:2.0.0 \
namespace=go-app-test \
--replicas=2

Você pode encontrar a aplicação de teste neste repositório.

Deloy do chart do Falco:

$ helm repo add falcosecurity https://falcosecurity.github.io/charts && helm repo update && \
helm install falco falcosecurity/falco -n falco \
--set falcosidekick.enabled=true \
--set falcosidekick.webui.enabled=true \
--set auditLog.enabled=true \
--set falco.grpc.enabled=true \
--set falco.grpcOutput.enabled=true

As opções que foram habilitadas referem-se a extensão falcosidekick para que possamos visualizar os dashboardos de detecção do Falco e também habilitamos o servidor gRPC para que ele exponha os eventos via gRPC socket para o falco-exporter.

Se o deploy foi bem sucedido, ao executar o comando abaixo, você deverá visualizar os logs do damonset do Falco indicando que ele esta ativo no cluster:

$ kubectl logs <falco-pod> -n falco

output:

logs de confirmação de funcionament do Falco

A partir desse momento o Falco já está monitorando as chamadas de sistemas dos nodes do Cluster EKS, no entanto ainda não temos a visibilidade dos eventos do EKS.

Para que possamos coletar os eventos de auditoria do kubernetes é ncessário alterar os arquivos de logs localizados no controlplane, ou seja no nó master do Kubernetes. Como o EKS trata-se de um serviço kubernetes gerenciado pela AWS, nós não temos acesso aos nodes masters.

O EKS envia os eventos de audit do kubernetes para um grupo de logs no Cloudwatch, dessa forma podemos fazer um “workaround” para resolver essa questão.

Analisando audit logs do EKS com o Falco

Para que consigamos enviar os logs do EKS para o Falco, precisaremos de uma aplicação que fará essa ponte: EKS audit integration.

Essa aplicação foi desenvolvida também pelo time da Sysdig, Inc e tem como função ler os logs do grupo de logs do EKS no Cloudwatch e enviar para o servidor do Falco que está em execução no cluster.

Para essa aplicação funcionar corretamente você precisara habilitar IAM Roles for Service Accounts (IRSA) no cluster EKS.

Na sequencia, vamos criar uma policy chamada ekscloudwatch-eks-cw-policy e em seguida criaremos uma role atachada à política anterior, chamada ekscloudwatch-eks-cw-role.

ekscloudwatch-eks-cw-policy.json:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Action": [
"logs:List*",
"logs:Get*",
"logs:FilterLogEvents",
"logs:Describe*",
"cloudwatch:List*",
"cloudwatch:Get*",
"cloudwatch:Describe*"
],
"Resource": "arn:aws:logs:*:*:log-group:/aws/eks/*"
}
]
}

ekscloudwatch-eks-cw-role.json:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::12345678911:oidc-provider/oidc.eks.us-east-2.amazonaws.com/id/10AAAASSSDDD22222222111"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.us-east-2.amazonaws.com/id/10AAAASSSDDD22222222111:sub": "system:serviceaccount:default:ekscloudwatch"
}
}
}
]
}

Repare que na role é especificada a serviceaccount do kubernetes que terá permissão para ler os logs do EKS:

"system:serviceaccount:default:ekscloudwatch"

Altere essa informação conforme a necessidade.

Certo, uma vez que as configurações no ambiente da AWS estejam finalizadas, precisamos agora aplicar o deployment da aplicação que irá ler os logs do EKS.

Fique atento para as seguintes informações:

  • eks.amazonaws.com/role-arn: ARN da role do seu ambiente criada acima.
  • endpoint: trata-se do hostname do service do falco, no nosso cenário esse endpoit é: http://falco.falco:8765/k8s-audit, mas isso pode variar de acordo com o namespace onde foi realizado o deploy do Falco.
  • cluster_name: Nome do seu cluster EKS

deployment-ekscloudwatch.yaml:

apiVersion: v1
kind: ServiceAccount
metadata:
name: ekscloudwatch
namespace: default
annotations:
eks.amazonaws.com/role-arn:"arn:aws:iam::12345667891011:role/ekscloudwatch-eks-cw-role"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ekscloudwatch-config
namespace: default
data:
# Required: Endpoint to forward audit events to, such as Sysdig Secure agent
# The agent must expose a k8s audit server (k8s_audit_server_port must be configured in the agent as well)
endpoint: "http://falco.falco:8765/k8s-audit"

# Required: Cloudwatch polling interval
cw_polling: "5m"
# Required: CloudWatch query filter
cw_filter: '{ $.sourceIPs[0] != "::1" && $.sourceIPs[0] != "127.0.0.1" }'
# Optional: the EKS cluster name
# This can be omitted if the EC2 instance can perform the ec2:DescribeInstances action
cluster_name: "nome-do-seu-cluster-eks"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: eks-cloudwatch
namespace: default
spec:
minReadySeconds: 5
replicas: 1
selector:
matchLabels:
app: eks-cloudwatch
template:
metadata:
labels:
app: eks-cloudwatch
spec:
serviceAccountName: ekscloudwatch
securityContext:
fsGroup: 65534 # to be able to read Kubernetes and AWS token files
containers:
- image: sysdiglabs/k8sauditlogforwarder:ekscloudwatch-0.2
imagePullPolicy: Always
name: eks-cloudwatch-container
env:
- name: ENDPOINT
valueFrom:
configMapKeyRef:
name: ekscloudwatch-config
key: endpoint

- name: CLUSTER_NAME
valueFrom:
configMapKeyRef:
name: ekscloudwatch-config
key: cluster_name
- name: CW_POLLING
valueFrom:
configMapKeyRef:
name: ekscloudwatch-config
key: cw_polling
- name: CW_FILTER
valueFrom:
configMapKeyRef:
name: ekscloudwatch-config
key: cw_filter

Aplique o manifesto:

$ kubectl apply -f deployment-ekscloudwatch.yaml

Se tudo ocorrer bem no deployment, você deverá ver a seguinte saída quando inspecionar os logs do pod:

$ kubectl logs eks-cloudwatch-pod

output:

logs ekscloudwatch pod

A partir desse momento também temos os eventos do EKS sendo monitorados pela aplicação do Falco!

Falcosidekick-ui

Durante o deploy do helm chart do Falco, foi habilitado também a interface gráfica do falcosidekick.

Essa interface nos permite analisar de uma maneira mais agradável os eventos que as regras do Falco já detectaram.

Para acessar a interface gráfica do Falco faremos o seguinte:

$ k port-forward svc/falco-falcosidekick-ui -n falco 2802:2802

Em seguida, no seu navegador digite http://localhost:2802.

Você deverá ver uma imagem semelhante abaixo:

dahsboard Falco

Detalhamento do evento do Falco:

detalhe de umevento do Falco

Observação: Esse dashboard do Falco não requer autenticação, sendo assim pense bem antes de expor esse serviço para um acesso externo.

Deploy do falco-exporter

Falco-exporter é um exportador de métricas do Falco para o Prometheus.

Antes de usar esse chart, lembre-se que precisamos do Falco em execução e com a opção de gRPC Output habilitada.

Deploy do chart do Falco-Expoter:

$ helm repo add falcosecurity https://falcosecurity.github.io/charts && helm repo update && \
helm install falco-exporter falcosecurity/falco-exporter -n falco \
--set serviceMonitor.enabled=true \
--set grafanaDashboard.enabled=true

Não há grandes configurações a serem realizadas para o exporter do Falco, uma vez que ele esteja em execução no seu cluster, basta conferir se as métricas já encontram-se no ambiente do prometheus.

Grafana dashboard para o Falco

Uma vez que as métricas do Falco já estejam sendo coletadas pelo ambiente do prometheus, caso você utilize o Grafana como a sua plataforma para configurar e visualizar dashboards, existe um dashboard exclusivo do Falco que você pode importar para o seu ambiente.

Na área de Dashboards do Grafana importe a seguinte URL:

https://grafana.com/grafana/dashboards/11914

Aponte o endpoint do seu prometheus como o datasource das mé tricas e pronto!

grafana dashboard para o Falco

Configurando uma regra customizada:

Como dito anteriormente o Falco possui mais de 60 regras ativas em sua instalação default, mas isso não é uma limitação, muito pelo contrário, existem situações onde é ncessário personalizar uma regra existente ou até mesmo criar uma nova regra.

Pra isso você precisa criar um arquivo yaml para passar essas regras.

Vamos chamá-lo de custom-rules.yaml.

custom-rules.yaml:

customRules:                               
rules-networking.yaml: |-
- rule: Launch Sensitive Mount Container
condition: >
and not allow_repositorys
append: true
- macro: allow_repositorys
condition: >
container.image.repository in (allow_images)
- list: allow_images
items: [
"quay.io/prometheus-operator/prometheus-operator"]
- rule: Read secrets in /var/run/secrets
desc: Detected reads in sensitive directory /var/run /secrets.This directory typicaly stores serviceaccount tokens and other sensitive values.
condition: go_app_test and spawned_process and container.id != host and proc.cmdline contains /var/run/secrets/ output: Possible extrafiltration ran inside a container (command=%proc.cmdline %container.info)
priority: EMERGENCY
- macro: go_app_test
condition: k8s.ns.name = teste-nginx and k8s.pod.label.app = goappteste

No arquivo acima, estamos realizando duas tarefas diferentes:

  • Alterando uma regra existente: Para isso precisamos passar a chave/valor “append:true”. Isso irá indicar para o arquivo de regras do Falco que uma nova entrada pra uma regra que já existe está sendo criada.
  • Criando uma nova regra: Nesse caso não precisamos da indicar a propriedade “append:true”, pois essa é uma regra que não existe no Falco, no momento que o deploy for atualizado a nova regra será incorporada.

Vamos olhar bem a condition da nossa regra:

k8s.ns.name = teste-nginx and k8s.pod.label.app = goappteste

Aqui nós especificamos bem o escopo ao qual a regra se restringe. Informando o namespace e label da aplicação ao qual essa regra se aplica.

Vamos atualizar o deploy do Falco:

helm upgrade falco falcosecurity/falco -n falco -f custom-rules.yaml

Por fim, vamos verificar se a regra está funcionando.

kubectl exec -it go-app-test-6b97b7dcc9-swh8k -n teste-nginx -- ls /var/run/secrets/kubernetes.io/serviceaccount

E o resultado é o seguinte:

regra customizada acionada

O diretório /var/run/secrets é montado por default quando um pod é inicializado. Esse diretório armazena as informações do token da serviceaccount.

Se essa serviceaccount possui muitos privilégios e por algum motivo é comprometida, um atacante (com acesso à um container comprometido, por exemplo) pode utilizar esse token para autenticar na API do Kubernetes:

# Point to the internal API server hostname
export APISERVER=https://kubernetes.default.svc
# Path to ServiceAccount token
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
# Read this Pod's namespace
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
# Read the ServiceAccount bearer token
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
# Reference the internal certificate authority (CA)
export CACERT=${SERVICEACCOUNT}/ca.crt
# Explore the API with TOKEN
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api

Conclusão

O post cobriu o deploy completo do Falco Security, uma solução para análise da segurança de containers em tempo de execução. Além do deploy da aplicação em sí foram aborados apsectos de integrações bem específicas com intuito de aproximar o cenário aprensentado a um cenário mais real do dia-a-dia. Vou deixar abaixo todas as referẽncias que utilizei elaborar esse cenário.

Se você reprduziu esse cenáraio no seu ambiente, não precisa necessáriamente ser igual, deixa aí no comentário, vamos trocar ideia e aprender juntos! E não se esqueça de destruir o ambiente para salvar aquela grana!

Referências:

--

--

Iluiz Sousa

Tentando me tornar o que chamam de engenheiro DevOps.