Web applications are often deployed behind a load balancer or application firewall. This component provides secure encrypted sessions from the internet to the application.
It accomplishes this by listening for requests for the web application on ports exposed to the external environment and forwarding that traffic onto the application’s listening port. This forwarded traffic is often unencrypted.
However, in shared network environments such as Spinup, we highly recommend that traffic from the load balancer to the instance hosting the application be encrypted for conventional Spinup and require it for Secure Spinup.
Implementing encrypted session traffic to a web application hosted on a Spinup Linux instance is accomplished by installing Nginx as proxy on the instance hosting the web application.
This example has been validated on a CentOs 7 instance but is applicable to similar rpm
-based distributions.
The procedure involves the following steps:
Configuring Nginx as a reverse-proxy using the self-signed SSL certificate
Example configurations
In order to encrypt the connection between the internet-facing load balancer and the instance running your web application, we will need to create and install an SSL certificate.
Since only the load balancer will be connecting to the Nginx proxy, a self-signed certificate is sufficient.
Cut and paste the following shell commands into your remote shell session:
export CERT_KEY='/etc/pki/tls/private/domain.key' # Default CentOS certificate key export CERT='/etc/pki/tls/certs/domain.crt' # Default CentOS certificate path cert_expire=${CERT_EXPIRE:-3650} cert_size=${CERT_SIZE:-rsa:2048} cert_subject=${CERT_SUBJECT:-/CN=localhost} sudo openssl req -x509 -nodes \ -days "${cert_expire}" \ -newkey "${cert_size}" \ -keyout "${CERT_KEY}" \ -out "${CERT}" \ -subj "${cert_subject}" |
(Different distributions, use different locations for the key and the certificate. For example, Ubuntu uses the following:
CERT_KEY='/etc/ssl/private' # Default CentOS certificate key CERT='/etc/ssl/certs' # Default CentOS certificate path |
)
Cut and paste the following shell commands into your remote shell session:
# CentOS sudo yum -y update sudo yum install -y epel-release sudo yum install -y nginx # Amazon Linux 2 # sudo amazon-linux-extras install nginx1.12 # Ubuntu # sudo apt update # sudo apt install nginx |
Configuration of Nginx is modular. Global configuration is located in /etc/nginx/nginx.conf
. One could create server
blocks in this file. However, a better practice is to separate server
definitions in separate configuration files that are sourced into the main configuration when it starts.
Separate [server].conf
files can be created under the /etc/nginx/conf.d/
directory which are loaded at start. (Debian and Ubuntu take this even further. These distributions place site configurations in /etc/nginx/sites-available/
. To enable a specific site, a symlink to the specific /etc/nginx/sites-available/[server].conf
is created in /etc/nginx/sites-enabled/
.)
Cut and paste the following
export CERT_KEY='/etc/pki/tls/private/domain.key' # Default CentOS certificate key export CERT='/etc/pki/tls/certs/domain.crt' # Default CentOS certificate path export SERVER_FQDN="{{ .serverFqdn }}" export BACKEND_PORT="{{ .backEndPort }}" # change {{}} to the port the webapp is listening on. sudo tee /etc/nginx/conf.d/reverse-proxy-tls.conf<<-EOF upstream backend { server localhost:${BACKEND_PORT}; } server { listen 80; listen [::]:80; server_name ${SERVER_FQDN}; # redirect all HTTP requests to HTTPS with a 301 Moved Permanently response. return 301 https://${SERVER_FQDN}\$request_uri; } server { listen 443; server_name ${SERVER_FQDN}; ssl on; ssl_session_cache shared:SSL:40m; ssl_session_timeout 4h; ssl_protocols TLSv1.2; ssl_ciphers ECDH+AESGCM:ECDH+AES256-CBC:ECDH+AES128-CBC:DH+3DES:!ADH:!AECDH:!MD5; ssl_prefer_server_ciphers on; ssl_certificate ${CERT}; ssl_certificate_key ${CERT_KEY}; access_log /var/log/nginx/${SERVER_FQDN//./_}.log; error_log /var/log/nginx/${SERVER_FQDN//./_}-error.log error; location / { proxy_pass http://backend; proxy_buffers 16 4k; proxy_buffer_size 2k; proxy_set_header Host \$http_host; proxy_set_header ServerName \$server_name; proxy_set_header ServerPort 443; 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 https; proxy_set_header X-Forwarded-Scheme https; proxy_set_header X-Forwarded-URL-Scheme https; proxy_redirect default; } } EOF |
server { # .... Omitted location / { include uwsgi_params; uwsgi_pass unix:///home/klo9/webapp/webapp.sock; } } |
export CERT_KEY='/etc/pki/tls/private/domain.key' # Default CentOS certificate key export CERT='/etc/pki/tls/certs/domain.crt' # Default CentOS certificate path export SERVER_FQDN="{{ .serverFqdn }}" export BACKEND_PORT=3838 sudo tee "/etc/nginx/conf.d/${SERVER_FQDN//./_}.conf"<<-EOF upstream backend { server localhost:${BACKEND_PORT}; } map \$http_upgrade \$connection_upgrade { default upgrade; '' close; } server { listen 80; listen [::]:80; server_name ${SERVER_FQDN}; # redirect all HTTP requests to HTTPS with a 301 Moved Permanently response. return 301 https://${SERVER_FQDN}\$request_uri; } server { listen 443; server_name ${SERVER_FQDN}; ssl on; ssl_session_cache shared:SSL:40m; ssl_session_timeout 4h; ssl_protocols TLSv1.2; ssl_ciphers ECDH+AESGCM:ECDH+AES256-CBC:ECDH+AES128-CBC:DH+3DES:!ADH:!AECDH:!MD5; ssl_prefer_server_ciphers on; ssl_certificate ${CERT}; ssl_certificate_key ${CERT_KEY}; access_log /var/log/nginx/${SERVER_FQDN//./_}.log; error_log /var/log/nginx/${SERVER_FQDN//./_}-error.log error; location / { proxy_pass http://backend; proxy_read_timeout 20d; proxy_buffering off; proxy_http_version 1.1; 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_set_header Upgrade \$http_upgrade; proxy_set_header Connection \$connection_upgrade; } } EOF |
sudo systemctl enable nginx.service sudo systemctl start nginx.service |
Note for SELinux
On CentOS 7, in order to use Nginx as a proxy service (as we are doing in these examples), it must be allowed access to the network:
sudo setsebool -P httpd_can_network_connect 1
Note for SELinux
On CentOS 7, in order to use Nginx as a proxy service (as we are doing in these examples), it must be allowed access to the network:
sudo setsebool -P httpd_can_network_connect 1
In order to prevent direct access to the web application and force traffic to the TLS listener running in Nginx, you must enable the Space’s firewall for port 443
: How do I use the Firewall in Spinup Spaces?
Additionally, for moderate and low risk CIS OS images, the host based iptables
must be modified to allow traffic to port 443
.
https://www.nginx.com/blog/nginx-ssl/
https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/
https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp
https://docs.nginx.com/nginx/admin-guide/security-controls/securing-http-traffic-upstream/