Skip to content

MSK clusters should have public access disabled

MSK clusters handle streaming data that often includes sensitive application events, user activity, and internal system telemetry. Exposing broker endpoints to the public internet lets any actor with valid credentials (or exploiting an authentication bypass) connect directly to your Kafka topics. Private connectivity through VPC peering, PrivateLink, or transit gateway keeps this traffic off the internet entirely.

Public brokers expand your attack surface for port scanning and brute-force attempts against SASL or TLS authentication. Keeping access private reduces the blast radius of credential leaks and simplifies network-level access control.

Retrofit consideration

Switching a live MSK cluster from public to private access requires a configuration update that triggers a rolling restart of broker nodes. Any clients connecting over public endpoints will lose connectivity and must be migrated to private networking first.

Implementation

Choose the approach that matches how you manage Terraform.

If you use terraform-aws-modules/msk-kafka-cluster/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "msk_kafka_cluster" {
  source  = "terraform-aws-modules/msk-kafka-cluster/aws"
  version = ">=3.0.0"

  broker_node_client_subnets  = ["subnet-12345678", "subnet-12345678", "subnet-12345678"]
  broker_node_instance_type   = "kafka.t3.small"
  broker_node_security_groups = ["sg-12345678"]
  client_authentication = {
    sasl = {
      iam = true
    }
  }
  kafka_version          = "3.6.0"
  name                   = "abc123"
  number_of_broker_nodes = 3

  broker_node_connectivity_info = {
    public_access = {
      type = "DISABLED"
    }
  }
}

Use AWS provider resources directly. See docs for the resources involved: aws_msk_cluster.

resource "aws_msk_cluster" "this" {
  client_authentication {
    sasl {
      iam = true
    }
  }
  cluster_name           = "pofix-abc123"
  kafka_version          = "3.5.1"
  number_of_broker_nodes = 2

  broker_node_group_info {
    client_subnets = [element(["subnet-abc123", "subnet-def456"], 0), element(["subnet-abc123", "subnet-def456"], 1)]

    connectivity_info {
      public_access {
        type = "DISABLED"
      }
    }

    instance_type   = "kafka.t3.small"
    security_groups = ["sg-abc12345"]
  }
}

What this control checks

The policy evaluates the aws_msk_cluster resource's public access configuration. Within the broker_node_group_info block, the connectivity_info block contains a public_access block with a type argument. To pass, type must be set to "DISABLED" or omitted entirely ("DISABLED" is the default). It fails when type is "SERVICE_PROVIDED_EIPS", which assigns Elastic IPs to each broker and makes them reachable from the internet. No other values are accepted for this argument. If the connectivity_info or public_access blocks are absent, the cluster defaults to private access and passes.

Common pitfalls

  • Public access requires specific prerequisites that mask the real risk

    MSK only allows enabling public access when unauthenticated access is disabled and ACLs or IAM auth are configured. Teams sometimes assume that means it's safe. The control still fails because network exposure is the risk, not the authentication layer.

  • Connectivity info block defaults can mislead

    If connectivity_info is omitted from Terraform, the cluster defaults to private. The problem is drift: someone adds public_access { type = "SERVICE_PROVIDED_EIPS" } through the console or CLI, and Terraform won't enforce your intended setting unless the block is explicitly declared in config. Set type = "DISABLED" explicitly to lock in the desired state.

  • MSK Serverless clusters use a different resource

    aws_msk_serverless_cluster doesn't support public access (it's always VPC-connected). This control applies only to provisioned aws_msk_cluster resources. Don't conflate the two when auditing.

Audit evidence

AWS Config rule evaluations showing all MSK clusters as COMPLIANT are the primary evidence. The MSK cluster detail page in the console, under Networking, shows public access status per cluster. aws kafka describe-cluster (or describe-cluster-v2) output should show ConnectivityInfo.PublicAccess.Type as "DISABLED" for every cluster across all active regions.

CloudTrail logs for UpdateConnectivity API calls show when any public access configuration was changed and confirm the timestamp of remediation.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: msk_cluster_not_publicly_accessible

  • AWS Config Managed Rule: MSK_CLUSTER_PUBLIC_ACCESS_DISABLED

  • Checkov Check: CKV_AWS_291

  • Powerpipe Control: aws_compliance.control.msk_cluster_not_publicly_accessible

  • Prowler Check: kafka_cluster_is_public

  • AWS Security Hub Control: MSK.4

  • KICS Query: 54378d69-dd7c-4b08-a43e-80d563396857

Last reviewed: 2026-03-09