Setting up Let's Encrypt on AWS can be a little bit tricky. In this post I'm going to explain you how to configure your environment correctly for one or several domains / subdomains.

.ebextensions folder

In this folder, create a new file named (for example) : AWS_letsencrypt_config.config.

In this example, I have one domain and two subdomains :

  • mywebsite.com
  • admin.mywebsite.com
  • test.mywebsite.com

Step 1 : Configure the security groups

This step will allow traffic on port 443 (SSL). Don't forget to open port 443 if you have an existing instance.

Resources:
  sslSecurityGroupIngress: 
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]}
      IpProtocol: tcp
      ToPort: 443
      FromPort: 443
      CidrIp: 0.0.0.0/0

Step 2 : Configure Nginx

Now, we are going to create two configuration files.

The first file (000_http_redirect_custom.conf), will tell to the Nginx server to listen to any requests that come on port 80 (default HTTP) and redirect them to HTTPS.

files:
  /etc/nginx/conf.d/000_http_redirect_custom.conf:
    mode: "000644"
    owner: root
    group: root
    content: |
      server {
        listen 80;
        return 301 https://$host$request_uri;
      }

The second file (https_custom.pre), is about the SSL configuration : files location for our certificates and proxy.

/etc/nginx/conf.d/https_custom.pre:
    mode: "000644"
    owner: root
    group: root
    content: |
      # HTTPS server
      server {
        listen       443 default ssl;
        server_name  localhost;
        error_page  497 https://$host$request_uri;
        
        ssl_certificate      /etc/letsencrypt/live/ebcert/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/ebcert/privkey.pem;

        ssl_session_timeout  5m;
        ssl_protocols  TLSv1.1 TLSv1.2;
        ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
        ssl_prefer_server_ciphers   on;

        location / {
          proxy_pass http://docker;
          proxy_http_version 1.1;

          proxy_set_header Connection "";
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
      }

Step 3 : Container configuration

packages : package option to grab EPEL (Extra Packages for Enterprise Linux) for our certbot.

container_commands : the following code is divided in 7 steps :

  1. 00_create_dir : create a directory for certbot
  2. 10_installcertbotandchmod : download certbot-auto and give permission
  3. 20_getcert : generate certificate
  4. 30_link : create a symbolic link between our certificate and the live configuration directory
  5. 40_config : certificates are now created, so we can rename the .pre to .config
  6. 50_restartnginx : restart nginx
  7. 60_cronjobsetrenewal : create a cron job

Don't forget to set the EMAIL variable of your environnement configuration (from your AWS Console).

packages: 
  yum:
    epel-release: []

container_commands:
  00_create_dir:
    command: "mkdir -p /opt/certbot"
  10_installcertbotandchmod:
    command: "wget https://dl.eff.org/certbot-auto -O /opt/certbot/certbot-auto;chmod a+x /opt/certbot/certbot-auto"
  20_getcert:
    command: "sudo /opt/certbot/certbot-auto certonly --standalone --debug --non-interactive --email ${EMAIL} --agree-tos --domains mywebsite.com \
    -d admin.mywebsite.com \
    -d test.mywebsite.com \
    --expand --renew-with-new-domains --pre-hook \"service nginx stop\""
  30_link:
    command: "sudo ln -sf /etc/letsencrypt/live/mywebsite.com /etc/letsencrypt/live/ebcert"
  40_config:
    command: "mv /etc/nginx/conf.d/https_custom.pre /etc/nginx/conf.d/https_custom.conf"
  50_restartnginx:
    command: "sudo service nginx restart"
  60_cronjobsetrenewal:
    command: '(crontab -l ; echo ''0 6 * * * root /opt/certbot/certbot-auto renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start" --force-renew'') | crontab -'

That's it! Commit your changes and deploy it : eb deploy


Complete code :

Resources:
  sslSecurityGroupIngress: 
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]}
      IpProtocol: tcp
      ToPort: 443
      FromPort: 443
      CidrIp: 0.0.0.0/0

files:
  /etc/nginx/conf.d/000_http_redirect_custom.conf:
    mode: "000644"
    owner: root
    group: root
    content: |
      server {
        listen 80;
        return 301 https://$host$request_uri;
      }

    mode: "000644"
    owner: root
    group: root
    content: |
      # HTTPS server
      server {
        listen       443 default ssl;
        server_name  localhost;
        error_page  497 https://$host$request_uri;
        
        ssl_certificate      /etc/letsencrypt/live/ebcert/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/ebcert/privkey.pem;

        ssl_session_timeout  5m;
        ssl_protocols  TLSv1.1 TLSv1.2;
        ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
        ssl_prefer_server_ciphers   on;

        location / {
          proxy_pass http://docker;
          proxy_http_version 1.1;

          proxy_set_header Connection "";
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
      }

packages: 
  yum:
    epel-release: []

container_commands:
  00_create_dir:
    command: "mkdir -p /opt/certbot"
  10_installcertbotandchmod:
    command: "wget https://dl.eff.org/certbot-auto -O /opt/certbot/certbot-auto;chmod a+x /opt/certbot/certbot-auto"
  20_getcert:
    command: "sudo /opt/certbot/certbot-auto certonly --standalone --debug --non-interactive --email ${EMAIL} --agree-tos --domains mywebsite.com \
    -d admin.mywebsite.com \
    -d test.mywebsite.com \
    --expand --renew-with-new-domains --pre-hook \"service nginx stop\""
  30_link:
    command: "sudo ln -sf /etc/letsencrypt/live/mywebsite.com /etc/letsencrypt/live/ebcert"
  40_config:
    command: "mv /etc/nginx/conf.d/https_custom.pre /etc/nginx/conf.d/https_custom.conf"
  50_restartnginx:
    command: "sudo service nginx restart"
  60_cronjobsetrenewal:
    command: '(crontab -l ; echo ''0 6 * * * root /opt/certbot/certbot-auto renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start" --force-renew'') | crontab -'

Photo by Paul Esch-Laurent on Unsplash

Sources :

  1. https://matthewoden.com/setting-up-ssl-on-elastic-beanstalk/
  2. https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/ebextensions.html
  3. https://www.digitalocean.com/community/tutorials/how-to-configure-nginx-with-ssl-as-a-reverse-proxy-for-jenkins
  4. https://gist.github.com/tony-gutierrez/198988c34e020af0192bab543d35a62a