Nginx load balancer SSL certs - nginx

I'm totally new to NGINX. I would like to use the free version (not nginx plus) to load balance (reverse proxy) between 3 servers and the connection must be SSL / 443.
Do i put the SSL certificate on the NGINX load balancer server or do I put 3 x SSL certs on the 3 web servers individually? I've heard mixed reviews. I'm looking for best performance.
Additional info: i'm using a wildcard SSL cert and the web other web servers are IIS with IP_Hash to keep sessions on the same web servers.

Open your configuration file again for edit.
sudo nano /etc/nginx/conf.d/load-balancer.conf
Then add the following server segment to the end of the file.
server {
listen 443 ssl;
server_name domain_name;
ssl_certificate /etc/letsencrypt/live/domain_name/cert.pem;
ssl_certificate_key /etc/letsencrypt/live/domain_name/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
location / {
proxy_pass http://backend;
}
}
Then save the file, exit the editor and restart nginx again.
sudo systemctl restart nginx
With the HTTPS-enabled you also have the option to enforce encryption to all connections to your load balancer
server {
listen 80;
server_name domain_name;
return 301 https://$server_name$request_uri;
#location / {
# proxy_pass http://backend;
#}
}
Save the file again after you have made the changes. Then restart nginx.
sudo systemctl restart nginx

This question was asked already in the StackExchange network, but I'm going to try and answer your question anyway.
The performance impact should be noticeable, but it really depends on what you're running.
One thing to consider using multiple certificates, is that once the request hits the load balancer, it stays secure inside the datacenter/network.
This is useful, in cases where you don't own the hardware and don't have physical access to the machines/datacenter. This is because there are probably multiple people running servers and applications in a shared space and you can't know for sure if someone in that network is snooping around and watching the traffic. You just can't be sure that's not going to happen.
Using only one certificate (for the load balancer) is called 'SSL Offloading' and you can and should find out more about it here:
https://security.stackexchange.com/questions/30403/should-ssl-be-terminated-at-a-load-balancer
https://security.stackexchange.com/questions/79672/in-detail-how-does-ssl-offloading-acceleration-termination-work
SSL performance implications
https://serverfault.com/questions/112547/does-using-ssl-cause-a-significant-performance-hit

Related

How to use nginx to direct traffic to two different ports on the same device?

I am currently working on a FPV robotics project that has two servers, flask/werkzeug and streamserver, serving http traffic and streaming video to an external web server, located on a different machine.
The way it is currently configured is like this:
http://1.2.3.4:5000 is the "web" traffic (command and control) served by flask/werkzeug
http://1.2.3.4:5001 is the streaming video channel served by streamserver.
I want to place them behind a https reverse proxy so that I can connect to this via https://example.com where "example.com" is set to 1.2.3.4 in my external system's hosts file.
I would like to:
Pass traffic to the internal connection at 1.2.3.4:5000 through as a secure connection. (certain services, like the gamepad, won't work unless it's a secure connection.)
Pass traffic to 1.2.3.4:5001 as a plain-text connection on the inside as "streamserver" does not support HTTPS connections.
. . . such that the "external" connection (to ports 5000 and 5001 are both secure connections as far as the outside world is concerned, such that:
[external system]-https://example.com:5000/5001----nginx----https://example.com:5000
\---http://example.com:5001
http://example.com:5000 or 5001 redirects to https.
All of the literature I have seen so far talks about:
Routing/load-balancing to different physical servers.
Doing everything within a Kubernates and/or Docker container.
My application is just an every-day plain vanilla server type configuration, and the only reason I am even messing with https is because of the really annoying problems with things not working except in a secure context which prevents me from completing my project.
I am sure this is possible, but the literature is either hideously confusing or appears to talk to a different use case.
A reference to a simple how-to would be the most usefull choice.
Clear and unambiguous steps would also be appreciated.
Thanks in advance for any help you can provide.
This minimal config should provide public endpoints:
http://example.com/* => https://example.com/*
https://example.com/stream => http://1.2.3.4:5001/
https://example.com/* => https://1.2.3.4:5000/
# redirect to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com
www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com
www.example.com;
ssl_certificate /etc/nginx/ssl/server.cer;
ssl_certificate_key /etc/nginx/ssl/server.key;
location /stream {
proxy_pass http://1.2.3.4:5001/; # HTTP
}
# fallback location
location / {
proxy_pass https://1.2.3.4:5000/; # HTTPS
}
}
First, credit where credit is due: #AnthumChris's answer is essentially correct.  However, if you've never done this before, the following additional information may be useful:
There is actually too much information online, most of which is contradictory, possibly wrong, and unnecessarily complicated.
It is not necessary to edit the nginx.conf file.  In fact, that's probably a bad idea.
The current open-source version of nginx can be used as a reverse proxy, despite the comments on the nginx web-site saying you need the Pro version.  As of this instant date, the current version for the Raspberry Pi is 1.14.
After sorting through the reams of information, I discovered that setting up a reverse proxy to multiple backend devices/server instances is remarkably simple.  Much simpler than the on-line documentation would lead you to believe.
 
Installing nginx:
When you install nginx for the first time, it will report that the installation has failed.  This is a bogus warning.  You get this warning because the installation process tries to start the nginx service(s) and there isn't a valid configuration yet - so the startup of the services fails, however the installation is (likey) correct and proper.
 
Configuring the systems using nginx and connecting to it:
 
Note: This is a special case unique to my use-case as this is running on a stand-alone robot for development purposes and my domain is not a "live" domain on a web-facing server.  It is a "real" domain with a "real" and trusted certificate to avoid browser warnings while development progresses.
It was necessary for me to make entries in the robot's and remote system's HOSTS file to automagically redirect references to my domain to the correct device, (the robot's fixed IP address), instead of directnic's servers where the domain is parked.
 
Configuring nginx:
The correct place to put your configuration file, (on the raspberry pi), is /etc/nginx/sites-available and create a symlink to that file in /etc/nginx/sites-enabled
It does not matter what you name it as nginx.conf blindly imports whatever is in that directory.  The other side of that is if there is anything already in that directory, you should remove it or rename it with a leading dot.
nginx -T is your friend!  You can use this to "test" your configuration for problems before you try to start it.
sudo systemctl restart nginx will attempt to restart nginx, (which as you begin configuration, will likely fail.)
sudo systemctl status nginx.service > ./[path]/log.txt 2>&1 is also your friend.  This allows you to collect error messages at runtime that will prevent the service from starting.  In my case, the majority of the problems were caused by other services using ports I had selected, or silly mis-configurations.
Once you have nginx started, and the status returns no problems, try sudo netstat -tulpn | grep nginx to make sure it's listening on the correct ports.
 
Troubleshooting nginx after you have it running:
Most browsers, (Firefox and Chrome at least) support a "developer mode" that you enter by pressing F-12.  The console messages can be very helpful.
 
SSL certificates:
Unlike other SSL servers, nginx requires the site certificate to be combined with the intermediate certificate bundle received from the certificate authority by using cat mycert.crt bundle.file > combined.crt to create it.
 
Ultimately I ended up with the following configuration file:
Note that I commented out the HTTP redirect as there was a service using port 80 on my device.  Under normal conditions, you will want to automatically re-direct port 80 to the secure connection.
Also note that I did not use hard-coded IP addresses in the config file.  This allows you to reconfigure the target IP address if necessary.
A corollary to that is - if you're redirecting to an internal secure device configured with the same certificates, you have to pass it through as the domain instead of the IP address, otherwise the secure connection will fail.
 
#server {
# listen example.com:80;
# server_name example.com;
# return 301 https://example.com$request_uri;
# }
# This is the "web" server (command and control), running Flask/Werkzeug
# that must be passed through as a secure connection so that the
# joystick/gamepad works.
#
# Note that the internal Flask server must be configured to use a
# secure connection too. (Actually, that may not be true, but that's
# how I set it up. . .)
#
server {
listen example.com:443 ssl;
server_name example.com;
ssl_certificate /usr/local/share/ca-certificates/extra/combined.crt;
ssl_certificate_key /usr/local/share/ca-certificates/extra/example.com.key;
ssl_prefer_server_ciphers on;
location / {
proxy_pass https://example.com:5000;
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;
}
}
# This is the video streaming port/server running streamserver
# which is not, and cannot be, secured. However, since most
# modern browsers will not mix insecure and secure content on
# the same page, the outward facing connection must be secure.
#
server {
listen example.com:5001 ssl;
server_name example.com;
ssl_certificate /usr/local/share/ca-certificates/extra/combined.crt;
ssl_certificate_key /usr/local/share/ca-certificates/extra/www.example.com.key;
ssl_prefer_server_ciphers on;
# After securing the outward facing connection, pass it through
# as an insecure connection so streamserver doesn't barf.
location / {
proxy_pass http://example.com:5002;
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;
}
}
Hopefully this will help the next person who encounters this problem.

NGINX HTTPS - SSL Certificates

I have three files regarding certificates:
- example.com.ca-bundle # contains root and intermediate certificates
- example.com.crt # Certificate
- example.com.p7b # only contains certificates and chain certificates
I need to configure NGINX to accept HTTPS, but I feel a bit confused as I do not have the most experience with SSL, and most examples seem to use a certificate/key pair. Would this NGINX configuration be the correct approach?
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /path/to/example.com.crt;
ssl_certificate_key /path/to/example.com.crt;
...
}
Like others pointed out in the comments you need to use the private key for ssl_certificate_key. If it helps further, below is an excerpt from an Nginx configuration using Let's Encrypt SSL certificates. In some cases cert.pem is not enough and chain.pem is also needed for compatibility across more browsers and server types.
# Full chain fullchain.pem is just a concatenation of public key cert.pem and chain.pem. If you are using certbot this will be automatically created.
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
# Like others pointed out in the comments there should be a private key and not the cert file
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
You can use certbot (a tool by Let's Encrypt) to automate the generation of your certificates. Here is a link with Step by Step instructions for automating HTTPS / SSL Certificate setup for your Nginx Hosted Website

Nginx redirecting too many times with reverse proxy

I have a debian server with MySQL and Meilisearch, I want to use Nginx as a reverse proxy for future load balancing and also having TLS security.
I'm following Meilisearch's Documentation to set up Nginx with Meilisearch and Lets Encrypt with success, but they force Nginx to proxy everything to port 7700, I want to proxy to 3306 for MySQL, 7700 for Meilisearch, and 80 for an error page or a fallback web server. But after modifying /etc/nginx/nginx.conf, the website reloads too many times.
This is the configuration I'm trying at /etc/nginx/nginx.conf:
user www-data;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
stream {
upstream mysql {
server 127.0.0.1:3306;
}
upstream meilisearch {
server 127.0.0.1:7700;
}
server {
listen 6666;
proxy_pass mysql;
}
server {
listen 9999;
proxy_pass meilisearch;
}
}
http {
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name example.com;
return 301 https://\$server_name$request_uri;
}
server {
server_name example.com;
location / {
proxy_pass http://127.0.0.1:80;
}
listen [::]:443 ssl ipv6only=on;
listen 443 ssl;
# ssl_certificate managed by Certbot
# ssl_certificate_key managed by Certbot
}
}
The only difference is example.com is replaced by my domain, which has been already set to redirect to the server ip.
As ippi pointed out, it isn't necessary to reverse proxy MySQL in this particular case.
I used proxy_pass http://127.0.0.1:7700 as Meilisearch docs suggest.
For future DB load balancing, I'd use MySQL Clusters, and point out to them on another Nginx implementation that proxies everything (ie. HTTPS to web server, DB access to list of clusters, etc.).
Also, in this particular case, I don't actually require encrypted connections to the DB, but if I needed them, I'd use a self signed certificate in MySQL, and a CA certificate for the website, since my front ends communicate with a "central" Nginx proxy that could encrypt communication between backend servers and the proxy.
If I actually wanted to use the Let's Encrypt certificates for MySQL, I've found a good
recipe, but instead of copy pasting the certificate (which is painful) I'd mount the Let's Encrypt certificates' directory into MySQL's and change the permissions to 600 with chown. But then again, this is probably bad practice and would be better to use self signed certificates for MySQL as it's own docs suggest.

Nginx Gunicorn socket issue? Unresponsive

I'm trying to deploy a Django project to a AWS Lightsail server.
I followed mostly this tutorial. I added some SSL protocols for additional security.
This projects runs perfectly on my Ubuntu 18.04 VirtualBox with exact same setup and exact same components, same SSL protocols. However on the Lightsail it doesn't respond to the browser request. It will redirect me to https but then will die... I wasn't able to identify any errors in any of the logs. Which leaves me guessing
/etc/systemd/system/webrock.socket:
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/webrock.sock
[Install]
WantedBy=sockets.target
/etc/systemd/system/webrock.service:
[Unit]
Description=gunicorn daemon
Requires=webrock.socket
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/django/webrock
ExecStart=/home/ubuntu/django/webrock/venv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/webrock.sock \
core.wsgi:application
[Install]
WantedBy=multi-user.target
/etc/nginx/sites-available/webrock:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2 ipv6only=on;
include snippets/signed.conf; # path to certs
include snippets/params.conf; # cert related params
index index.html index.htm index.nginx-debian.html;
server_name mydomain.com www.mydomain.com; #changed this line by replacing domain name with dummy
location = /favicon.ico {access_log off; log_not_found off;}
location /static/ {
root /home/ubuntu/django/webrock;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/webrock.sock;
try_files $uri $uri/ =404;
}
}
server {
listen 80;
listen [::]:80;
server_name mydomain.com www.mydomain.com; #changed this line by replacing domain name with dummy
return 302 https://$server_name$request_uri;
}
I left the nginx default file alone. Now every time I visit the page by punching in the server IP, I see the nginx default page. When I use the domain name I get redirected to HTTPS, but then... nothing. I assume that there is some disruption between gunicorn and nginx, but I'm not experienced enough to troubleshoot there or solve to solve it.
As I mentioned above, exact the same setup runs flawless on the similar system in my VirtualBox.
I'm very thankful for suggestions and hints.
Update:
I disabled the redirect portion in nginx and made it listen to port 80. It worked. Now I'm trying to figure out how to introduce HTTP2 and port 443 back to the setup. BTW my ufw looks like this:
After two days banging my head against this issue here is the solution.
So Amazon Lightsail has an additional firewall in front of the UFW on the actual server.
You can access Lightsail firewall by clicking on...
Menue of your instance > Manage > Networking
You will see a summarized networking for your instance like IP addresses, Firewall, Loadbalancer. In that firewall you need to add an additional port (In my case HTTPS).
Why would they put an additional firewall in front of UFW beats me.

NGINX cannot re-start when configured with SSL certificate and external SSL server that went down

I have an NGINX configuration with both HTTP and HTTPS traffic server blocks. Below is the HTTPS server block configuration snippet that is causing the problem.
server {
listen 10.1.1.5:443 default ssl;
listen 10.1.1.6:8080;
server_name myservice.traffic.dns.tmp;
ssl_certificate /etc/config/ssl/myservice.traffic.cert.pem;
ssl_certificate_key engine:name:myservice.traffic;
}
The external SSL server named "engine:name" is used to get the SSL certificate key. Initially, NGINX starts successfully. When the external SSL server goes down, if I try to restart NGINX, the restart fails because NGINX cannot connect with the external SSL server.
nginx: [emerg] ENGINE_load_private_key("385.1") failed
ENGINE_load_private_key:failed loading private key
This creates a "Hard" dependency on an external service.
How could I continue to serve my HTTP traffic even when the SSL external server is down? I would like to make nginx restart to succeed even when the external SSL server is down.
Thanks

Resources