Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
AWS Elastic Beanstalk .ebextensions config for single instance free SSL using letsencrypt certbot and nginx. http://bluefletch.com/blog/domain-agnostic-letsencrypt-ssl-config-for-elastic-beanstalk-single-instances/
# Dont forget to set the env variable "certdomain", and either fill in your email below or use an env variable for that too.
# Also note that this config is using the LetsEncrypt staging server, remove the flag when ready!
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:
# The Nginx config forces https, and is meant as an example only.
/etc/nginx/conf.d/000_http_redirect_custom.conf:
mode: "000644"
owner: root
group: root
content: |
server {
listen 8080;
return 301 https://$host$request_uri;
}
# The Nginx config forces https, and is meant as an example only.
/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;
if ($ssl_protocol = "") {
rewrite ^ https://$host$request_uri? permanent;
}
location ~ ^/(lib/|img/) {
root /var/app/current/public;
access_log off;
}
location / {
proxy_pass http://nodejs;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
packages:
yum:
epel-release: []
container_commands:
10_installcertbot:
command: "wget https://dl.eff.org/certbot-auto;chmod a+x certbot-auto"
20_getcert:
command: "sudo ./certbot-auto certonly --debug --non-interactive --email XXX@XXX.com --agree-tos --standalone --domains ${certdomain} --keep-until-expiring --pre-hook \"service nginx stop\" --staging"
30_link:
command: "ln -sf /etc/letsencrypt/live/${certdomain} /etc/letsencrypt/live/ebcert"
40_config:
command: "mv /etc/nginx/conf.d/https_custom.pre /etc/nginx/conf.d/https_custom.conf"
@kikar

This comment has been minimized.

Copy link

commented Jul 7, 2016

Hey Tony, this is amazing, exactly what I was looking for.
So all I have to do is:

  1. create a folder .ebextensions and stick this file in it
  2. Set enviroment variable in EBS configuration called 'certdomain'
  3. Replace the email XXX@XXX.com in the script
  4. Remove --staging flag
    Correct?
@tony-gutierrez

This comment has been minimized.

Copy link
Owner Author

commented Jul 11, 2016

More or less correct. See my reply to your comment on the original blogpost. Also I found that using --standalone now works on AWS and is more reliable. See latest version of GIST.

@Giaco9

This comment has been minimized.

Copy link

commented Jul 12, 2016

Very nice Gist!
I'm using it on AWS Elastic Beanstalk single instance, nodeJS and socket.io and I need ssl. Over http everything works fine, on https socket.io always downgrade to polling transport, googling it i found only this link, could be a solution for my problem? Can you help me?

Thanks a lot!

@williamdias

This comment has been minimized.

Copy link

commented Jul 15, 2016

Did you have any success running this configuration during the elastic beanstalk creation? It seems that neither nginx nor puma (in my case) are running when certbot tries to validate the domain and get the certificate. I tried to create a shell script on /opt/elasticbeanstalk/hooks/appdeploy/post directory to perform all these configurations after the EBS setup but it didn't work either. Certbot can't get a response from the server (I tried forcing validation on ports 80 and 443). Nonetheless, if you are performing a simple deploy (not a eb creation), both methods work. Any ideas?

@tony-gutierrez

This comment has been minimized.

Copy link
Owner Author

commented Jul 26, 2016

@williamdias, I usually launch instances with the sample app, and then upload my code, so it may not work on creation. It sounds like a timing issue. Let me know what you find out? Also, I don't get notified of comments here, so you might want to comment on the original blog post. Thanks!

@DkGr

This comment has been minimized.

Copy link

commented Feb 17, 2017

Your script doesn't work anymore because nginx service is now always forced to run in all new AWS EBS instance.
So I modified one line to fix this in the 20_getcert command :

--pre-hook \"service nginx stop\"       become =>      --pre-hook \"initctl stop nginx\" --post-hook \"initctl start nginx\"

And I added a new command :

50_restartnginx:
  command: "sudo initctl restart nginx"

according to this stackoverflow fix

@Flo-D

This comment has been minimized.

Copy link

commented Apr 1, 2017

Hi @tony-gutierrez, I really want to thank you for this code ! It seems to be working for the certificate (I'm still with LetsEncrypt staging server)

However I keep getting this error (from /var/log/nginx/error.log) with a 502 Bad Gateway nginx/1.10.1 :

upstream prematurely closed connection while reading response header from upstream, client: 76.67.169.189, server: localhost, request: "GET /favicon.ico HTTP/1.1", upstream: "http://127.0.0.1:8081/favicon.ico", host: "xxx-env.eu-central-1.elasticbeanstalk.com", referrer: "https://xxx-env.eu-central-1.elasticbeanstalk.com/"

I'm running a NodeJS server. I've been looking for a solution for hours and I can't figure out what's causing it...
I tried to set proxy_buffers 8 32k, proxy_buffer_size 64k, proxy_read_timeout 90;

Have you - or anybody else - an idea of what could be the problem ?

EDIT : Solved the issue, seems like the proxy headers are case sensitive, such as this line :

proxy_set_header        Connection      "upgrade";

should become :

proxy_set_header        Connection      "Upgrade";

See : https://github.com/websocket-rails/websocket-rails/issues/55

@timnovis

This comment has been minimized.

Copy link

commented May 22, 2017

Thanks for this, absolute lifesaver! Do you have any tips on how I could automate renewal of the certificate?

@adailey14

This comment has been minimized.

Copy link

commented Aug 15, 2017

This is a great gist, thanks for sharing it.

I'm exploring using certbot via the nginx plugin (certbot-auto --nginx) in a post deploy hook. Have you tried that at all?

@adailey14

This comment has been minimized.

Copy link

commented Aug 15, 2017

FYI I did it via certbot-auto --nginx and was able to get it to work, with relatively simpler configuration. Hoping to make a script that can update ssls for new domains without a new deploy (for whitelabel client setup) by running certbot-auto -- nginx.

@jamesone

This comment has been minimized.

Copy link

commented Aug 16, 2017

@adailey14 let us know how you go :)

@jamesone

This comment has been minimized.

Copy link

commented Aug 16, 2017

Just out of curiosity, have you guys tried using AWS cert manager? They provide certs to you for free: https://aws.amazon.com/certificate-manager/pricing/

@starovoitovs

This comment has been minimized.

Copy link

commented Sep 1, 2017

You are great, thank you!

@erm213

This comment has been minimized.

Copy link

commented Sep 20, 2017

Does this handle the cert expiration? I know they are only valid for 90 days. My environment will probably be reloaded in that time, but just in case.

@utdrmac

This comment has been minimized.

Copy link

commented Dec 20, 2017

@adailey14 can you share your simplified version?

@cichondev

This comment has been minimized.

Copy link

commented Jan 9, 2018

Hello, I used this Gist to make mine, in which I use Apache. Who has interest, follow link: link gist .
Thanks @tony-gutierreztony-gutierrez, this was very helpful.

@SammyHam

This comment has been minimized.

Copy link

commented Jan 24, 2018

There are some problems with Certbot for the mode standalone on ngnix i think. So I created another script based on yours for the Webroot mode and it works well.

https://github.com/SammyHam/LetsEncrypt-SSL-config-for-Elastic-Beanstalk

Thank you very much for sharing.

@devaffair

This comment has been minimized.

Copy link

commented Feb 5, 2018

Hey
I got a noob's question..
What should be the value of the CERTDOMAIN attribute?

@jiawenzhang

This comment has been minimized.

Copy link

commented Feb 24, 2018

I own a domain xxx.com
there is CNAME record:
parse.xxx.com. | CNAME | app-production.us-east-1.elasticbeanstalk.com

what should I use for the CERTDOMAIN?
xxx.com, or parse.xxx.com or app-production.us-east-1.elasticbeanstalk.com?

I have tried all them and I keep getting the following errors:

Domain: app-production.us-east-1.elasticbeanstalk.com
Type: connection
Detail: Fetching
https://app-production.us-east-1.elasticbeanstalk.com/.well-known/acme-challenge/2Jxww5TDK7BuWhRL_LDcaYJ3J6Wj53Qd117U9itqaKg:
Connection refused

To fix these errors, please make sure that your domain name was
entered correctly and the DNS A/AAAA record(s) for that domain
contain(s) the right IP address.

@slenky

This comment has been minimized.

Copy link

commented Mar 5, 2018

@jiawenzhang , you would need to use domain name which has access from internet on 80 port. It's a simple LetsEncrypt verification step

@nabilfreeman

This comment has been minimized.

Copy link

commented Mar 7, 2018

@jiawenzhang - make sure your certdomain variable is lowercase (unless you edited the script to be uppercase).

@godsmustbcrazy

This comment has been minimized.

Copy link

commented Apr 26, 2018

@adailey14 Would you mind sharing your solution ?

@Pterobyte

This comment has been minimized.

Copy link

commented Nov 29, 2018

Anyone else getting an unauthorized error response from certbot?

image

Not sure if this is a problem with my domain (route-53) or with the .ebextensions config file

@dgnovo

This comment has been minimized.

Copy link

commented Mar 21, 2019

@jiawenzhang

I own a domain xxx.com
there is CNAME record:
parse.xxx.com. | CNAME | app-production.us-east-1.elasticbeanstalk.com

what should I use for the CERTDOMAIN?
xxx.com, or parse.xxx.com or app-production.us-east-1.elasticbeanstalk.com?

I have tried all them and I keep getting the following errors:

Domain: app-production.us-east-1.elasticbeanstalk.com
Type: connection
Detail: Fetching
https://app-production.us-east-1.elasticbeanstalk.com/.well-known/acme-challenge/2Jxww5TDK7BuWhRL_LDcaYJ3J6Wj53Qd117U9itqaKg:
Connection refused

To fix these errors, please make sure that your domain name was
entered correctly and the DNS A/AAAA record(s) for that domain
contain(s) the right IP address.

I have the same problem, do you resolve it?
Can you explain to me how?
Thanks in advance

@dgnovo

This comment has been minimized.

Copy link

commented Mar 21, 2019

Now i resolved problem with CNAME, but i have a problem with port / configuragion.
On my ELB, i have two listeners:
HTTP 80 => HTTP 80
and
HTTP 443 => HTTPS 443

Where can i corret?
Thanks

@tbezemer

This comment has been minimized.

Copy link

commented May 9, 2019

My two cents:
I integrated @DkGr 's comment, but I ran into some problems because the command he/she used to restart nginx didn't run on my flavour of AWS Linux, so I replaced the start/stop/restart service commands with sudo service nginx ... (as described in the Gist, and as empirically tested to work for me on my system):

container_commands:
  10_installcertbot:
    command: "wget https://dl.eff.org/certbot-auto;chmod a+x certbot-auto"
  20_getcert:
    command: "sudo ./certbot-auto certonly --debug --non-interactive --email XXX@XXXX.XXX --agree-tos --standalone --domains ${certdomain} --keep-until-expiring --pre-hook \"sudo service nginx stop\" --post-hook \"sudo service nginx start\""
  30_link:
    command: "ln -sf /etc/letsencrypt/live/${certdomain} /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"
@fkeichfe

This comment has been minimized.

Copy link

commented Jul 5, 2019

I followed the steps as described above and concerning the advice of @tbezemer.
The certificate is downloaded and installed, all steps from container_commands have been executed successfully, but the nginx does not use my custom configuration - in fact, the nginx configuration seems to be resetted to a standard configuration.
I checked this by connecting via SSH to the beanstalk-server. During deployment of my application I can see there for a short time my custom config file https_custom.conf in the folder /etc/nginx/conf.d, but a short time later it is gone...

Finally I discovered the solution for my problem - I had to modify the line 40_config as follows (50_restartnginx also seems not to be necessary, I could remove it without problem):

40_config:
    command: "mv /etc/nginx/conf.d/https_custom.pre /var/elasticbeanstalk/staging/nginx/conf.d/https_custom.conf"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.