Encrypt Traffic Https Load Balancers With Tls Certificates
More Info:
Encrypt traffic to HTTPS load balancers using TLS certificates.
Risk Level
Low
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS AKS
- CIS Critical Security Controls v8
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- Securities and Exchange Board of India (SEBI) - Cloud Security Adoption Framework
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Inventory all AKS load balancers and frontends
- On any machine with Azure CLI access:
For each AKS cluster:az aks list -o tableRESOURCE_GROUP="<aks-resource-group>"CLUSTER_NAME="<aks-cluster-name>"# Find the node resource groupNODE_RG=$(az aks show -g "$RESOURCE_GROUP" -n "$CLUSTER_NAME" --query nodeResourceGroup -o tsv)# List load balancers in the node RGaz network lb list -g "$NODE_RG" -o table
- Record which LBs/frontends are used for HTTPS (often those tied to Ingress controllers or apps on port 443).
- On any machine with Azure CLI access:
-
Identify public HTTPS frontends and ports
- For each load balancer in the node resource group:
LB_NAME="<load-balancer-name>"NODE_RG="<node-resource-group>"# Frontend IPsaz network lb show -g "$NODE_RG" -n "$LB_NAME" --query "frontendIPConfigurations[].{name:name,private:privateIPAddress,public:publicIPAddress.id}" -o table# Load-balancing rules (look for port 443)az network lb rule list -g "$NODE_RG" --lb-name "$LB_NAME" -o table
- Determine which frontends expose HTTPS (port 443) to the internet (public IPs).
- For each load balancer in the node resource group:
-
Determine TLS termination point and certificate usage
- If you use an Azure Application Gateway / Application Gateway Ingress Controller (AGIC):
# List gatewaysaz network application-gateway list -o tableAPPGW_RG="<appgw-resource-group>"APPGW_NAME="<appgw-name>"# Show HTTPS listeners and SSL certsaz network application-gateway http-listener list -g "$APPGW_RG" --gateway-name "$APPGW_NAME" -o tableaz network application-gateway ssl-cert list -g "$APPGW_RG" --gateway-name "$APPGW_NAME" -o table
- If you terminate TLS on Kubernetes Ingress (e.g., NGINX ingress) behind a TCP/HTTP load balancer, review Ingress objects and their secrets (from any kubectl machine):
kubectl get ingress -Akubectl describe ingress -n <namespace> <name>kubectl get secret -n <namespace> <tls-secret-name> -o yaml
- Decide for each HTTPS entrypoint whether TLS is terminated at Azure L7 (Application Gateway/API Management/Front Door) or at the pod/Ingress.
- If you use an Azure Application Gateway / Application Gateway Ingress Controller (AGIC):
-
Assess TLS configuration, certificate validity, and coverage
- For each external hostname (e.g., app.example.com) exposed via the AKS cluster, run from any machine with
openssland network access:HOSTNAME="app.example.com"openssl s_client -connect "${HOSTNAME}:443" -servername "${HOSTNAME}" </dev/null 2>/dev/null | openssl x509 -noout -text - Confirm:
- A certificate is presented (not plain HTTP).
- Certificate is not expired, is signed by a trusted CA, and matches the hostname.
- TLS version and ciphers comply with your org policy (e.g., TLS 1.2+).
- For each external hostname (e.g., app.example.com) exposed via the AKS cluster, run from any machine with
-
Remediate missing or improper TLS on HTTPS endpoints
- For any public endpoint on port 443 that is not encrypted, misconfigured, or using an invalid/self-signed cert where that is not acceptable:
- Choose a termination point (Azure Application Gateway, Azure API Management, Azure Front Door, or Kubernetes Ingress with a managed cert like cert-manager + Azure DNS/Key Vault).
- Provision or update certificates (e.g., Azure Key Vault-backed cert or Let’s Encrypt) and bind them to HTTPS listeners:
- Application Gateway example (attach an existing cert in Key Vault):
APPGW_RG="<appgw-resource-group>"APPGW_NAME="<appgw-name>"CERT_NAME="<ssl-cert-name>"az network application-gateway ssl-cert upload \-g "$APPGW_RG" \--gateway-name "$APPGW_NAME" \-n "$CERT_NAME" \--key-vault-secret-id "<full-kv-secret-id>"
- Update listeners to use HTTPS and the uploaded cert (if not already):
az network application-gateway http-listener update \-g "$APPGW_RG" \--gateway-name "$APPGW_NAME" \-n "<listener-name>" \--protocol Https \--ssl-cert "$CERT_NAME"
- Application Gateway example (attach an existing cert in Key Vault):
- If TLS terminates at Ingress, ensure each HTTPS host has a valid
tlssection and a corresponding secret containing a proper cert/key.
- For any public endpoint on port 443 that is not encrypted, misconfigured, or using an invalid/self-signed cert where that is not acceptable:
-
Re-verify after changes
- From any machine with network access:
HOSTNAME="app.example.com"curl -vk "https://${HOSTNAME}/" >/dev/nullopenssl s_client -connect "${HOSTNAME}:443" -servername "${HOSTNAME}" </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject -dates
- Confirm all externally accessible 443 endpoints serving cluster workloads now present valid TLS certificates and no unencrypted HTTPS load balancer endpoints remain.
- From any machine with network access:
Using kubectl
kubectl cannot be used to configure HTTPS load balancers or attach TLS certificates because this setting is managed at the cloud provider / managed control plane level (console, CLI, or IaC), not via Kubernetes API objects. Refer to the Manual Steps section for guidance on reviewing and configuring TLS for your cloud load balancers.
Automation
#!/usr/bin/env bash
#
# Report HTTPS Services and check for TLS/secret configuration
# Run on: any machine with kubectl access and appropriate permissions
#
# Requirements:
# - kubectl installed and configured (KUBECONFIG or in-cluster)
# - jq installed
set -euo pipefail
if ! command -v jq >/dev/null 2>&1; then
echo "ERROR: jq is required but not installed. Install jq and re-run." >&2
exit 1
fi
echo "=== Listing Services that are likely fronted by HTTPS load balancers ==="
echo "Namespace,Service,Type,Ports,Has_TLS_Secret,Ingress_Refs"
kubectl get svc --all-namespaces -o json \
| jq -r '
.items[]
| . as $svc
| .spec.ports[]?
| select(
# Heuristics for HTTPS:
# - port 443, 8443, or 9443
# - or named "https" or containing "tls"
(.port == 443 or .port == 8443 or .port == 9443) or
(.name != null and ((.name | ascii_downcase) == "https" or (.name | ascii_downcase) | contains("tls")))
)
| {
ns: $svc.metadata.namespace,
name: $svc.metadata.name,
type: $svc.spec.type,
ports: ($svc.spec.ports | map(.port|tostring) | join("/")),
tlsSecrets: (
# Look for common annotations that may reference TLS secrets/certs
[
($svc.metadata.annotations."service.beta.kubernetes.io/aws-load-balancer-ssl-cert" // empty),
($svc.metadata.annotations."service.beta.kubernetes.io/aws-load-balancer-ssl-ports" // empty),
($svc.metadata.annotations."service.beta.kubernetes.io/azure-load-balancer-tls-cert" // empty),
($svc.metadata.annotations."cloud.google.com/neg" // empty),
($svc.metadata.annotations."cloud.google.com/ssl-cert" // empty),
($svc.metadata.annotations."networking.gke.io/managed-certificates" // empty),
($svc.metadata.annotations."oci.oraclecloud.com/ssl-certificate-ocid" // empty)
]
| map(select(. != "")) | join(";")
),
ingRefs: (
# Collect Ingresses that route to this Service
(
[inputs] | . # no-op; placeholder when we extend
)
)
}
' \
| while IFS= read -r line; do
# We only printed the service info fields; consume as JSON again
ns=$(jq -r '.ns' <<<"$line")
name=$(jq -r '.name' <<<"$line")
type=$(jq -r '.type' <<<"$line")
ports=$(jq -r '.ports' <<<"$line")
tlsSecrets=$(jq -r '.tlsSecrets' <<<"$line")
has_tls="no"
if [ -n "$tlsSecrets" ]; then
has_tls="maybe"
fi
# Find ingress objects that reference this service
ingRefs=$(kubectl get ingress --all-namespaces -o json 2>/dev/null \
| jq -r --arg NS "$ns" --arg NAME "$name" '
.items[]
| . as $ing
| (.spec.rules[]?.http.paths[]?.backend.service // {}) as $b
| select($b.name == $NAME and $ing.metadata.namespace == $NS)
| "\($ing.metadata.namespace)/\($ing.metadata.name)"
' | sort -u | paste -sd "," - || true)
if [ -z "$ingRefs" ]; then
ingRefs="-"
fi
echo "${ns},${name},${type},${ports},${has_tls},${ingRefs}"
done
echo
echo "=== Listing Ingresses with / without TLS configuration ==="
echo "Namespace,Ingress,Has_TLS_Block,Hosts,Services"
kubectl get ingress --all-namespaces -o json 2>/dev/null \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
name: .metadata.name,
hasTLS: (if (.spec.tls // []) | length > 0 then "yes" else "no" end),
hosts: (
(.spec.rules // [])
| map(.host // "<no-host>") | sort | unique | join(";")
),
svcs: (
(.spec.rules // [])
| map(.http.paths[]?.backend.service.name // empty)
| map(select(. != "")) | sort | unique | join(";")
)
}
| "\(.ns),\(.name),\(.hasTLS),\(.hosts),\(.svcs)"
'
echo
echo "=== INTERPRETING THE OUTPUT ==="
cat <<'EOF'
Potential problems that need manual review:
1) Services section:
- Any Service of type LoadBalancer or NodePort exposing HTTPS-like ports (443/8443/9443 or named "https"/with "tls")
where "Has_TLS_Secret" is "no":
-> May indicate traffic to the load balancer is NOT using provider-managed TLS termination.
Confirm in your cloud provider:
- Is the LB listener using HTTPS with a certificate?
- Is TLS terminated at the LB or passed through to the node?
- "Has_TLS_Secret" = "maybe":
-> The Service has annotations that might reference TLS/cert configuration.
Manually verify in the cloud provider console/CLI that:
- A valid TLS certificate is attached.
- Only HTTPS listeners are exposed (or HTTP is redirected to HTTPS).
2) Ingress section:
- Any Ingress where "Has_TLS_Block" = "no":
-> Ingress rules serve HTTP only. For internet-facing endpoints this is usually a problem.
Manually verify:
- Whether TLS should be enabled for these hosts.
- Whether there is a corresponding HTTPS Ingress or LB configuration in the provider.
- Ingress with "Has_TLS_Block" = "yes" but:
-> Check in the cloud provider:
- The referenced secret actually contains a valid certificate and key.
- The associated load balancer listeners are configured for HTTPS and use that cert.
This script cannot modify cloud load balancer or certificate settings.
Use it to identify Services/Ingresses to investigate, then review and configure
TLS termination in your cloud provider console/CLI/IaC as appropriate.
EOF
What indicates a problem
-
In the Services report: rows where:
TypeisLoadBalancerorNodePort, and- HTTPS-like ports (443/8443/9443 or named
https/withtls) are present, and Has_TLS_Secretisno.
These likely represent externally exposed endpoints where TLS at the load balancer is absent or unclear and require provider-side review.
-
In the Ingress report: rows where:
Has_TLS_Blockisnofor internet-facing hosts.
These indicate HTTP-only ingress where TLS should usually be configured, again requiring manual review and configuration in the cloud provider.