Multi SSL Host With ELB

Saurabh Modh

08 May 2017

Multi SSL Host With ELB

For our setup, SSL negotiation will be done by nginx on the web server, rather than by the ELB. With nginx, leveraging multiple server blocks each with its own SSL certificate is pretty straight forward. Here is what you will need:

Nginx >= 1.6.2 Ubuntu >= 14.04 AWS CLI

Prerequisites: The nginx PPA includes the required modules, so there is no need to compile a build. Feel free to adjust to your own requirements.

Install Nginx

sudo add-apt-repository -y ppa:nginx/stable
sudo apt-get update
sudo apt-get -y install nginx

The AWS CLI will require credentials provided by your account.

Install AWS CLI

sudo apt-get install awscli
aws configure

Create and Configure the Load Balancer: The listener port should be created using the TCP protocol for both the Load Balancer Protocol and the Instance Protocol. The application layer protocol (HTTPS) is not handled until we reach the nginx instance. In most cases, the public port should be the standard 443.

Create proxy protocol policy

aws elb create-load-balancer-policy \
  --load-balancer-name acme-balancer \
  --policy-name EnableProxyProtocol \
  --policy-type-name ProxyProtocolPolicyType \
  --policy-attributes AttributeName=ProxyProtocol,AttributeValue=True

Add policy to elb

aws elb set-load-balancer-policies-for-backend-server \
  --load-balancer-name acme-balancer \
  --instance-port 443 \
  --policy-names EnableProxyProtocol
aws elb set-load-balancer-policies-for-backend-server \
  --load-balancer-name acme-balancer \
  --instance-port 80 \
  --policy-names EnableProxyProtocol

Describe ELB

upstream django {
  server app:8000;
}
server {
  listen 80 proxy_protocol;
    # sets the proper client ip
    real_ip_header proxy_protocol;
    # aws vpc subnet ip range
    set_real_ip_from ;
  server_name xyz.com;
  return 301 https://$server_name$request_uri;
   }
server {
  listen 443 ssl proxy_protocol;
  ssl    on;
  ssl_certificate /etc/cert/xyz_com.crt;
  ssl_certificate_key /etc/cert/xyz_com.key;
      # sets the proper client ip
    real_ip_header proxy_protocol;
    # aws vpc subnet ip range
    set_real_ip_from ;
  server_namexyz.com;
#  root /var/www/html;
    access_log /var/log/nginx-access.log;
    error_log /var/log/nginx-error.log;
    client_max_body_size 32M;
	gzip on;
	gzip_http_version  1.1;
	gzip_comp_level    2;
	gzip_buffers 16 8k;
	#gzip_min_length    1100;
	gzip_proxied       any;
	gzip_disable "MSIE [1-6].(?!.*SV1)";
	gzip_vary          on;
	gzip_types
    application/atom+xml
    application/javascript
    application/json
    application/rss+xml
    application/vnd.ms-fontobject
    application/x-font-ttf
    application/x-web-app-manifest+json
    application/xhtml+xml
    application/xml
    font/opentype
    image/svg+xml
    image/x-icon
    text/css
    text/plain
    text/x-component;
    location /static {
        alias   /srv/conf/collected/;
}
  location / {
    proxy_pass http://app:8000;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Port 443;
    proxy_redirect off;
    proxy_next_upstream error;
    # Add HTTP Strict Transport Security for good measure.
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;";
  }
}

And that’s it. If the real IP settings are working correctly, you should not need to setup a custom log format.

Creating separate server blocks for direct and proxied traffic is more verbose, but has a few benefits. It mitigates the need for conditional blocks later down the road. I also find that it is easier for others to understand.


Have a question?

Need Technology advice?

Connect

+1 669 253 9011

contact@hashedin.com

facebook twitter linkedIn youtube