Encrypt Traffic To HTTPS Load Balancers With TLS
More Info:
Terminate load balancer traffic over HTTPS using TLS certificates so data in transit to and from the cluster is encrypted.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS AKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Inventory all AKS-facing load balancers and ingresses
- On any machine with Azure CLI access:
For each cluster:az aks list -o tableIf using an ingress controller, review its Service and Ingress definitions via IaC or repo to identify all external entrypoints.# Get resource group and node RGaz aks show -n <AKS_CLUSTER_NAME> -g <AKS_RG> \--query "{nodeResourceGroup:nodeResourceGroup}" -o tsv# List public IPs and load balancers in node RGaz network public-ip list -g <NODE_RESOURCE_GROUP> -o tableaz network lb list -g <NODE_RESOURCE_GROUP> -o table
- On any machine with Azure CLI access:
-
Confirm only HTTPS (TCP/443) is exposed externally
- For each public IP used by a load balancer/ingress:
az network lb show -g <NODE_RESOURCE_GROUP> -n <LB_NAME> \--query "frontendIpConfigurations[].inboundNatRules"az network lb rule list -g <NODE_RESOURCE_GROUP> --lb-name <LB_NAME> -o table
- Verify that:
- Frontend rules expose port 443 (or custom TLS ports) to clients.
- Port 80 (HTTP) is either disabled externally or only used for internal HTTP→HTTPS redirects at the application/ingress level.
- For each public IP used by a load balancer/ingress:
-
Verify TLS termination and certificate configuration
- If using an Azure Application Gateway / AGIC:
Ensure HTTPS listeners are configured and mapped to the relevant frontend IPs, and HTTP listeners are either disabled or only used for redirects.az network application-gateway list -o tableaz network application-gateway show -g <APPGW_RG> -n <APPGW_NAME> \--query "{frontendPorts:frontendPorts, httpsListeners:sslCertificates}" -o json
- If using a cloud load balancer with TLS passthrough to ingress: check the ingress controller configuration (Helm values, Terraform, etc.) to confirm TLS termination is enabled and bound to the HTTPS listener.
- If using an Azure Application Gateway / AGIC:
-
Review TLS certificate sources, validity, and key management
- For certificates stored in Azure Key Vault:
az keyvault certificate list --vault-name <KEYVAULT_NAME> -o tableaz keyvault certificate show --vault-name <KEYVAULT_NAME> -n <CERT_NAME> \--query "{name:name, enabled:attributes.enabled, \notBefore:attributes.notBefore, expires:attributes.expires}" -o json
- Confirm:
- Certificates are valid (not expired, correct CN/SAN for hostnames).
- Private keys are protected (Key Vault or secure secret store).
- Automated renewal and reload are configured where possible.
- For certificates stored in Azure Key Vault:
-
Harden TLS policy and disable weak protocols/ciphers
- For Application Gateway:
az network application-gateway show -g <APPGW_RG> -n <APPGW_NAME> \--query "sslPolicy" -o json
- Decide whether to:
- Use a predefined strong policy (e.g.,
AppGwSslPolicy20170401S) or - A custom policy that disables TLS 1.0/1.1 and weak ciphers.
- Use a predefined strong policy (e.g.,
- Update via console or IaC to enforce the chosen policy across all HTTPS listeners.
- For Application Gateway:
-
Document and, if necessary, implement corrections via console/CLI/IaC; then re-verify
- Apply required changes in your chosen IaC (ARM/Bicep/Terraform) or Azure portal:
- Ensure all external listeners use HTTPS with valid certificates.
- Remove or redirect plain HTTP endpoints.
- Align all entrypoints with the selected TLS policy.
- Re-run the evidence-gathering commands from steps 2–5 to confirm that:
- Only HTTPS endpoints are exposed externally.
- Certificates and TLS policies match your security requirements.
- Apply required changes in your chosen IaC (ARM/Bicep/Terraform) or Azure portal:
Using kubectl
kubectl cannot configure HTTPS load balancer TLS termination, because this setting is managed in your cloud provider’s load balancer and/or IaC configuration, not via Kubernetes API objects. Make the necessary changes in your cloud console/CLI or IaC definitions, and refer to the Manual Steps section for detailed guidance.
Automation
#!/usr/bin/env bash
# Report Kubernetes Services exposed via cloud load balancers and whether they
# are configured to use HTTPS/TLS on the external interface.
#
# Run on: any machine with kubectl access and correct context.
# Requires: kubectl, jq
set -euo pipefail
# Confirm kubectl works
kubectl version --short >/dev/null
echo "Collecting LoadBalancer Services and ingress-related annotations..."
echo
# Header
printf "%-30s %-20s %-20s %-10s %-60s\n" \
"NAMESPACE/NAME" "TYPE" "EXTERNAL_IP/INGRESS" "PORTS" "TLS / HTTPS INDICATORS"
echo "------------------------------------------------------------------------------------------------------------------------------------------"
# 1) Services of type LoadBalancer
kubectl get svc --all-namespaces -o json | jq -r '
.items[] |
select(.spec.type == "LoadBalancer") |
# Basic info
. as $svc |
(
(.metadata.namespace + "/" + .metadata.name) as $id |
"Service " + $id,
(.status.loadBalancer.ingress // [] | map(.ip // .hostname) | join(",")) as $ing,
(.spec.ports // [] | map((.port|tostring)+"/"+(.protocol//"")) | join(",")) as $ports,
# Heuristics for HTTPS/TLS:
# - Any port 443 or named "https"
# - Common cloud-controller annotations showing HTTPS listeners or certificates
(
[
(if any(.spec.ports[]?; .port==443 or .name=="https") then "PORT_443_OR_HTTPS" else empty end),
(.metadata.annotations["service.beta.kubernetes.io/aws-load-balancer-ssl-cert"] // empty | select(length>0) | "AWS_SSL_CERT"),
(.metadata.annotations["service.beta.kubernetes.io/aws-load-balancer-backend-protocol"] // empty | select(test("https|ssl";"i")) | "AWS_BACKEND_HTTPS"),
(.metadata.annotations["service.beta.kubernetes.io/aws-load-balancer-ssl-ports"] // empty | "AWS_SSL_PORTS=" + .),
(.metadata.annotations["service.beta.kubernetes.io/azure-load-balancer-internal"] // empty | "AZURE_INTERNAL_LB"),
(.metadata.annotations["service.beta.kubernetes.io/azure-load-balancer-tls-cert"] // empty | "AZURE_TLS_CERT"),
(.metadata.annotations["cloud.google.com/app-protocols"] // empty | "GKE_APP_PROTOCOLS=" + .),
(.metadata.annotations["networking.gke.io/app-protocols"] // empty | "GKE_NET_APP_PROTOCOLS=" + .),
(.metadata.annotations["networking.gke.io/managed-certificates"] // empty | "GKE_MANAGED_CERTS=" + .),
(.metadata.annotations["service.beta.kubernetes.io/do-loadbalancer-tls-passthrough"] // empty | "DO_TLS_PASSTHROUGH=" + .),
(.metadata.annotations["service.beta.kubernetes.io/do-loadbalancer-protocol"] // empty | "DO_LB_PROTOCOL=" + .),
(.metadata.annotations["service.beta.kubernetes.io/linode-loadbalancer-ssl-ports"] // empty | "LINODE_SSL_PORTS=" + .),
(.metadata.annotations["service.beta.kubernetes.io/oci-load-balancer-ssl-ports"] // empty | "OCI_SSL_PORTS=" + .)
] | unique | join(",")
) as $tlsHints |
# Emit one line
@tsv "\($id)\tService\t\($ing)\t\($ports)\t\($tlsHints)"
)
' | while IFS=$'\t' read -r id type ing ports tls; do
printf "%-30s %-20s %-20s %-10s %-60s\n" "$id" "$type" "${ing:-<pending>}" "${ports:--}" "${tls:-<none>}"
done
echo
echo "Collecting Ingresses and TLS usage..."
echo
# 2) Ingress resources (may front HTTPS load balancers depending on class/controller)
kubectl get ingress --all-namespaces -o json 2>/dev/null | jq -r '
.items[]? |
. as $ing |
(
(.metadata.namespace + "/" + .metadata.name) as $id |
(.spec.ingressClassName // .metadata.annotations["kubernetes.io/ingress.class"] // "<none>") as $class |
(.status.loadBalancer.ingress // [] | map(.ip // .hostname) | join(",")) as $addr |
((.spec.tls // []) | length | tostring) as $tlsCount |
(
[
(if (.spec.tls // []) | length > 0 then "INGRESS_TLS_SECTIONS="+$tlsCount else empty end),
(.metadata.annotations["nginx.ingress.kubernetes.io/backend-protocol"] // empty | "NGINX_BACKEND_PROTO=" + .),
(.metadata.annotations["nginx.ingress.kubernetes.io/ssl-redirect"] // empty | "NGINX_SSL_REDIRECT=" + .),
(.metadata.annotations["ingress.gcp.kubernetes.io/pre-shared-cert"] // empty | "GCE_PRESHARED_CERT=" + .),
(.metadata.annotations["networking.gke.io/managed-certificates"] // empty | "GKE_MANAGED_CERTS=" + .),
(.metadata.annotations["alb.ingress.kubernetes.io/certificate-arn"] // empty | "AWS_ALB_CERT_ARN=" + .),
(.metadata.annotations["alb.ingress.kubernetes.io/listen-ports"] // empty | "AWS_ALB_LISTEN_PORTS=" + .),
(.metadata.annotations["azure/application-gateway-cert"] // empty | "AZURE_APPGW_CERT=" + .),
(.metadata.annotations["konghq.com/protocols"] // empty | "KONG_PROTOCOLS=" + .)
] | unique | join(",")
) as $tlsHints |
@tsv "\($id)\tIngress(\($class))\t\($addr)\t-\t\($tlsHints)"
)
' 2>/dev/null | while IFS=$'\t' read -r id type addr ports tls; do
printf "%-30s %-20s %-20s %-10s %-60s\n" "$id" "$type" "${addr:-<pending>}" "-" "${tls:-<none>}"
done
cat <<'EOF'
How to interpret this report
----------------------------
Each row represents:
- Service: a Service of type LoadBalancer that will be backed by a cloud load balancer.
- Ingress: an Ingress that typically provisions an external load balancer through its controller.
Columns:
- NAMESPACE/NAME: Resource identifier.
- TYPE: Service or Ingress(+class).
- EXTERNAL_IP/INGRESS: External address of the cloud load balancer if assigned.
- PORTS: Exposed ports (for Services).
- TLS / HTTPS INDICATORS: Heuristics suggesting HTTPS/TLS is configured.
Potential problems (to review manually in cloud console / IaC):
- Service rows where:
- PORTS only show HTTP ports (e.g., 80/TCP) and
- TLS / HTTPS INDICATORS is "<none>" or does not mention provider-specific TLS annotations.
- Ingress rows where:
- TLS / HTTPS INDICATORS is "<none>" and
- You expect HTTPS termination at the load balancer for those hosts.
This script does NOT prove traffic is encrypted:
- It only highlights where TLS-like config appears present or absent in Kubernetes objects.
- You must confirm in your cloud provider console, CLI, or IaC:
- That the external load balancer listeners use HTTPS/TLS.
- That appropriate certificates are attached.
- That HTTP (if present) is redirected to HTTPS or otherwise acceptable by your policy.
EOF