How to configure SSL termination on Nginx with a Rails application - ruby-on-rails

I get my SSL request through a load balancer which is performing SSL termination and sending the decrypted traffic to my server on port 81.
I'm running Nginx and have setup a listener to the port 81 and I would like to tell my Rails app that this is a SSL request.
Here is how my Nginx config looks like:
server {
listen 81;
server_name SERVER;
root /var/www/app/current/public;
error_log /var/log/nginx/app-error.log;
access_log /var/log/nginx/app-access.log;
passenger_enabled on;
client_max_body_size 100M;
location / {
passenger_enabled on;
proxy_set_header X-FORWARDED-PROTO https;
}
}
It goes through but Ruby on Rails doesn't detect the header request.headers['X-Forwarded-Proto'], so it takes the request as non-https.
What should I add in order to get Rails thinking this is a SSL request?

So proxy_set_header is not used by passenger, you need to use passenger_set_cgi_param instead:
passenger_set_cgi_param HTTP_X_FORWARDED_PROTO https;

The nginx directive passenger_set_cgi_param has been replaced by passenger_set_env.
https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html#PassengerEnvVar
for newer nginx the directive should be
passenger_set_cgi_param HTTP_X_FORWARDED_PROTO https;

Related

ERR_TOO_MANY_REDIRECTS Rails + Puma + Nginx (Dockerized)

I'm running a dockerized rails app with puma and nginx, however, I'm getting ERR_TOO_MANY_REDIRECTS when trying to access the application from a browser.
I have config.force_ssl = true on my application.rb and this is my nginx conf file:
upstream kisoul {
server rails:3000;
}
server {
listen 80;
listen 443 ssl;
root /usr/share/nginx/kisoul;
try_files $uri #kisoul;
location #kisoul {
proxy_pass_request_headers on;
proxy_ignore_headers Expires Cache-Control;
proxy_set_header Host $http_host;
proxy_pass_header Set-Cookie;
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://kisoul;
}
ssl_certificate /etc/nginx/fullchain.pem;
ssl_certificate_key /etc/nginx/privkey.pem;
}
I have already tried disabling force_ssl from rails and forcing the redirect through nginx, but then I get a problem with Origin header, saying that the origin header (https://localhost) didn't match request.base_url (HTTP://localhost)
I tried many different solutions already described here, but I couldn't find any solution

How to set the right Nginx reverse proxy configuration in docker

I deployed Nginx reverse proxy in docker, and it belong to the bridge network which using 172.16.10.0/24. And I have the other web app in docker which in different bridge network 172.16.20.0/24. In order to let Niginx reverse proxy to connect web app, I have set Nginx reverse proxy to join the 172.16.20.0/24 as well.
My web app is hosting in http://localhost:8899, and I have bind host:8899 --> container:80. What I want to try is: when someone visit https://mydomain, and reverse proxy should pass to http://localhost:8899.
My nginx config is as follow:
server {
listen 80;
listen [::]:80;
server_name mydomain;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name mydomain;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
ssl_certificate /ssl/my_domain_cert.pem;
ssl_certificate_key /ssl/my_domain.key;
location / {
proxy_set_header Host $host;
proxy_set_header Cookie $http_cookie;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_pass http://localhost:8899;
proxy_read_timeout 90;
}
}
However, when i connect to https://mydomain, the error is SSL handshake failed (Error code 525). How should I fix the problem?
The 525 HTTP error means, there is no valid SSL certificate installed.
The nginx conf is searching for the SSL certificate files in these locations:
ssl_certificate /ssl/my_domain_cert.pem;
ssl_certificate_key /ssl/my_domain.key;
Unless you created a SSL certificate in your Dockerfile or created one before and put them in these locations, you have to MANUALLY create a SSL certificate.
How to create a key and pem file:
https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-on-centos-7
How to get .pem file from .key and .crt files?

Make rails url_helpers use https in production

I use Rails url_helpers to build resources urls in my app.
An example of a url in development is http://localhost:3000/resource/1, generated by the resource_url(resource) url_helper.
I use nginx in production to proxy_pass the requests to my rails app. My nginx listens on port 433 https and redirects to my rails app on port 5000 http:
server {
listen 443 ssl;
server_name api.staging.zup.me;
ssl_certificate /etc/ssl/certs/cert.crt;
ssl_certificate_key /etc/ssl/certs/cert.key;
location / {
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:5000;
}
}
Because the request that my Rails app receives from the nginx proxy is http, it is generating the urls like, http://example.com/resource/1, but only in production I want all my urls to use https, for example, https://example.com/resource/1.
What is the best way to make Rails generate urls with the https protocol only in production?
You can use redirect in nginx and never touch rails app to enable https:
server {
listen 80 default_server deferred;
server_name myserver.domain;
return 301 https://$server_name$request_uri; # enforce https
}
# HTTPS server
server {
listen 443;
server_name myserver.domain;
ssl on;
ssl_certificate /etc/nginx/ssl/myserver.domain.crt;
ssl_certificate_key /etc/nginx/ssl/myserver.domain.key;
root /srv/www/myserver;
...
}
Not sure if it's a best way, but I like it becuase you don't have to do anything with rails app, only nginx.

nginx + passenger + rails: do I need to start the rails server or just start nginx?

I'm trying to have my rails server listen on 2 different ports. One solution proposed to me was to use nginx. I installed nginx with sudo passenger-install-nginx-module and added the following to /etc/nginx/conf.d:
server {
listen 80;
listen 10000;
server_name www.myapp.com
passenger_enabled on;
root /root/myapp/public;}
When I went to www.myapp.com I got a 403 Forbidden error. I figured it was because there were no static html files in /public. I dropped a simple "hello world" html page in there and it loaded correctly. I then proceeded to start my rails app using passenger start -e production, which caused it to run in standalone phusion passenger mode on port 3000. I go to myapp.com:3000 and I get the app. However, myapp:80 and myapp:10000 still don't work. I'm confused on how to get my nginx to point to the rails server I'm running. Am I doing this completely wrong? Thanks!
Set nginx to forward to my rails server using this https://gist.github.com/jeffrafter/1229497
worker_processes 1;
error_log /usr/local/var/log/nginx.error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream dev {
server 127.0.0.1:3000;
}
server {
listen 80;
# You could put a server_name directive here (or multiple) if
# you have not setup wildcard DNS for *.dev domains
# See http://jessedearing.com/nodes/9-setting-up-wildcard-subdomains-on-os-x-10-6
# If we choose a root, then we can't switch things around easily
# Using /dev/null means that static assets are served through
# Rails instead, which for development is okay
root /dev/null;
index index.html index.htm;
try_files $uri/index.html $uri.html $uri #dev;
location #dev {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://dev;
}
error_page 500 502 503 504 /50x.html;
}
}

Getting a HTTPS connection to work with a local Rails app and nginx?

I have a Ruby on Rails app that I'm running locally. I then run a nginx server locally, too. Now, the connection works fine over HTTP, but when I try to connect over HTTPS, I get the "502 Bad Gateway" error from nginx. I'm pretty sure the certs are working correctly, because when they weren't, I had a different error, but now something is wrong with my nginx config? Here's the config:
http {
access_log off;
#include mime.types;
default_type application/octet-stream;
upstream mydomain.com {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name local www.local;
location / {
proxy_pass http://mydomain.com;
}
add_header Pragma "no-cache";
}
server {
listen 443;
server_name local www.local;
ssl on;
ssl_certificate /Users/daise/projects/mydomain/run/ssl/server.crt;
ssl_certificate_key /Users/daise/projects/mydomain/run/ssl/server.key;
location / {
proxy_set_header X-FORWARDED_PROTO https;
#proxy_pass https://mydomain.herokuapp.com;
proxy_pass https://mydomain.com;
}
add_header Pragma "no-cache";
}
}
I should also add that I spoof my /etc/hosts file to resolve to 127.0.0.1
Change
proxy_pass https://mydomain.com;
to
proxy_pass http://mydomain.com;
Your ssl is terminated in nginx. So there is no need to use https to connect to the upstream server.
[More]
Also notice a typo in your config file:
proxy_set_header X-FORWARDED_PROTO https
to
proxy_set_header X-FORWARDED-PROTO https;

Resources