How to setup SSL on Amazon Web Services with Nginx

If you wanted to enable SSL for your website, it is the best time to do it - "Let's Encrypt" provides SSL certificates for free. This article describes how to setup SSL using certificate issued by "Let's Encrypt" for the Amazon EC2 instance and Amazon S3 bucket.

Part 1. Setting up SSL for the Amazon EC2 instance

The first thing you should do is to prepare your machine.

  1. Install Python 2.7 and header libs:

    $ sudo yum install python27 python27-devel
    
  2. Update symlink to the Python installation:

    $ ln -sfn /usr/bin/python2.7 /etc/alternatives/python
    
  3. Install PIP:

    $ sudo curl https://bootstrap.pypa.io/get-pip.py | python2.7
    
  4. Or update PIP if you already have it installed:

    $ pip install --upgrade pip
    
  5. Update Virtual Environment:

    $ pip install --upgrade virtualenv
    
  6. Create new virtual environment:

    $ virtualenv -p /usr/bin/python27 venv27
    
  7. Activate virtual environment:

    $ . venv27/bin/activate
    
  8. If you don't have Git installed on your machine, install it:

    $ yum install git
    
  9. Clone "Let's Encrypt" repo and go to the letsencrypt folder:

    $ git clone https://github.com/letsencrypt/letsencrypt && cd letsencrypt/
    
  10. If you have Nginx server running, stop it:

    $ service nginx stop
    
  11. Run standalone plugin to issue the certificate. Please note, that running standalone plugin requires port 80 to be free (that is why Nginx was stopped in the previous step) since this plugin will run the temporary server on it:

    $ ./letsencrypt-auto certonly --debug --standalone -d <domain-name>
    
  12. After last step you'll find 4 files in the /etc/letsencrypt/live/<domain-name>:

    • cert.pem: Your domain's certificate
    • chain.pem: The Let's Encrypt chain certificate
    • fullchain.pem: cert.pem and chain.pem combined
    • privkey.pem: Your certificate's private key
  13. Now you need to edit Nginx configuration file, setup certificate and write a rule to redirect from http to the https version of your site:

    server {
        listen 80;
        server_name <your_server_name>;
        return 301 https://$server_name$request_uri;
    }
    
    
    server {
        listen 443 ssl;
        server_name your_server_name;            
        ... 
        add_header Strict-Transport-Security "max-age=31536000";        
        ssl_certificate /etc/letsencrypt/live/<domain-name>/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/<domain-name>/privkey.pem;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:AES256+ECDHE';
        ...
    }
    
  14. Open port 443 in the security group for the EC2 instance you are using in the EC2 management console.

  15. Great, now you can start the server and enjoy using SSL:

    $ service nginx start
    
  16. Test the server for the SSL configuration on the SSL Labs website. Probably, your mark will be B. This is because your server is vulnerable to the Logjam attack (more reading here, but this issue can be solved easily. All you need to do is just to generate strong Diffie-Hellman group:

    $ openssl dhparam -out dhparams.pem 2048
    

    And use it in the Nginx configuration:

    server {
        ...
        ssl_dhparam {path to dhparams.pem}
        ...
    }
    
  17. Re-test the site using SSL Labs, now you should have A+ rating.

  18. Please note, that certificates issued by "Let's Encrypt" are valid for 3 months, so every 3 months you should renew them. You can take a look at the official guide for the details.

Part 2. Setting up SSL for the Amazon S3

If you are hosting static files on the Amazon S3 and would like to access the bucket using SSL with you domain name, this part is for you (please note, that by default Amazon S3 already provides SSL support for the buckets by using address https://<bucket-name>.<region>.amazonaws.com). For this part you'll have to use Amazon CloudFront service, please check the pricing.

  1. Since you have no access to the Amazon S3 server, you need to use manual plugin to generate the certificate (you should have DNS record with statics sub-domain pointing to the Amazon S3 bucket):

    $ ./letsencrypt-auto certonly --manual -d <your-subdomain-name>
    

    By using this plugin you'll be asked to place special file with a special string in the bucket. After verification is completed, you'll get the same 4 files as in previous part.

  2. Install Amazon Web Services CLI:

    $ pip install awscli
    
  3. Log in to the Amazon IAM console. Now you need to create a user, just click Users -> Create New Users. After user is created, save the file with its credentials (it will contain Access Key Id and Secret Access Key), then click on its name and copy User ARN.

  4. The next thing you should do is to create the policy to upload the certificate. For this you can use policy generator: Policies -> Create Policy -> Select Policy Generator -> Select AWS Identity and Access Management in the AWS Service dropdown and check UploadServerCertificate in the Actions dropdown. In the Amazon Resource Name (ARN) paste ARN from the previous step. After the policy is created, attach it to the user.

  5. Configure Amazon Web Services CLI:

    $ awscli configure
    

    Paste Access Key Id and Secret Access Key you've got on the step 3, other fields can be skipped.

  6. Upload certificate to the AWS:

    $ sudo aws iam upload-server-certificate --server-certificate-name <certificate-name>
      --certificate-body file:///etc/letsencrypt/live/<subdomain-name>/cert.pem
      --private-key file:///etc/letsencrypt/live/<subdomain-name>/privkey.pem
      --certificate-chain file:///etc/letsencrypt/live/<subdomain-name>/chain.pem
      --path /cloudfront/certs/
    
  7. Log in to the Amazon CloudFront console. Click on the Create Distribution button -> Web distribution.

  8. The fields you should change/fill:

    • Origin Domain Name: click and select S3 bucket.
    • Origin ID: type some unique name.
    • View Protocol Policy: HTTPS only, but you can leave it as is if you want - HTTP and HTTPS.
    • Alternate Domain Names (CNAMEs): type your sub-domain name.
    • SSL Certificate: click Custom SSL Certificate and choose the one you've uploaded at the step 6.
    • Custom SSL Client Support: Select Only Clients that Support Server Name Indication (SNI). It is very important to choose this option, otherwise you'll be charged for ~600$ per month.
  9. Click Create distribution. After that you'll be given a link like <hash>.cloudfront.net. Please note, that it will take some time for the distribution to be created.

  10. Open console of your DNS registrar and create CNAME record with your sub-domain pointing to the CloudFront address you've got at the previous step. That is it, now you have working SSL for Amazon S3 bucket with your sub-domain name.

Part 3. Troubleshooting

  1. ERR_SSL_VERSION_OR_CIPHER_MISMATCH (Chrome)/ssl_error_no_cypher_overlap (Firefox) error while trying to access resource on the Amazon S3 bucket using my sub-domain.

    Probably, you didn't fill correctly Alternate Domain Names (CNAMEs) field for the CloudFront distribution.

  2. Website cannot be opened because of timeout error.

    Probably, you forgot to open port 443 in the security group of the EC2 instance.

  3. Running letsencrypt-auto returns ImportError: No module named OpenSSL error.

    Old Amazon AMIs have outdated virtualenv, that is why you have to update it. OpenSSL module will be installed to lib64 folder and this path is not correctly handled by virtualenv. See https://github.com/letsencrypt/letsencrypt/issues/1680#issuecomment-170641501. You can check where OpenSSL is installed by running:

     $ find ./ -type d | grep -i openssl
    

References

comments powered by Disqus