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
Remediation
Manual Steps
-
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>
-
Determine listener protocols and TLS configuration
- For each Application/Network Load Balancer ARN:
Review each listener: confirm that internet-facing listeners useaws elbv2 describe-listeners --region us-east-1 --load-balancer-arn <lb-arn>
Protocol: HTTPS(orTLSfor NLB) with a certificate (SslPolicyandCertificatesset). - For each Classic Load Balancer name:
Confirm at least oneaws elb describe-load-balancers --region us-east-1 \--load-balancer-names <elb-name>
ListenerhasProtocol: HTTPSandSSLCertificateIdset.
- For each Application/Network Load Balancer ARN:
-
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 ISSUEDEnsure:aws acm describe-certificate --region us-east-1 \--certificate-arn <cert-arn>
- Certificate is
ISSUED, not expired, and covers the LB DNS name or custom domain. SslPolicyon listeners is a modern one (e.g.,ELBSecurityPolicy-TLS13-1-2-Ext1-2021-06or similar per your security standard).
- Certificate is
- For each HTTPS/TLS listener, list the referenced certificate from ACM:
-
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-06aws 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>
- For any internet-facing listener using HTTP/TCP without TLS:
-
Update IaC definitions (if used)
- In Terraform/CloudFormation/CDK, ensure:
aws_lb_listener/AWS::ElasticLoadBalancingV2::ListenerusesHTTPS/TLSwith ACM certificate ARNs and secure SSL policies.- Any
HTTPlistener has aredirectaction to HTTPS.
- Re-apply your IaC so the configuration is managed declaratively and not drift-prone.
- In Terraform/CloudFormation/CDK, ensure:
-
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
- Re-run listener inspection to confirm only HTTPS/TLS is exposed externally and certificates are attached:
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