Form.io/AWS Elastic Beanstalk End-To-End Encrypted Deployment
This tutorial will walk through how to create an example Form.io deployment environment on AWS Elastic Beanstalk that enables TLS/SSL-encrypted communication to the load balancer, from the load balancer to the EC2 instances, and lastly between each container within each instance. This tutorial assumes you have prior knowledge of Form.io and AWS Elastic Beanstalk, that you have an already running Form.io deployment in Elastic Beanstalk, and that your Elastic Beanstalk load balancer has an attached HTTPS listener. Please see AWS Deployment for more details.
This deployment configuration is rarely necessary, and should be considered a very advanced Form.io configuration for customers with strict external security regulations. If you have questions about whether your deployment may require TLS/SSL-encrypted communication between containers, please contact [email protected].
First, we'll need to ensure that the HTTPS listener on our load balancer will re-encrypt traffic going to the instances by adding an environment configuration file. Please see the Elastic Beanstalk documentation for more details.
.ebextensions/https-reencrypt-alb.config
option_settings:
aws:elbv2:listener:443:
DefaultProcess: https
ListenerEnabled: 'true'
Protocol: HTTPS
aws:elasticbeanstalk:environment:process:https:
Port: '443'
Protocol: HTTPS
Second, we'll need to modify our load balancer's security group to allow traffic. This will depend on whether you adopted a default security group or created a custom one during your Elastic Beanstalk environment creation; to cover both scenarios, use the following configuration file to create a security group and attach it to the load balancer.
.ebextensions/https-lbsecuritygroup.config
option_settings:
# Use the custom security group for the load balancer
aws:elb:loadbalancer:
SecurityGroups: '`{ "Ref" : "loadbalancersg" }`'
ManagedSecurityGroup: '`{ "Ref" : "loadbalancersg" }`'
Resources:
loadbalancersg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: load balancer security group
VpcId: vpc-########
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
(Be sure to replace
########
with your vpc identification number.)Next, we'll add ingress and egress rules that allow communication over port 443 between the load balancer's security group and the instances' security group.
.ebextensions/https-backendsecurity.config
Resources:
# Add 443-inbound to instance security group (AWSEBSecurityGroup)
httpsFromLoadBalancerSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]}
IpProtocol: tcp
ToPort: 443
FromPort: 443
SourceSecurityGroupId: {"Fn::GetAtt" : ["loadbalancersg", "GroupId"]}
# Add 443-outbound to load balancer security group (loadbalancersg)
httpsToBackendInstances:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: {"Fn::GetAtt" : ["loadbalancersg", "GroupId"]}
IpProtocol: tcp
ToPort: 443
FromPort: 443
DestinationSecurityGroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]}
Now that we've ensured TLS/SSL-encrypted traffic from the load balancer to our instances, we'll move on to configuring our instances to be able to handle HTTPS traffic.
In a typical enterprise deployment of the Form.io platform, each server instance behind the load balancer will contain the Enterprise Server Docker container, the PDF Server Docker container, and a reverse proxy (commonly nginx running in its own Docker container) to direct traffic that arrives at the instance to one or both services.
[diagram here?]
docker-compose.yml
version: "3.8"
services:
api-server:
image: formio/formio-enterprise:8.0.1
mem_limit: 2048m
restart: always
links:
- pdf-server
volumes:
- "./certs:/src/certs:ro"
environment:
MONGO_CA: /src/certs/rds-combined-ca-bundle.pem
PORTAL_ENABLED: 1
PORT: 3000
ports:
- "3000:3000"
env_file:
- .api.env
pdf-server:
image: formio/uswds-viewer:4.0.0-rc.1
restart: always
mem_limit: 2048m
volumes:
- "./certs:/src/certs:ro"
environment:
MONGO_CA: /src/certs/rds-combined-ca-bundle.pem
FORMIO_PDF_PORT: 4005
ports:
- "4005:4005"
env_file:
- .pdf.env
nginx-proxy:
image: nginx
restart: always
mem_limit: 128m
ports:
- "443:443"
volumes:
- "./certs:/src/certs:ro"
- "./conf.d:/etc/nginx/conf.d:ro"
links:
- api-server
- pdf-server
Since our load balancer will be sending HTTPS traffic, we'll first need to ensure that our nginx reverse proxy can process HTTPS traffic by updating our
conf.d
file.conf.d/default.conf
client_header_timeout 300;
client_body_timeout 300;
send_timeout 300;
proxy_connect_timeout 300;
proxy_read_timeout 300;
proxy_send_timeout 300;
server {
listen 443 ssl;
ssl_certificate /src/certs/mydomain.com.pem;
ssl_certificate_key /src/certs/mydomain.com-key.pem;
client_max_body_size 20M;
client_body_buffer_size 20M;
location / {
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 X-Forwarded-Proto $scheme;
proxy_read_timeout 300;
proxy_pass https://api-server:3000;
proxy_redirect https://api-server:3000 https://$host;
}
location /pdf/ {
rewrite ^/pdf/(.*)$ /$1 break;
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 X-Forwarded-Proto $scheme;
proxy_read_timeout 300;
proxy_pass https://pdf-server:4005;
proxy_redirect https://pdf-server:4005 https://$host;
}
}
You can find more details about this reverse proxy configuration in our deployment guide, but generally this reverse proxy will be able to receive HTTPS traffic and direct it, encrypted, to our containers at HTTPS addresses (in this case provided by the Docker network, e.g.
https://api-server:3000
or https://pdf-server:4005
).Now that our instances can receive HTTPS traffic and direct it to our containers, the final step will be to configure our Form.io applications to be able to receive this traffic.
Since the Form.io Enterprise Server and the Form.io PDF Server are NodeJS applications, we'll need to configure them to be able to process TLS/SSL-encrypted traffic. For this, we'll add the SSL_ENABLED, SSL_KEY, and SSL_CERT variables to each environment configuration (you of course can accomplish this via an environment file or via the Elastic Beanstalk configuration console).
.env
ENABLE_SSL=true
SSL_CERT="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
SSL_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
Pay special attention to these variables, as they should contain the content of the key and the certificate and not their path in the filesystem. Additionally, they should be a single line escaped with
\n
characters.Keep in mind that NodeJS maintains its own trusted root certificate store. If you're using certificates with a private CA (or using a tool like mkcert) you'll need to add your root certificate to NodeJS by using the
NODE_EXTRA_CA_CERTS
environment variable (e.g. NODE_EXTRA_CA_CERTS=/src/certs/rootCA.pem
). We don't recommend self-signed certificates for this configuration.With these variables in your environment, NodeJS will be able to accept TLS/SSL-encrypted traffic, and your deployment is encrypted end-to-end!
Last modified 26d ago