Falco is a cloud-native security solution striving to become the premier threat detection engine for Kubernetes. It identifies potential security risks in real time by closely observing and tracking the actions of various elements within your Kubernetes environment, including Nodes, Pods, Applications, and the Kubernetes API. In achieving this, Falco uses data from Linux system calls and Kubernetes Audit Logs.
If you're more interested in how Falco works, we delved into its workings in another article.
Falcosidekick is an open-source tool that retrieves all Falco events and forwards them to various possible outputs. For example, you can use Falcosidekick to receive alerts triggered by Falco on Slack or Opsgenie.
Deploying Falco with Falcosidekick on a Kubernetes cluster is very straightforward, thanks to the Helm chart "falcosecurity/falco." This Helm chart enables the deployment of a Falco instance on each node of the cluster with only a single instance of Falcosidekick that receives all the events. This schema explains the architecture on a Kubernetes cluster.
In our case, we implemented Falco on an ECS cluster with EC2 launch type. In the EC2 launch type, the ECS cluster has a fleet of EC2 instances. Each EC2 instance can be likened to a node in Kubernetes on which tasks can be launched.
We've opted for a different strategy than the one explained for a Kubernetes cluster. We deploy one instance of Falco and one instance of Falcosidekick per EC2 instance.
We chose this approach because to have a single instance of Falcosidekick, we would need to establish inter-container communication across different EC2 instances. Unlike Kubernetes, there's no built-in internal DNS resolution to facilitate this communication in ECS. It is possible to implement, but it's technically more complex than the solution we've chosen. Furthermore, Falcosidekick has very low consumption, so it doesn't have much of a FinOps impact.
If you're interested in delving into inter-service communication on AWS ECS, you can refer to AWS documentation.
We used Terraform to deploy Falco and Falcosidekick.
[
{
"name": "falco",
"image": "falcosecurity/falco:0.35.1",
"essential": true,
"cpu": 10,
"memory": 512,
"privileged": true,
"mountPoints": [
{
"containerPath": "/host/var/run/docker.sock",
"sourceVolume": "docker-socket"
},
{
"containerPath": "/host/dev",
"sourceVolume": "dev-fs"
},
{
"containerPath": "/host/proc",
"sourceVolume": "proc-fs",
"readOnly": true
},
{
"containerPath": "/host/boot",
"sourceVolume": "boot-fs",
"readOnly": true
},
{
"containerPath": "/host/lib/modules",
"sourceVolume": "lib-modules",
"readOnly": true
},
{
"containerPath": "/host/usr",
"sourceVolume": "usr-fs",
"readOnly": true
},
{
"containerPath": "/host/etc",
"sourceVolume": "etc-fs",
"readOnly": true
}
]
},
{
"name": "falcosidekick",
"image": "falcosecurity/falcosidekick:2.28.0",
"essential": true,
"cpu": 2,
"memory": 126,
"portMappings": [
{
"containerPort": 2801,
"hostPort": 2801,
"protocol": "tcp"
}
],
"environment": [
{
"name": "SLACK_WEBHOOKURL",
"value": "https://hooks.slack.com/services/XXXX/YYYY/ZZZZ"
}
]
}
]
In the Falcosidekick definition, we've mapped port 2801, which needs to be accessible for Falco to send its events. We've also defined an environment variable SLACK_WEBHOOKURL
to send logs to a Slack channel. You can refer to the Falcosidekick documentation for instructions on configuring other types of channel output using environment variables. You can also edit Falco configuration through environment variables in the task definition of Falco container.
aws_ecs_cluster
data to access ECS cluster from Terraform.data "aws_ecs_cluster" "your-ecs-cluster" {
cluster_name = "your-ecs-cluster-name"
}
aws_ecs_task_definition
resource, pointing to your task definition JSON file.resource "aws_ecs_task_definition" "falco" {
family = "falco"
container_definitions = file("path/to/your/task/definition/file.json")
network_mode = "host"
volume {
name = "docker-socket"
host_path = "/var/run/docker.sock"
}
volume {
name = "dev-fs"
host_path = "/dev"
}
volume {
name = "proc-fs"
host_path = "/proc"
}
volume {
name = "boot-fs"
host_path = "/root"
}
volume {
name = "lib-modules"
host_path = "/lib/modules"
}
volume {
name = "usr-fs"
host_path = "/usr"
}
volume {
name = "etc-fs"
host_path = "/etc"
}
}
The "host" network mode is used to allow the containers within a task to share the network of the underlying EC2 host instance they run on. This is useful to facilitate easy communication between the Falco and Falcosidekick containers.
aws_ecs_service
resource.resource "aws_ecs_service" "falco" {
name = "falco"
cluster = aws_ecs_cluster.your-ecs-cluster.id
task_definition = aws_ecs_task_definition.falco.arn
scheduling_strategy = "DAEMON"
}
The DAEMON
scheduling strategy is used to ensure that precisely one task is launched on each EC2 instance.
Apply your terraform code and voilà! Falco should run on each of the EC2 instances within the cluster, and you should receive alerts on the configured output channel.
If you want to add custom rules to Falco, you can create a file (falco_rules.local.yaml
) and copy it into the Falco Docker image. Here is an example of Dockerfile :
FROM falcosecurity/falco:0.35.1
COPY falco_rules.local.yaml /etc/falco/falco_rules.local.yaml
CMD ["/usr/bin/falco", "-pc"]
Then, push the Docker image in an ECR. Update the image in the task definition of the Falco container to your custom image.
Installing an intrusion detection system on your clusters is an excellent way to enhance the security of your infrastructure. We hope this tutorial has helped you in implementing Falco on ECS!