I searched and searched for a way to be able to bootstrap Kafka clients using vanity DNS names instead of the AWS-generated DNS names for the MSK brokers.
My team and I finally figured out a solution after piecing together information from different sources. Here is our solution.
For each MSK broker:
- Create a network load balancer (NLB) in the same VPC (for us, this was in the public subnet)
- Create a TLS-type load balancer (LB) listener and attach a TLS certificate with the custom vanity domain name in the Subject Alternative Names list.
- Create a TLS certificate for the vanity domain
- Create a TLS-type load balancer (LB) target group (TG) that targets the MSK broker by IP address
The MSK broker IP addresses were looked up by their DNS A records
Here is the terraform configuration. I hope it is helpful:
locals {
msk_scram_addrs = split(",", aws_msk_cluster.myclust.bootstrap_brokers_sasl_scram)
msk_scram_hosts = toset([for x in local.msk_scram_addrs : split(":", x)[0]])
# b-1, b-2, b-3, ...
broker_short_names = toset([
for a in data.dns_a_record_set.myclust_scram_brokers :
split(".", a.host)[0]
])
}
data "dns_a_record_set" "myclust_scram_brokers" {
for_each = local.msk_scram_hosts
host = each.value
depends_on = [
aws_msk_cluster.myclust
]
}
resource "aws_lb" "msk_broker" {
for_each = local.broker_short_names
name = each.value
tags = [...]
internal = false
subnets = local.aws_vpc_public_subnets
load_balancer_type = "network"
idle_timeout = 3600
timeouts {
create = "20m"
}
}
resource "aws_lb_target_group" "msk_broker_scram_public" {
for_each = local.broker_short_names
name = each.value
tags = [...]
# https://aws.amazon.com/blogs/aws/new-tls-termination-for-network-load-balancers/
protocol = "TLS"
vpc_id = local.aws_vpc_id
target_type = "ip" // Targets Kafka broker by IP address.
port = 9196
lifecycle {
create_before_destroy = true
}
depends_on = [
aws_lb.msk_broker
]
}
resource "aws_lb_target_group_attachment" "msk_broker_scram_public" {
for_each = {
for a in data.dns_a_record_set.myclust_scram_brokers : split(".", a.host)[0] => a.addrs[0]
}
target_group_arn = aws_lb_target_group.msk_broker_scram_public[each.key].arn
target_id = each.value // This is the Kafka broker IP address.
}
resource "aws_lb_listener" "msk_bootstrap_public" {
for_each = local.broker_short_names
# https://aws.amazon.com/blogs/aws/new-tls-termination-for-network-load-balancers/
protocol = "TLS"
port = 9196
certificate_arn = module.msk_lb_cert[each.value].acm_cert.arn
load_balancer_arn = aws_lb.msk_broker[each.value].arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.msk_broker_scram_public[each.value].arn
}
lifecycle {
create_before_destroy = true
}
}
module "msk_lb_cert" {
source = "../certificates"
for_each = local.broker_short_names
org_public_zone_id = local.org_zone_id
io_public_zone_id = data.aws_route53_zone.io_public.zone_id
org_domain_name = "${each.value}.${local.params.msk_org_domain}"
vanity_domain_names = [
"${each.value}.${local.params.msk_vanity_domain}"
]
resource_health_check = false
resource_domain_name = aws_lb.msk_broker[each.value].dns_name
resource_zone_id = aws_lb.msk_broker[each.value].zone_id
providers = {
aws.org_zone = aws.org
aws.io_zone = aws.io
}
}