Permalink
Fetching contributors…
Cannot retrieve contributors at this time
132 lines (87 sloc) 6.93 KB

Managing Elastic Load Balancers with Ironfan

Example

Ironfan.cluster "sparky" do

  cloud(:ec2) do
    # This certificate can be defined at a cluster or facet level.
    # However, the last call wins if the same attribute is defined
    # in more than one context.
    iam_server_certificate "snake-oil" do
      certificate       IO.read('snakeoil.crt')
      private_key       IO.read('snakeoil.key')
      certificate_chain IO.read('snakeoil.crt.bundle') # optional
    end
  end

  facet :web do
    instances 2
    cloud(:ec2) do

      elastic_load_balancer "sparky-elb" do
        map_port('HTTP',   80, 'HTTP', 81)

        # This SSL listener uses the certificate defined above
        map_port('HTTPS', 443, 'HTTP', 81, 'snake-oil')

        # Applies to all HTTPS/SSL listeners
        allowed_ciphers(%w[ Protocol-SSLv3 Protocol-TLSv1 RC4-MD5 RC4-SHA ])

        # If AWS tries to add other ciphers automatically because "they know
        # best", and you really don't want that cipher (e.g. the cipher is
        # flagged as problematic by SSLLabs, nessus, etc.) you can explicitly
        # disallow the cipher from your HTTPS/SSL listeners thusly.
        disallowed_ciphers(%w[ AES128-SHA ])
        # PROTIP: The disallowed_ciphers call is usually unnecessary

        # Health check that is made against ALL running instances
        health_check do
          ping_protocol       'HTTP'
          ping_port           82
          ping_path           '/healthcheck'
          timeout             4
          interval            10
          unhealthy_threshold 3
          healthy_threshold   2
        end
      end

    end
  end
end

Uploading your certificate

There are two ways to supply your certificate to Ironfan. The first is to make the certificate, private key, and (optionally) certificate chain data readable by Ruby code in your cluster file.

iam_server_certificate "snake-oil" do
  certificate       IO.read('snakeoil.crt')
  private_key       IO.read('snakeoil.key')
  certificate_chain IO.read('snakeoil.crt.bundle') # optional
end

Having your server certificates persistently available in your ironfan-homebase might not be optimal, so you can simply specify the ARN of the existing IAM server certificate:

iam_server_certificate "snake-oil" do
  arn 'arn:aws:iam::782698214375:server-certificate/ironfan-sparky-snake-oil'
end

When are Certificates and ELBs updated?

Your server certificates and ELBs are only relevant when there are server instances present to service them. Therefore, the following logic is applied to decide when to synchronize or update your certificate/ELB configuration:

bootstrap, kill, launch, start, stop, sync

When one of these knife cluster actions finishes, the Ironfan code will examine the up-to-date list of running servers to see if there are any ELBs that are now needed, or which are no longer needed. In support of these feature, any required certificate uploads, health check modifications, SSL policy updates, or listener modifications will be performed as well.

If an ELB is no longer needed, it will be destroyed but any corresponding certificates will not be destroyed. This is a convenience for system administrators who want to load their sensitive/precious certificates into AWS just once, and is intended to be used in conjunction with the iam_server_certificate.arn attribute.

(To get a list of existing ARNs, try the IAM Command Line Toolkit provided by AWS.)

kick, list, proxy, pry, show, ssh

These knife cluster commands are not associated with updates of the Chef or IAAS configurations of your server instances, so no certificate or ELB modifications occur after these commands complete.

SSL policy

The SSL policy control in Ironfan is very rudimentary. You may control which ciphers are explicitly allowed or disallowed as follows

elastic_load_balancer "sparky-elb" do
  ...
  allowed_ciphers(%w[ Protocol-SSLv3 Protocol-TLSv1 RC4-MD5 RC4-SHA ])
  disallowed_ciphers(%w[ AES128-SHA ])
  ...
end

Note that the default behavior is to allow a standard "safe" list of ciphers supported by most modern browsers, and to disallow ciphers that are hypothetically vulnerable to the BEAST attack and RC4 attacks (http://en.wikipedia.org/wiki/Transport_Layer_Security#RC4_attacks). You probably don't want or need to change it.

NOTE: If you do call allowed_ciphers or disallowed_ciphers, you will be overriding the built-in defaults and will need to specify the complete list of allowed or disallowed ciphers instead of just the ones you want to add or remove from the list.

How do port mappings work?

A port mapping is a mapping between a TCP port on the ELB and a TCP port on your instance. A request arriving at external_port_number on your ELB will be negotiated using the external_protocol, and will be forwarded to the internal_port on one of your instances using internal_protocol.

If external_protocol is 'HTTPS' and internal_protocol is 'HTTP' (which is referred to as "SSL termination" at the ELB), you'll probably want to know that in your server code. ELBs set the header X-Forwarded-Proto to let you know what protocol was used to connect to the ELB. Awareness of that header in your server allows you to aboid redirect loops if, for example, your code insists that clients connect over HTTPS.

The format of the map_port call is as follows:

map_port(external_protocol, external_port_number, internal_protocol, internal_port_number [, certificate])

You'll need a certificate if the external_protocol is 'HTTPS' or 'SSL'. It will be ignored otherwise.

Your one-and-only SSL policy (probably the BEAST-defeating default) will be applied to all port mappings with certificates.

Other policies

The Ironfan ELB code does not support other types of listener policies. Please contact me using the information below if you need other types of policy support.

VPC support

There is no explicit VPC support in this code. If you need to use Ironfan-based ELBs in a VPC context and find that this code doesn't give you what you need, please submit an issue or, better yet, a pull request.

Spanning facets

An ELB can be declared at the cluster level rather than the facet level, but that's a weird thing to do. But don't let me tell you not to be weird!

Note that since each ELB has only one health check, it would need to be the case that your servers in each facet could respond to the same health check request. If they can, they should probably be members of the same facet.

Contact me

This feature was initially created by Nick Marden. I'd love your feedback and suggestions.