SSL offloading is an approach to handling secure Web traffic in which the computational burden of processing encrypted requests is allocated (or “offloaded”) to a specific component within an application’s environment.
The approach can improve performance as it allows application servers to serve unencrypted requests, which are computationally less expensive than encrypted ones. It can also reduce maintenance overhead as it requires certificates to be installed only on the component that is handling encrypted requests.
The approach obviously cannot be used in environments that require end-to-end encryption; in environments that do not have this requirement, however, it can be a useful technique to employ.
In this post I will describe how SSL offloading was implemented for this blog, a WordPress application that is deployed to AWS Elastic Beanstalk (EB). In so doing I make the following assumptions:
- The use of .ebextensions files to configure the EB environment
- The use of the EB CLI to create the environment
- The use of Apache HTTP Server as the WordPress application’s Web server
With these caveats out of the way, the first step toward implementing SSL offloading for this blog was to ensure the EB environment was instantiated with a load balancer, given that the load balancer is the component that will be handling encrypted requests.
Establishing the load balancer
In order for the EB environment to be instantiated with a load balancer, it was necessary to configure the environment for autoscaling. This is because, unlike single-instance environments, autoscaled environments require a load balancer in order to distribute traffic among EC2 instances. Following is the .ebextensions file that was used to ensure the load balancer was created:
option_settings: aws:autoscaling:launchconfiguration: InstanceType: {{InstanceType}} aws:autoscaling:asg: MinSize: {{MinSize}} MaxSize: {{MaxSize}}
The config specifies the type of EC2 instance (e.g., t3.small) autoscaling should launch within the target group, as well as the minimum and maximum number of instances that should be allowed within the group. (MinSize and MaxSize can both be set to 1 if a single instance is desired.)
With autoscaling thus configured, the next step toward implementing SSL offloading for this blog was to configure the load balancer itself.
Configuring the load balancer
Given that the load balancer needs to handle encrypted requests and that its default listener doesn’t handle such requests, it was necessary to create a new listener on the load balancer specifically for this purpose. Following is the .ebextensions file that was used to establish this listener:
Resources: HttpsListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: LoadBalancerArn: Ref: AWSEBV2LoadBalancer Protocol: HTTPS Port: 443 DefaultActions: - Type: forward TargetGroupArn: Ref: AWSEBV2LoadBalancerTargetGroup Certificates: - CertificateArn: {{CertificateArn}} SslPolicy: {{SslPolicy}}
The config specifies the type of resource to create (AWS::ElasticLoadBalancingV2::Listener) and the properties it should be created with. Some notes on the specific properties:
- LoadBalancerArn: References the load balancer by its logical name
- Protocol: Specifies that the listener should listen for HTTPS requests
- Port: Specifies that the listener should listen on port 443
- DefaultActions: Specifies that requests should be forwarded to the load balancer’s associated target group
- Certificates: References the SSL certificate (in this case stored in AWS Certificate Manager) that should be used to process requests
- SslPolicy: Specifies the SSL policy that should be used to enforce standards for the requests
With the load balancer thus configured, the next step toward implementing SSL for this blog was to configure the load balancer’s security group.
Configuring the load balancer’s security group
Given that the load balancer needs to process incoming requests on port 443 and that its security group doesn’t allow such requests by default, it was necessary to create an inbound rule on the security group for this purpose. Following is the .ebextensions file that was used to establish this inbound rule:
Resources: HttpsIngressRule: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Ref: AWSEBLoadBalancerSecurityGroup IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0
The config specifies the type of resource to create (AWS::EC2::SecurityGroupIngress) and the properties it should be created with. Some notes on the specific properties:
- GroupId: References the security group for the load balancer by its logical name
- IpProtocol: Specifies that the rule applies to TCP traffic
- FromPort: Specifies the lowest port number the rule should apply to
- ToPort: Specifies the highest port number the rule should apply to
- CidrIp: Specifies that the rule should allow traffic from the outside world
(Note that FromPort and ToPort have the same value; this results in the rule limiting traffic to port 443.)
With the security group thus configured, the EB environment was now ready to be created. Running eb create against the .ebextensions files described above created and configured a load balancer, and configured the load balancer’s security group.
With the EB requirements addressed, the only remaining step in implementing SSL offloading for this blog was to configure the WordPress application’s Web server to be able to operate in the context of an SSL-offloaded environment.
Configuring the Web server
The Web server used by the WordPress installation for this blog is Apache HTTP Server (Apache). Given that encrypted requests are being offloaded to EB, Apache is free to serve unencrypted requests, which as you’ll recall is one of the benefits of SSL offloading.
In order to resolve URLs correctly, however, WordPress needs to know that a request was originally encrypted, i.e., was sent over HTTPS. As such it was necessary to configure Apache to make WordPress “context-aware.” This was done via the following customization to the Apache conf file:
<VirtualHost *:80> ... <IfModule mod_setenvif.c> SetEnvIf X-Forwarded-Proto "^https$" HTTPS </IfModule> ... </VirtualHost>
The customization sets an environment variable (HTTPS) if Apache detects that a request was originally sent over HTTPS–internally WordPress reads from this variable when determining the protocol for URLs. A check is made to ensure Apache’s setenvif module is enabled. If so, the SetEnvIf directive enables the environment variable if the request has an “X-Forwarded-Proto” header with a value matching the supplied regular expression–“X-Forwarded-Proto” is an HTTP header that is sent along with requests from the EB load balancer to Apache. Note that Apache is configured to run on port 80 (VirtualHost *:80).
With Apache thus configured, SSL offloading was fully implemented for this blog.
Conclusion
While I wasn’t aware of SSL offloading as an approach prior to migrating this blog to EB, finding out about the approach and then implementing it turned out to be an added benefit of the migration, both in terms of simplifying the configuration for the blog’s development and production environments, and in terms of heightening my own awareness of the architecture that underpins an EB environment.