From e064b58724e00b1a64be71f68f0214120bdd91c5 Mon Sep 17 00:00:00 2001 From: Robert Fairburn <8029478+rfairburn@users.noreply.github.com> Date: Wed, 13 Dec 2023 11:52:57 -0600 Subject: [PATCH] ECR Repo and images for osquery in dogfood-free (#15516) --- .../aws-tf-module/.terraform.lock.hcl | 22 ++ .../terraform/aws-tf-module/docker/main.tf | 41 ++++ .../files/free_detail_query_overrides.yaml | 6 + .../terraform/aws-tf-module/free-ecs-hosts.tf | 232 ++++++++++++++++++ .../dogfood/terraform/aws-tf-module/main.tf | 4 + 5 files changed, 305 insertions(+) create mode 100644 infrastructure/dogfood/terraform/aws-tf-module/docker/main.tf create mode 100644 infrastructure/dogfood/terraform/aws-tf-module/files/free_detail_query_overrides.yaml create mode 100644 infrastructure/dogfood/terraform/aws-tf-module/free-ecs-hosts.tf diff --git a/infrastructure/dogfood/terraform/aws-tf-module/.terraform.lock.hcl b/infrastructure/dogfood/terraform/aws-tf-module/.terraform.lock.hcl index ab6d0f5c9d..0729331472 100644 --- a/infrastructure/dogfood/terraform/aws-tf-module/.terraform.lock.hcl +++ b/infrastructure/dogfood/terraform/aws-tf-module/.terraform.lock.hcl @@ -148,3 +148,25 @@ provider "registry.terraform.io/hashicorp/tls" { "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", ] } + +provider "registry.terraform.io/kreuzwerker/docker" { + version = "3.0.2" + constraints = "3.0.2" + hashes = [ + "h1:XjdpVL61KtTsuPE8swok3GY8A+Bu3TZs8T2DOEpyiXo=", + "zh:15b0a2b2b563d8d40f62f83057d91acb02cd0096f207488d8b4298a59203d64f", + "zh:23d919de139f7cd5ebfd2ff1b94e6d9913f0977fcfc2ca02e1573be53e269f95", + "zh:38081b3fe317c7e9555b2aaad325ad3fa516a886d2dfa8605ae6a809c1072138", + "zh:4a9c5065b178082f79ad8160243369c185214d874ff5048556d48d3edd03c4da", + "zh:5438ef6afe057945f28bce43d76c4401254073de01a774760169ac1058830ac2", + "zh:60b7fadc287166e5c9873dfe53a7976d98244979e0ab66428ea0dea1ebf33e06", + "zh:61c5ec1cb94e4c4a4fb1e4a24576d5f39a955f09afb17dab982de62b70a9bdd1", + "zh:a38fe9016ace5f911ab00c88e64b156ebbbbfb72a51a44da3c13d442cd214710", + "zh:c2c4d2b1fd9ebb291c57f524b3bf9d0994ff3e815c0cd9c9bcb87166dc687005", + "zh:d567bb8ce483ab2cf0602e07eae57027a1a53994aba470fa76095912a505533d", + "zh:e83bf05ab6a19dd8c43547ce9a8a511f8c331a124d11ac64687c764ab9d5a792", + "zh:e90c934b5cd65516fbcc454c89a150bfa726e7cf1fe749790c7480bbeb19d387", + "zh:f05f167d2eaf913045d8e7b88c13757e3cf595dd5cd333057fdafc7c4b7fed62", + "zh:fcc9c1cea5ce85e8bcb593862e699a881bd36dffd29e2e367f82d15368659c3d", + ] +} diff --git a/infrastructure/dogfood/terraform/aws-tf-module/docker/main.tf b/infrastructure/dogfood/terraform/aws-tf-module/docker/main.tf new file mode 100644 index 0000000000..46e5038957 --- /dev/null +++ b/infrastructure/dogfood/terraform/aws-tf-module/docker/main.tf @@ -0,0 +1,41 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + docker = { + source = "kreuzwerker/docker" + version = "3.0.2" + } + } +} + +variable "osquery_tag" { + description = "The osquery tag to take from dockerhub to your ecr repo." + type = string +} + +variable "ecr_repo" { + description = "The ecr repo to push to" + type = string +} + +resource "docker_image" "dockerhub" { + name = "osquery/osquery:${var.osquery_tag}" +} + +resource "docker_tag" "osquery" { + source_image = docker_image.dockerhub.name + # We can't include the sha256 when pushing even if they match + target_image = "${var.ecr_repo}:${split("@sha256", var.osquery_tag)[0]}" +} + +resource "docker_registry_image" "osquery" { + name = docker_tag.osquery.target_image + keep_remotely = true +} + +output "ecr_image" { + value = docker_tag.osquery.target_image +} diff --git a/infrastructure/dogfood/terraform/aws-tf-module/files/free_detail_query_overrides.yaml b/infrastructure/dogfood/terraform/aws-tf-module/files/free_detail_query_overrides.yaml new file mode 100644 index 0000000000..d3c180ed64 --- /dev/null +++ b/infrastructure/dogfood/terraform/aws-tf-module/files/free_detail_query_overrides.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: config +spec: + features: + detail_query_overrides: + system_info: "select case when (select count(value) from process_envs where key = 'FAKE_HOSTNAME') > 0 then (select value from process_envs where key = 'FAKE_HOSTNAME' limit 1) else hostname end as hostname, uuid, cpu_type, cpu_subtype, cpu_brand, cpu_physical_cores, cpu_logical_cores, cpu_microcode, physical_memory, hardware_vendor, hardware_model, hardware_version, hardware_serial, board_vendor, board_model, board_version, board_serial, case when (select count(value) from process_envs where key = 'FAKE_HOSTNAME') > 0 then (select value from process_envs where key = 'FAKE_HOSTNAME' limit 1) else computer_name end as computer_name, case when (select count(value) from process_envs where key = 'FAKE_HOSTNAME') > 0 then (select value from process_envs where key = 'FAKE_HOSTNAME' limit 1) else local_hostname end as local_hostname from system_info;" diff --git a/infrastructure/dogfood/terraform/aws-tf-module/free-ecs-hosts.tf b/infrastructure/dogfood/terraform/aws-tf-module/free-ecs-hosts.tf new file mode 100644 index 0000000000..801dc9bee0 --- /dev/null +++ b/infrastructure/dogfood/terraform/aws-tf-module/free-ecs-hosts.tf @@ -0,0 +1,232 @@ +## Linux hosts in ECS + +locals { + osquery_hosts = { + "5.8.2-ubuntu22.04@sha256:b77c7b06c4d7f2a3c58cc3a34e51fffc480e97795fb3c75cb1dc1cf3709e3dc6" = "Skys-laptop" + "5.8.2-ubuntu20.04@sha256:3496ffd0ad570c88a9f405e6ef517079cfeed6ce405b9d22db4dc5ef6ed3faac" = "Cloud-City-server" + "5.8.2-ubuntu18.04@sha256:372575e876c218dde3c5c0e24fd240d193800fca9b314e94b4ad4e6e22006c9b" = "Mists-laptop" + "5.8.2-ubuntu16.04@sha256:112655c42951960d8858c116529fb4c64951e4cf2e34cb7c08cd599a009025bb" = "Ethers-laptop" + "5.8.2-debian10@sha256:de29337896aac89b2b03c7642805859d3fb6d52e5dc08230f987bbab4eeba9c5" = "Breezes-laptop" + "5.8.2-debian9@sha256:47e46c19cebdf0dc704dd0061328856bda7e1e86b8c0fefdd6f78bd092c6200e" = "Aero-server" + "5.8.2-centos8@sha256:88a8adde80bd3b1b257e098bc6e41b6afea840f60033653dcb9fe984f36b0f97" = "Stratuss-laptop" + "5.8.2-centos7@sha256:ff251de4935b80a91c5fc1ac352aebdab9a6bbbf5bda1aaada8e26d22b50202d" = "Zephyrs-Laptop" + "5.8.2-centos6@sha256:b56736be8436288d3fbd2549ec6165e0588cd7197e91600de4a2f00f1df28617" = "Halo-server" + } + +} + + +# ECR to store the images +resource "aws_iam_role" "osquery" { + name = "fleet-free-osquery-execution" + description = "IAM Execution role for osquery containers" + assume_role_policy = data.aws_iam_policy_document.osquery_assume_role.json +} + +data "aws_iam_policy_document" "osquery_assume_role" { + statement { + effect = "Allow" + actions = ["sts:AssumeRole"] + principals { + identifiers = ["ecs.amazonaws.com", "ecs-tasks.amazonaws.com"] + type = "Service" + } + } +} + +resource "aws_iam_role_policy_attachment" "osquery_execution_attachment" { + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" + role = aws_iam_role.osquery.name +} + +resource "aws_iam_role_policy_attachment" "osquery" { + policy_arn = aws_iam_policy.osquery.arn + role = aws_iam_role.osquery.name +} + +resource "aws_iam_policy" "osquery" { + name = "osquery-ecr-policy" + description = "IAM policy that Osquery containers use to define access to AWS resources" + policy = data.aws_iam_policy_document.osquery.json +} + +data "aws_iam_policy_document" "osquery" { + statement { + actions = [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:GetDownloadUrlForLayer", + "ecr:GetAuthorizationToken" + ] + resources = ["*"] + } + statement { + actions = [ #tfsec:ignore:aws-iam-no-policy-wildcards + "kms:Encrypt*", + "kms:Decrypt*", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:Describe*" + ] + resources = [aws_kms_key.osquery.arn] + } + statement { + actions = [ #tfsec:ignore:aws-iam-no-policy-wildcards + "secretsmanager:GetSecretValue" + ] + resources = [aws_secretsmanager_secret.osquery_enroll.arn] + + } +} + +resource "aws_ecr_repository" "osquery" { + name = "osquery" + image_tag_mutability = "IMMUTABLE" + + image_scanning_configuration { + scan_on_push = true + } + + encryption_configuration { + encryption_type = "KMS" + kms_key = aws_kms_key.osquery.arn + } +} + +resource "aws_kms_key" "osquery" { + deletion_window_in_days = 10 + enable_key_rotation = true +} + +resource "aws_secretsmanager_secret" "osquery_enroll" { + name = "osquery-enroll-secret" +} + +output "osquery_repo" { + value = aws_ecr_repository.osquery +} + +output "osquery_iam_policy" { + value = aws_iam_policy.osquery +} + +data "aws_region" "current" {} +data "aws_ecr_authorization_token" "token" {} + +provider "docker" { + # Configuration options + registry_auth { + address = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${data.aws_region.current.name}.amazonaws.com" + username = data.aws_ecr_authorization_token.token.user_name + password = data.aws_ecr_authorization_token.token.password + } +} + +module "osquery_docker" { + for_each = local.osquery_hosts + source = "./docker" + ecr_repo = aws_ecr_repository.osquery.repository_url + osquery_tag = each.key +} + +resource "random_uuid" "osquery" { + for_each = local.osquery_hosts +} + +resource "aws_ecs_task_definition" "osquery" { + for_each = local.osquery_hosts + // e.g. 5-8-2-ubuntu22-04 to match naming requirements + family = "osquery-${replace(split("@sha256", each.key)[0], ".", "-")}" + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + execution_role_arn = aws_iam_role.osquery.arn + cpu = 256 + memory = 512 + # Needed to run hostname command + container_definitions = jsonencode( + [ + { + name = "osquery" + image = module.osquery_docker[each.key].ecr_image + cpu = 256 + memory = 512 + mountPoints = [] + volumesFrom = [] + essential = true + ulimits = [ + { + softLimit = 999999, + hardLimit = 999999, + name = "nofile" + } + ] + networkMode = "awsvpc" + logConfiguration = { + logDriver = "awslogs" + options = module.free.byo-db.byo-ecs.logging_config + } + environment = [ + { + name = "FAKE_HOSTNAME" + value = each.value + } + ] + secrets = [ + { + name = "ENROLL_SECRET" + valueFrom = aws_secretsmanager_secret.osquery_enroll.arn + } + ] + workingDirectory = "/", + command = [ + "osqueryd", + "--tls_hostname=free.fleetdm.com", + "--force=true", + # Ensure that the host identifier remains the same between invocations + "--host_identifier=specified", + "--specified_identifier=${random_uuid.osquery[each.key].result}", + "--verbose=true", + "--tls_dump=true", + "--enroll_secret_env=ENROLL_SECRET", + "--enroll_tls_endpoint=/api/osquery/enroll", + "--config_plugin=tls", + "--config_tls_endpoint=/api/osquery/config", + "--config_refresh=10", + "--disable_distributed=false", + "--distributed_plugin=tls", + "--distributed_interval=10", + "--distributed_tls_max_attempts=3", + "--distributed_tls_read_endpoint=/api/osquery/distributed/read", + "--distributed_tls_write_endpoint=/api/osquery/distributed/write", + "--logger_plugin=tls", + "--logger_tls_endpoint=/api/osquery/log", + "--logger_tls_period=10", + "--disable_carver=false", + "--carver_start_endpoint=/api/osquery/carve/begin", + "--carver_continue_endpoint=/api/osquery/carve/block", + "--carver_block_size=8000000", + ] + } + ]) + lifecycle { + create_before_destroy = true + } +} + +resource "aws_ecs_service" "osquery" { + for_each = local.osquery_hosts + # Name must match ^[A-Za-z-_]+$ e.g. 5-8-2-ubuntu22-04 + name = "osquery_${replace(split("@sha256", each.key)[0], ".", "-")}" + launch_type = "FARGATE" + cluster = module.free.byo-db.byo-ecs.service.cluster + task_definition = aws_ecs_task_definition.osquery[each.key].arn + desired_count = 1 + # Spin down before spin up since we are specifying the host identifier manually + deployment_minimum_healthy_percent = 0 + deployment_maximum_percent = 100 + + network_configuration { + subnets = module.free.byo-db.byo-ecs.service.network_configuration[0].subnets + security_groups = module.free.byo-db.byo-ecs.service.network_configuration[0].security_groups + } +} diff --git a/infrastructure/dogfood/terraform/aws-tf-module/main.tf b/infrastructure/dogfood/terraform/aws-tf-module/main.tf index c7e1959688..353cff63e0 100644 --- a/infrastructure/dogfood/terraform/aws-tf-module/main.tf +++ b/infrastructure/dogfood/terraform/aws-tf-module/main.tf @@ -21,6 +21,10 @@ terraform { source = "hashicorp/aws" version = "~> 5.0" } + docker = { + source = "kreuzwerker/docker" + version = "3.0.2" + } } }