Skip to main content

Encrypt Traffic To HTTPS Load Balancers With TLS

More Info:

Configure HTTPS load balancers with TLS certificates so that traffic to the cluster is encrypted in transit.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS EKS

Triage and Remediation

Remediation

Manual Steps
  1. Inventory all external-facing load balancers

    • On any machine with cloud CLI access, list load balancers that front your EKS cluster (adjust region):
    • AWS:
      aws elbv2 describe-load-balancers --region us-east-1 \
      --query 'LoadBalancers[].{Name:LoadBalancerName,DNS:DNSName,Scheme:Scheme,Type:Type}'
      aws elb describe-load-balancers --region us-east-1 \
      --query 'LoadBalancerDescriptions[].{Name:LoadBalancerName,DNS:DNSName,Scheme:Scheme}'
    • From each returned LB, identify those used by the cluster (via tags or target groups):
      aws elbv2 describe-tags --region us-east-1 \
      --resource-arns <lb-arn-1> <lb-arn-2>
  2. Determine listener protocols and TLS configuration

    • For each Application/Network Load Balancer ARN:
      aws elbv2 describe-listeners --region us-east-1 --load-balancer-arn <lb-arn>
      Review each listener: confirm that internet-facing listeners use Protocol: HTTPS (or TLS for NLB) with a certificate (SslPolicy and Certificates set).
    • For each Classic Load Balancer name:
      aws elb describe-load-balancers --region us-east-1 \
      --load-balancer-names <elb-name>
      Confirm at least one Listener has Protocol: HTTPS and SSLCertificateId set.
  3. Verify certificates and TLS policies

    • For each HTTPS/TLS listener, list the referenced certificate from ACM:
      aws acm list-certificates --region us-east-1 \
      --certificate-statuses ISSUED
      aws acm describe-certificate --region us-east-1 \
      --certificate-arn <cert-arn>
      Ensure:
      • Certificate is ISSUED, not expired, and covers the LB DNS name or custom domain.
      • SslPolicy on listeners is a modern one (e.g., ELBSecurityPolicy-TLS13-1-2-Ext1-2021-06 or similar per your security standard).
  4. Remediate non-encrypted or misconfigured listeners

    • For any internet-facing listener using HTTP/TCP without TLS:
      • Create or import a valid certificate in ACM (if not already present).
      • Modify or add an HTTPS/TLS listener and (optionally) redirect HTTP to HTTPS:
        aws elbv2 create-listener --region us-east-1 \
        --load-balancer-arn <lb-arn> \
        --protocol HTTPS --port 443 \
        --default-actions Type=forward,TargetGroupArn=<tg-arn> \
        --certificates CertificateArn=<cert-arn> \
        --ssl-policy ELBSecurityPolicy-TLS13-1-2-Ext1-2021-06
        aws elbv2 modify-listener --region us-east-1 \
        --listener-arn <http-listener-arn> \
        --default-actions Type=redirect,RedirectConfig='{"Protocol":"HTTPS","Port":"443","StatusCode":"HTTP_301"}'
    • For Classic ELB:
      aws elb create-load-balancer-listeners --region us-east-1 \
      --load-balancer-name <elb-name> \
      --listeners Protocol=HTTPS,LoadBalancerPort=443,InstanceProtocol=HTTP,InstancePort=80,SSLCertificateId=<cert-arn>
  5. Update IaC definitions (if used)

    • In Terraform/CloudFormation/CDK, ensure:
      • aws_lb_listener / AWS::ElasticLoadBalancingV2::Listener uses HTTPS/TLS with ACM certificate ARNs and secure SSL policies.
      • Any HTTP listener has a redirect action to HTTPS.
    • Re-apply your IaC so the configuration is managed declaratively and not drift-prone.
  6. Verification (post-change)

    • Re-run listener inspection to confirm only HTTPS/TLS is exposed externally and certificates are attached:
      aws elbv2 describe-listeners --region us-east-1 --load-balancer-arn <lb-arn>
      aws elb describe-load-balancers --region us-east-1 --load-balancer-names <elb-name>
    • From a client, test that the endpoint negotiates TLS and matches the expected certificate:
      openssl s_client -connect <lb-dns-name>:443 -servername <your-domain> -showcerts </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
Using kubectl

kubectl cannot be used to configure TLS on HTTPS load balancers, because this setting is managed at the cloud provider / managed control-plane level (console, cloud CLI, or IaC). To remediate this finding, follow the provider-specific guidance in the Manual Steps section for configuring TLS certificates on your load balancers.

Automation
#!/usr/bin/env bash
#
# Check ingress and service frontends for use of HTTPS/TLS
# Run on: any machine with kubectl access and context set to the target cluster

set -euo pipefail

echo "=== Cluster-wide TLS Frontend Report ==="
echo "Context: $(kubectl config current-context)"
echo

############################################
# 1. Ingress resources (networking.k8s.io)
############################################
echo ">>> Ingress resources (expect tls: configured and HTTPS rules in most cases)"
kubectl get ingress --all-namespaces -o json | jq -r '
.items[] |
{
ns: .metadata.namespace,
name: .metadata.name,
class: (.spec.ingressClassName // "N/A"),
tls_hosts: ([.spec.tls[].hosts[]] // []),
rules: [.spec.rules[]? | {host, paths: [.http.paths[]? | {path, backend: .backend.service.name}]}]
} |
"NAMESPACE=\(.ns)\nNAME=\(.name)\nINGRESS_CLASS=\(.class)\nTLS_HOSTS=\(.tls_hosts|join(","))\nRULES=\(.rules)\n---"
'
echo

############################################
# 2. Services of type LoadBalancer
############################################
echo ">>> Services of type LoadBalancer (look for HTTPS ports and annotations)"
kubectl get svc --all-namespaces -o json | jq -r '
.items[] |
select(.spec.type == "LoadBalancer") |
{
ns: .metadata.namespace,
name: .metadata.name,
lb_ip: (.status.loadBalancer.ingress[0].ip // .status.loadBalancer.ingress[0].hostname // "pending"),
ports: [.spec.ports[] | {name, port, targetPort, protocol}],
annotations: (.metadata.annotations // {})
} |
"NAMESPACE=\(.ns)\nNAME=\(.name)\nEXTERNAL=\(.lb_ip)\nPORTS=\(.ports)\nANNOTATIONS=\(.annotations)\n---"
'
echo

############################################
# 3. IngressClasses (to infer controller / cloud LB type)
############################################
echo ">>> IngressClasses (to understand which controller / LB is used)"
kubectl get ingressclass -o wide || echo "No IngressClasses found"
echo

############################################
# 4. Optional: known TLS-related annotations per major cloud providers
############################################
echo ">>> Quick summary: LoadBalancer Services missing common TLS/HTTPS hints"
kubectl get svc --all-namespaces -o json | jq -r '
.items[] |
select(.spec.type == "LoadBalancer") |
. as $svc |
{
ns: .metadata.namespace,
name: .metadata.name,
ports: [.spec.ports[] | {port, name}],
annotations: (.metadata.annotations // {})
} |
. as $o |
(
# Heuristic: flag if any port is 80/http and no indication of HTTPS/SSL
([$o.ports[] | select((.port==80) or ((.name // "")|test("http";"i")))] | length) as $has_http
|
([$o.ports[] | select((.port==443) or ((.name // "")|test("https|ssl|tls";"i")))] | length) as $has_https_port
|
($o.annotations|tostring|test("tls|ssl|https";"i")) as $has_tls_ann
|
select($has_http == 1 and ($has_https_port == 0 and $has_tls_ann == false))
) |
"POTENTIAL_ISSUE: NAMESPACE=\(.ns) NAME=\(.name) PORTS=\(.ports)"
'
echo
echo "=== Interpretation ==="
cat <<'EOF'
This script does NOT confirm cloud LB TLS configuration; review is manual:

Potential problems in the output:
- Ingress:
- Ingress objects with no 'tls' section and rules that should be exposed securely.
- IngressClass/controller that maps to HTTP-only or non-TLS-terminated frontends.
- LoadBalancer Services:
- Services exposing port 80 / HTTP-only ports, with no corresponding 443/HTTPS port.
- Services whose annotations do not indicate TLS/SSL configuration for the provider.
Next steps:
- For each flagged or suspicious resource, locate the corresponding load balancer
in your cloud provider console/CLI and verify:
- An HTTPS listener is configured.
- A valid TLS certificate is attached.
- HTTP (port 80) is either disabled or redirects to HTTPS.
EOF