Fixing Kubernetes CrashLoopBackOff Due to Readiness Probe Failures on EKS

Tech Note: Always backup your configuration files before applying any changes to production environments. Fixing Kubernetes CrashLoopBackOff Due to Readiness Probe Failures on AWS EKS Experiencing a CrashLoopBackOff state in your Kubernetes pods running on Amazon Elastic Kubernetes Service (EKS) can be a frustrating and common issue. While various factors can lead to this state, one of the most frequent culprits is a misconfigured or failing Readiness Probe . As a Senior Cloud Solution Architect and Software Engineer, this comprehensive guide will walk you through understanding, diagnosing, and effectively resolving readiness probe failures to ensure your applications on EKS remain stable and highly available. Understanding Symptom Analysis & Root Causes The CrashLoopBackOff status indicates that a pod is repeatedly starting, crashing, and then restarting after a back-off delay. When this specific issue stems from a readiness prob...

Debugging Nginx SSL Handshake Failures with Lets Encrypt on AWS EC2 Behind an ELB

Tech Note: Always backup your configuration files before applying any changes to production environments.

Debugging Nginx SSL Handshake Failures with Let's Encrypt on AWS EC2 Behind an ELB

Navigating SSL/TLS handshake failures can be a daunting task, especially when dealing with a multi-layered infrastructure involving Nginx, Let's Encrypt, and AWS Elastic Load Balancers (ELB). This comprehensive guide provides a structured approach for diagnosing and resolving common SSL handshake issues, ensuring your web services remain secure and accessible. We'll dive deep into common symptoms, root causes, and provide step-by-step commands to get your secure connections back online.

Symptom Analysis & Root Causes

Understanding the symptoms is the first step towards an effective diagnosis. SSL handshake failures typically manifest in various ways depending on the client and the specific error.

Common Symptoms

  • Browser Errors: Clients encounter errors like ERR_SSL_PROTOCOL_ERROR, SSL_HANDSHAKE_FAILED, NET::ERR_CERT_COMMON_NAME_INVALID, or warnings about untrusted certificates.
  • curl or wget Failures: Command-line tools fail to connect with errors such as "SSL handshake failed", "certificate verify failed", or "unable to get local issuer certificate".
  • Nginx Error Logs: Nginx logs (typically found at /var/log/nginx/error.log) may show "SSL_do_handshake() failed", "no suitable certificate found", or other related SSL errors.
  • Application Connectivity Issues: Backend applications relying on SSL may fail to connect, indicating an underlying certificate or handshake problem.

Underlying Root Causes

SSL handshake failures in an AWS ELB + EC2 + Nginx + Let's Encrypt setup usually stem from misconfigurations at one of these layers:

  • AWS Elastic Load Balancer (ELB) Configuration:
    • Incorrect Listener Configuration: ELB not listening on port 443 (HTTPS) or forwarding to the wrong port on the EC2 instance (e.g., forwarding HTTPS to HTTP).
    • Target Group Issues: Health checks failing, causing ELB to mark instances as unhealthy.
    • ELB Security Group: Inbound rules on the ELB's security group not allowing traffic on port 443.
  • EC2 Security Group & Instance Firewall:
    • EC2 Security Group: Inbound rules not allowing traffic from the ELB (or 0.0.0.0/0 for testing) on port 443 (or the port Nginx is listening on for SSL).
    • Instance Firewall: Local firewall on the EC2 instance (e.g., ufw, firewalld, iptables) blocking port 443.
  • Nginx Configuration (nginx.conf):
    • Incorrect ssl_certificate or ssl_certificate_key paths.
    • Missing ssl_trusted_certificate (fullchain.pem) which provides the intermediate certificates.
    • Outdated or weak ssl_protocols or ssl_ciphers settings, causing incompatibility with clients.
    • Incorrect server_name directive for the SSL block.
    • Issues with HTTP/2 configuration (http2 directive).
    • Not enabling proxy_protocol if the ELB is configured to send it (typically for TCP listeners forwarding to an Nginx that handles SSL).
  • Let's Encrypt Certificates:
    • Expired Certificates: Certificates not renewed automatically by Certbot.
    • Incomplete Certificate Chain: Nginx configured with only the domain certificate, not the full chain (including intermediate CA certificates).
    • Permissions Issues: Nginx unable to read certificate files due to incorrect file permissions.
    • Wrong Domain: Certificate issued for a different domain name than the one Nginx is serving.
  • DNS Issues: Domain not resolving to the ELB's public DNS or IP address.

Step-by-Step Resolution Guide

Follow these steps systematically to pinpoint and resolve your SSL handshake issues.

Step 1: Verify AWS ELB Configuration

Start by ensuring your ELB is correctly configured to receive and forward HTTPS traffic. This guide assumes your ELB is set up with a TCP listener on port 443, forwarding traffic to the EC2 instance's port 443, where Nginx handles SSL termination. If your ELB terminates SSL, Nginx would typically listen on HTTP (port 80) and this guide's focus shifts slightly (but many Nginx configs for SSL still apply internally).

  • Check ELB Listener: Navigate to EC2 > Load Balancers in the AWS console. Select your ELB and check its "Listeners" tab. Ensure there's a listener for TCP:443 forwarding to a target group on TCP:443.
  • Target Group Health Checks: In the "Target Groups" section, select your target group and check the "Health checks" tab. Ensure the health check path and port are correct (e.g., HTTP on port 80 or 443 if you have an internal SSL setup for health checks). Instances must be "healthy".
  • ELB Security Group: Check the security group associated with your ELB. It must have an inbound rule allowing TCP:443 from 0.0.0.0/0 (or specific client IPs if restricted).

Step 2: Check EC2 Security Group and Instance Firewall

Ensure traffic can reach your EC2 instance on the required ports.

  • EC2 Security Group: Go to EC2 > Instances, select your instance, and check its "Security" tab. The associated security group must have an inbound rule allowing TCP:443 from the ELB's security group or 0.0.0.0/0 (for testing, restrict later). Also, ensure TCP:80 is open if you use HTTP for Certbot challenges (http-01).
  • Instance Firewall (e.g., UFW/Firewalld): SSH into your EC2 instance and check its local firewall status.
# For Ubuntu/Debian with UFW sudo ufw status verbose sudo ufw allow 443/tcp sudo ufw allow 80/tcp # For CentOS/RHEL with Firewalld sudo firewall-cmd --list-all sudo firewall-cmd --add-port=443/tcp --permanent sudo firewall-cmd --add-port=80/tcp --permanent sudo firewall-cmd --reload

Step 3: Inspect Nginx Configuration

Misconfigured Nginx SSL directives are a frequent culprit.

  • Locate Nginx Configuration: Nginx configurations are typically in /etc/nginx/nginx.conf and files included from /etc/nginx/sites-available/ or /etc/nginx/conf.d/.
  • Verify SSL Block: Look for the server block listening on port 443.
# Example Nginx SSL configuration snippet server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name yourdomain.com www.yourdomain.com; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem; # Important for OCSP stapling and full chain for older clients # Strong SSL/TLS protocols and ciphers ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'; ssl_prefer_server_ciphers off; # With modern ciphers, 'off' is recommended ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; # ... other Nginx directives ... }
  • Check File Paths: Ensure ssl_certificate and ssl_certificate_key paths point to valid Let's Encrypt files. Let's Encrypt typically creates symlinks in /etc/letsencrypt/live/yourdomain.com/.
  • Full Chain: Always use fullchain.pem for ssl_certificate. This includes your domain certificate and intermediate certificates. Some older clients might also benefit from ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem; if OCSP stapling is configured.
  • Syntax Check: Test your Nginx configuration for syntax errors.
sudo nginx -t

If there are errors, fix them based on the output. A successful check will return syntax is ok and configuration file /etc/nginx/nginx.conf test is successful.

Step 4: Validate Let's Encrypt Certificate

Ensure your Let's Encrypt certificates are valid, up-to-date, and accessible.

  • Check Certificate Expiry: Use Certbot to list your certificates and their expiry dates.
sudo certbot certificates
  • Manually Renew (if needed): If a certificate is expired or near expiration, attempt a dry run renewal first.
sudo certbot renew --dry-run # If dry run is successful, proceed with actual renewal sudo certbot renew
  • Check File Permissions: Ensure Nginx has read permissions for the certificate files and directories. The live directory and its contents are typically symlinked, and the actual certs are in /etc/letsencrypt/archive/. Nginx usually runs as the nginx or www-data user.
sudo ls -l /etc/letsencrypt/live/yourdomain.com/ sudo namei -mo /etc/letsencrypt/live/yourdomain.com/fullchain.pem

Step 5: Test SSL/TLS Handshake from Client Perspective

Use curl or openssl from a client machine (or your EC2 instance if allowed by firewall rules to connect to itself, or to the ELB DNS) to simulate a client connection.

# Using curl (from any client machine or your EC2 if ELB public facing) curl -vI https://yourdomain.com # Look for: # * SSL handshake successful # * Certificate details (CN, expiry, issuer) # * HTTP/2 (if enabled) # * HTTP headers

Errors like "SSL certificate problem: unable to get local issuer certificate" usually indicate an incomplete certificate chain provided by Nginx (e.g., missing fullchain.pem).

Step 6: Common Nginx SSL Directives Issues

  • ssl_protocols and ssl_ciphers: Ensure you are using modern, secure protocols (TLSv1.2 TLSv1.3) and strong ciphers. Remove older, insecure options like SSLv3, TLSv1, TLSv1.1.
  • server_name Mismatch: The server_name in your Nginx SSL block must match the domain name in your Let's Encrypt certificate.
  • http2 Directive: If using HTTP/2, ensure your Nginx version supports it and the http2 directive is present on the listen line (e.g., listen 443 ssl http2;).
  • Proxy Protocol (if applicable): If your ELB is a TCP listener on 443 and configured to use Proxy Protocol (less common but possible), Nginx might need the proxy_protocol directive on the listen line (e.g., listen 443 ssl http2 proxy_protocol;). However, this would typically be a "bad request" error, not an SSL handshake failure.

Step 7: Restart Nginx

After any configuration changes or certificate renewals, always reload or restart Nginx.

sudo systemctl reload nginx # Preferred for minimal downtime sudo systemctl restart nginx # Use if reload doesn't fix it or for significant changes sudo systemctl status nginx

Check sudo journalctl -xe | grep nginx or sudo tail -f /var/log/nginx/error.log for any new errors after restarting.

Step 8: Advanced Debugging with OpenSSL

For deeper insights, use openssl s_client to perform a handshake and examine the output.

# Connect to your server and dump certificate chain openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts # Look for: # * "Verify return code: 0 (ok)" # * The full certificate chain presented (Cert 0, Cert 1, etc.) # * Details of each certificate (Issuer, Subject, Expiry) # To check specific certificate file details openssl x509 -in /etc/letsencrypt/live/yourdomain.com/fullchain.pem -text -noout # To check private key validity openssl rsa -in /etc/letsencrypt/live/yourdomain.com/privkey.pem -check

If Verify return code is not 0 (ok), investigate the error code. Missing intermediate certificates are a common cause.

Best Practices for Prevention & Performance Optimization

Preventing SSL handshake failures is better than reacting to them. Implement these best practices for a robust and performant setup.

  • Automate Certbot Renewals: Ensure Certbot is configured to run automatically (e.g., via cron job) and includes a post-hook to reload Nginx:
    sudo certbot renew --nginx --post-hook "sudo systemctl reload nginx"
  • Use Strong Protocols & Ciphers: Regularly update your Nginx configuration to use only the latest and strongest TLS protocols (TLSv1.2, TLSv1.3) and ciphers. Use resources like Mozilla SSL Configuration Generator for recommended settings.
  • Enable OCSP Stapling: This reduces client load times and improves privacy by allowing Nginx to retrieve OCSP responses from the CA and "staple" them to the TLS handshake, instead of clients having to contact the CA directly. Ensure ssl_trusted_certificate and resolver directives are correctly set.
  • Implement HTTP to HTTPS Redirection: Force all traffic to HTTPS by redirecting HTTP requests to their HTTPS counterparts within Nginx.
    server { listen 80; listen [::]:80; server_name yourdomain.com www.yourdomain.com; return 301 https://$host$request_uri; }
  • Strict-Transport-Security (HSTS): Add the HSTS header to instruct browsers to always use HTTPS for your domain, even if they explicitly request HTTP. This enhances security and can improve performance on subsequent visits.
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
  • Monitor ELB Health Checks: Configure detailed health checks for your ELB target groups to swiftly identify unhealthy instances and prevent traffic from being routed to them.
  • Regularly Audit Configurations: Periodically review your Nginx, EC2 Security Group, and ELB configurations for any deviations from best practices or unintended changes.

Frequently Asked Questions (FAQ)

Q1: Why does my browser show NET::ERR_CERT_COMMON_NAME_INVALID?

This error indicates that the hostname you are trying to access (e.g., www.yourdomain.com) does not match the domain(s) listed on your SSL certificate. This can happen if the certificate was issued for yourdomain.com but not www.yourdomain.com (or vice-versa), or if you're trying to access an IP address directly. Ensure your Let's Encrypt certificate covers all necessary subdomains and aliases, and that your Nginx server_name directive correctly includes them.

Q2: My certificate is renewed, but clients still see an old one. Why?

After Certbot renews a certificate, you must reload or restart Nginx for it to pick up the new certificate files. If you only perform a certbot renew command without a post-hook, Nginx continues to serve the old, expired certificate. Always ensure your renewal process includes a command like sudo systemctl reload nginx. Additionally, some clients might cache old certificate information, though this is less common for full expiry issues.

Q3: What is OCSP stapling and should I use it?

OCSP (Online Certificate Status Protocol) stapling is a feature where the web server (Nginx) retrieves a signed, time-stamped OCSP response from the Certificate Authority (CA) and sends it ("staples" it) along with the certificate during the TLS handshake. This allows the client to verify the certificate's revocation status without having to connect to the CA's OCSP server directly. It significantly improves privacy (CA doesn't see client IP), performance (faster handshakes), and reliability (no reliance on client's connectivity to CA). Yes, you should definitely use it by configuring ssl_stapling on;, ssl_stapling_verify on;, and resolver directives in your Nginx configuration.

Popular posts from this blog

Debugging ImagePullBackOff in Kubernetes EKS with AWS ECR authentication issues

Fixing EKS Pod CrashLoopBackOff Due to Readiness Probe Failures

Resolve Nginx `upstream prematurely closed connection` with SSL termination for Docker containers