At Altenar, we often use RabbitMQ as an entry point for our products. In this specific case, we will discuss a system that calculates sports data and provides it to other systems within the organization as well as outside the perimeter. When designing the system, the RabbitMQ component had the following requirements:
The endpoint is secured with an SSL certificate
RabbitMQ is hosted in Kubernetes. We use the RabbitMQ cluster operator and RabbitMQ messaging topology operator
For this project, we are using Google Cloud. Like Let’s Encrypt, you can use a Google Managed SSL certificate for any public HTTP load balancer in Google Cloud. However, it has several limitations, with the key ones being:
It only works for HTTP Load balancers.
It only works for public Load balancers.
According to RabbitMQ documentation, there are two common approaches for client connections:
Configure RabbitMQ to handle TLS connections
Use a proxy or load balancer (such as HAproxy) to perform TLS termination of client connections and use plain TCP connections to RabbitMQ nodes.
Our idea was to use a Google Managed certificate for TCP/SSL proxy load balancer in Google Cloud. However, due to the aforementioned limitations, a managed certificate is not supported for TCP load balancers. On the other hand, Google allows you to use an SSL certificate for several load balancers, which we decided to explore. Overall the blueprint would look like this:

We wanted to have a default dummy HTTP service that we would expose over port 443 and use for the sake of managing the SSL certificate and automatic renewals.
We would have a separate endpoint reusing the same IP address and SSL certificate.
Let’s examine each part of the solution in detail.
GKE Cluster
As the main hosting platform, we use GKE. We use the Google Terraform module for the private GKE cluster, configure Workload Identity and install an AutoNEG controller to the cluster.
In this article, we won’t delve into the specifics of how Workload Identity works with AutoNEG, as they are used as described in the official documentation.
With all preparations in place, we deploy a dummy “Hello World” service to the cluster and attach it to the Google Load balancer via AutoNEG. Below is the YAML configuration for that:
apiVersion: apps/v1
kind: Deployment
metadata:
name: gcp-tls-certificate-issuer
labels:
app: gcp-tls-certificate-issuer
annotations:
deployment.kubernetes.io/revision: '1'
spec:
replicas: 2
selector:
matchLabels:
app: gcp-tls-certificate-issuer
template:
metadata:
labels:
app: gcp-tls-certificate-issuer
spec:
containers:
- name: ok
image: assemblyline/ok:latest
ports:
- containerPort: 8888
protocol: TCP
imagePullPolicy: Always
securityContext:
capabilities:
drop:
- ALL
runAsUser: 1000
runAsGroup: 3000
runAsNonRoot: true
readOnlyRootFilesystem: true
restartPolicy: Always
terminationGracePeriodSeconds: 30
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
---
apiVersion: v1
kind: Service
metadata:
name: gcp-tls-certificate-issuer
labels:
app: gcp-tls-certificate-issuer
annotations:
cloud.google.com/neg: '{"exposed_ports": {"8888":{"name": "gcp-tls-certificate-issuer"}}}'
controller.autoneg.dev/neg: '{"backend_services":{"8888":[{"name":"envcode-rabbit-https-backend-service","max_connections_per_endpoint":10000}]}}'
spec:
ports:
- name: http
protocol: TCP
port: 8888
targetPort: 8888
selector:
app: gcp-tls-certificate-issuer
clusterIP: 10.10.12.130
clusterIPs:
- 10.10.12.130
type: ClusterIP
Notice the service's annotation. This is how AutoNEG adds it to the Load balancer as a backend.
Google load balancer
The next part is managed outside GKE and created separately. Google Load balancer is not a single object but rather a set of different objects combined. Below is the Terraform code with comments:
resource "google_compute_managed_ssl_certificate" "rabbitmq" {
project = var.project
name = "${var.environment_name}-google-managed-certificate-rabbitmq"
managed {
domains = ["rabitmq.example.com."] # Replace with your domain
}
}
# reserved IP address
resource "google_compute_global_address" "default" {
project = var.project
name = "tcp-proxy-xlb-ip"
}
output "rabbitmq-ip" {
value = google_compute_global_address.default.address
}
# forwarding rule for TCP Loadbalanser
resource "google_compute_global_forwarding_rule" "default" {
project = var.project
name = "${var.environment_name}-tcp-global-loadbalancer"
provider = google
ip_protocol = "TCP"
load_balancing_scheme = "EXTERNAL"
port_range = "5671"
target = google_compute_target_ssl_proxy.default.id
ip_address = google_compute_global_address.default.id
}
# https://cloud.google.com/load-balancing/docs/ssl
# When you use Google-managed SSL certificates with SSL Proxy Load Balancing, the frontend port for traffic must be 443 to enable the Google-managed SSL certificates to be provisioned and renewed.
# forwarding rule for HTTPS Loadbalanser
resource "google_compute_global_forwarding_rule" "https" {
project = var.project
name = "${var.environment_name}-https-global-loadbalancer"
provider = google
ip_protocol = "TCP"
load_balancing_scheme = "EXTERNAL"
port_range = "443"
target = google_compute_target_ssl_proxy.https.id
ip_address = google_compute_global_address.default.id
}
resource "google_compute_target_ssl_proxy" "default" {
project = var.project
name = "${var.environment_name}-global-loadbalancer-tcp-proxy"
backend_service = google_compute_backend_service.default.id
ssl_certificates = [google_compute_managed_ssl_certificate.rabbitmq.id]
}
resource "google_compute_target_ssl_proxy" "https" {
project = var.project
name = "${var.environment_name}-global-loadbalancer-https-proxy"
backend_service = google_compute_backend_service.https.id
ssl_certificates = [google_compute_managed_ssl_certificate.rabbitmq.id]
}
# backend service For RabbitMQ Autoneg
resource "google_compute_backend_service" "default" {
project = var.project
name = "${var.environment_name}-tcp-backend-service"
protocol = "TCP"
port_name = "tcp"
load_balancing_scheme = "EXTERNAL"
timeout_sec = 10
health_checks = [google_compute_health_check.default.id]
session_affinity = "CLIENT_IP"
# We don't want TF to remove whatever was configured by AutoNEG
lifecycle {
ignore_changes = [backend]
}
}
# backend service For HTTPS Autoneg
resource "google_compute_backend_service" "https" {
project = var.project
#that's what you use in the service annotations
name = "${var.environment_name}-https-backend-service"
protocol = "TCP"
port_name = "tcp"
load_balancing_scheme = "EXTERNAL"
timeout_sec = 10
health_checks = [google_compute_health_check.https.id]
# We don't want TF to remove whatever was configured by AutoNEG
lifecycle {
ignore_changes = [backend]
}
}
resource "google_compute_health_check" "default" {
project = var.project
name = "tcp-proxy-health-check"
description = "Backend service for AutoNEG"
timeout_sec = 1
check_interval_sec = 1
tcp_health_check {
port = "5672" #use container port
}
}
resource "google_compute_health_check" "https" {
project = var.project
name = "https-proxy-health-check"
description = "Backend service for AutoNEG"
timeout_sec = 1
check_interval_sec = 1
tcp_health_check {
port = "8888" #use container port
}
}
]
As you can see, we created one IP address and one SSL certificate and then used them in two forwarding rules. This allowed us to have a managed SSL certificate used for the TCP Load Balancer.
Don’t forget to configure DNS and point the IP to the right hostname for the whole thing to work.
Tip: GKE has thel7-default-backend
deployment. Perhaps it would’ve been enough just creating a service with AutoNEG annotations and pointing it to that deployment’s pods. Try that and let me know in the comments if it works.