Skip to main content

Encrypt Traffic To HTTPS Load Balancers With TLS

More Info:

Configure HTTPS with TLS certificates on load balancers to encrypt traffic in transit. Consult your load balancer vendor documentation for configuration details.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS OKE

Triage and Remediation

Remediation

Manual Steps
  1. Inventory all externally exposed load balancers

    • On an admin workstation with cloud CLI configured, list all LBs that front Kubernetes workloads (adapt provider as needed):
      • AWS:
        aws elbv2 describe-load-balancers --query 'LoadBalancers[].{Name:LoadBalancerName,DNSName:DNSName,Type:Type,Scheme:Scheme}'
      • GCP:
        gcloud compute forwarding-rules list --format='table(name,IPAddress,IPProtocol,portRange,target)'
      • Azure:
        az network lb list --query '[].{Name:name,Frontends:frontendIPConfigurations[].privateIPAddress}'
    • Filter to internet-facing endpoints used by this cluster (by name, tags, annotations, or target backends).
  2. Verify protocol and ports for each public endpoint

    • For each LB DNS name or IP, check if HTTPS (443) is enabled and HTTP (80) is still open:
      nmap -Pn -p 80,443 <LB_DNS_or_IP>
    • In the cloud console, open each LB and review listeners / rules:
      • Confirm at least one HTTPS/TLS listener (usually port 443).
      • Note any plaintext HTTP listeners (80 or custom).
  3. Inspect TLS certificate configuration on HTTPS listeners

    • In the console for each LB with HTTPS:
      • Confirm a certificate is attached to the HTTPS listener.
      • Confirm:
        • The certificate is not expired and matches the LB hostname / domain.
        • A trusted CA was used (or your approved internal CA).
        • Strong TLS protocol versions and cipher policies are enforced (per your security baseline).
    • If needed, validate from an admin workstation:
      openssl s_client -connect <LB_DNS_or_IP>:443 -servername <LB_hostname> -showcerts </dev/null 2>/dev/null | openssl x509 -noout -text | egrep 'Subject:|Issuer:|Not Before:|Not After :'
  4. Decide and implement required HTTPS/TLS configuration changes

    • For any internet-facing LB without HTTPS:
      • In the cloud console or via IaC, add an HTTPS/TLS listener (e.g., 443) and point it to the same backend target group / pool.
      • Attach an existing approved certificate or request/import a new one using the provider’s certificate manager.
    • Optionally configure HTTP (80) only to:
      • Redirect to HTTPS (preferred), or
      • Be disabled entirely if not needed.
  5. Update or create TLS certificates as needed

    • For endpoints missing valid certificates or using weak / untrusted certs:
      • Request or import a compliant certificate via the cloud provider’s certificate manager (e.g., ACM, Google Managed Certs, Azure Key Vault/Certificates).
      • Attach the new certificate to the relevant HTTPS listeners.
    • Ensure IaC (Terraform, CloudFormation, ARM/Bicep, etc.) is updated to declare:
      • HTTPS listeners with certificate ARNs/IDs.
      • Any required redirect rules, so future deployments remain compliant.
  6. Verify enforcement of HTTPS and correct behavior

    • From an admin workstation, confirm HTTP is redirected or closed and HTTPS works:
      # Expect redirect or failure (no 200 OK over HTTP for sensitive apps)
      curl -I http://<LB_DNS_or_IP>

      # Expect successful TLS connection and 2xx/3xx response
      curl -I https://<LB_DNS_or_IP>
    • Optionally re-run the initial CLI listing and document which LBs:
      • Have HTTPS enabled with valid certificates, and
      • No longer expose unprotected HTTP endpoints (except where explicitly and formally approved).
Using kubectl

kubectl cannot configure TLS on cloud provider or external HTTPS load balancers; that configuration must be done in your cloud provider console/CLI or IaC tooling where the load balancer is defined. Refer to the Manual Steps section for guidance on reviewing and updating your load balancer TLS settings.

Automation
#!/usr/bin/env bash
# Purpose: Report Services using external load balancers that are NOT configured for HTTPS/TLS.
# Scope: Any machine with kubectl access and correct KUBECONFIG.

set -euo pipefail

echo "=== Checking Services of type LoadBalancer for HTTPS/TLS exposure ==="
echo

# 1) List all LoadBalancer Services with their ports and annotations
echo "All LoadBalancer Services and ports:"
kubectl get svc --all-namespaces -o jsonpath='{range .items[?(@.spec.type=="LoadBalancer")]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.loadBalancerIP}{"\t"}{range .spec.ports}{.port}{"/"}{.protocol}{":"}{.name}{" "}{end}{"\n"}{end}' \
| sed 's/[[:space:]]\+$//'
echo

# 2) Flag Services that:
# - Are type LoadBalancer
# - Expose any port that looks like HTTP (80/8080/8000-8099) AND
# - Do NOT expose a typical HTTPS/TLS port (443/8443) on the same Service
#
# These are *candidates* for unencrypted traffic and require manual review.

echo "Potentially PROBLEMATIC LoadBalancer Services (HTTP-like ports without HTTPS ports):"
echo -e "NAMESPACE\tNAME\tPORTS"

kubectl get svc --all-namespaces -o json | \
jq -r '
.items[]
| select(.spec.type == "LoadBalancer")
| {
ns: .metadata.namespace,
name: .metadata.name,
ports: [.spec.ports[] | {port: .port, name: (.name // ""), protocol: .protocol}]
}
| . as $svc
# Determine if any HTTP-like port exists
| ($svc.ports | map(select(
.protocol == "TCP"
and (
.port == 80
or .port == 8080
or (.port >= 8000 and .port <= 8099)
)
)) | length) as $http_like
# Determine if any HTTPS-like port exists
| ($svc.ports | map(select(
.protocol == "TCP"
and (
.port == 443
or .port == 8443
)
)) | length) as $https_like
# Report only those with HTTP-like ports and no HTTPS-like ports
| select($http_like > 0 and $https_like == 0)
| "\(.ns)\t\(.name)\t" +
($svc.ports | map("\(.port)/\(.protocol)(\(.name))") | join(", "))
' || echo "No candidates found or jq not installed."
echo

# 3) Ingress resources (if using cloud L7 load balancers) – list TLS usage

echo "Ingress resources and TLS configuration:"
echo -e "NAMESPACE\tNAME\tTLS_SECRETS"

kubectl get ingress --all-namespaces -o json 2>/dev/null | \
jq -r '
.items[]
| {
ns: .metadata.namespace,
name: .metadata.name,
tls: ( .spec.tls // [] | map(.secretName) | join(",") )
}
| "\(.ns)\t\(.name)\t\(.tls)"
' || echo "No Ingress resources found or jq not installed."
echo

cat <<'EOF'

INTERPRETING THE OUTPUT
-----------------------

1) "All LoadBalancer Services and ports":
- Review Services and confirm which ones are internet-facing load balancers.
- Any Service exposing HTTP-like ports without TLS termination on the LB is a concern.

2) "Potentially PROBLEMATIC LoadBalancer Services":
- Listed Services expose HTTP-like ports (80/8080/8000–8099) and do NOT expose typical HTTPS ports (443/8443).
- These are strong candidates for unencrypted traffic and must be reviewed:
* Check your cloud provider load balancer configuration (console/CLI/IaC)
to see if TLS is terminated there (HTTPS listener + certificate).
* If TLS is not configured on the load balancer, this is a finding.

3) "Ingress resources and TLS configuration":
- Ingress entries with an empty TLS_SECRETS column are *likely* serving HTTP only.
- For cloud-managed HTTP(S) load balancers, you must verify in the provider console
that HTTPS listeners/certificates are configured even if a TLS secret is present.

This script does NOT automatically fix configuration; it highlights where manual
review of cloud load balancer settings and TLS certificates is required.
EOF