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_infois omitted from Terraform, the cluster defaults to private. The problem is drift: someone addspublic_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. Settype = "DISABLED"explicitly to lock in the desired state.MSK Serverless clusters use a different resource
aws_msk_serverless_clusterdoesn't support public access (it's always VPC-connected). This control applies only to provisionedaws_msk_clusterresources. 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.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
msk_cluster_not_publicly_accessibleAWS Config Managed Rule:
MSK_CLUSTER_PUBLIC_ACCESS_DISABLEDCheckov Check:
CKV_AWS_291Powerpipe Control:
aws_compliance.control.msk_cluster_not_publicly_accessibleProwler Check:
kafka_cluster_is_publicAWS Security Hub Control:
MSK.4KICS Query:
54378d69-dd7c-4b08-a43e-80d563396857
Last reviewed: 2026-03-09