setting Rails environment hash variables for Rack middleware - ruby-on-rails

I have a Rails app being served on standalone Passenger behind an nginx proxy. Because of this setup, the environment hash needs a bit of tweaking to work with a piece of rack middleware we're using (rack-cas). Specifically I have to set env['SERVER_PORT'] = '443' and env['HTTPS'] = 'on' in the middleware's call method (we don't want Passenger using SSL, would rather the nginx proxy handle it).
I can do all that easy enough in the middleware but can I do it in the Rails app so I don't have to customize the middleware?

Turns out it can all be done from the nginx proxy's config, don't have to touch the app or middleware at all:
location /foo/ {
proxy_pass http://0.0.0.0:SOME_PORT/foo/;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host; # more robust than http_host
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # this ensures your app's env is correct
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https; # add this if you always want the redirects to go to HTTPS
}

Related

How to correctly redirect a request with $scheme via proxy_pass using nginx?

I have setup two nginx instances as follows:
nginx (https) → docker:[nginx (http) → uwsgi]
The front facing nginx process exposes the https service, which passes down all requests via proxy_pass to the docker nginx process. It also redirects all requests to http → https.
The problem is that the docker nginx process has the following line in a location block in its default server instance:
server {
...
location = / {
return 301 $scheme://$http_host${request_uri}login/;
}
}
With the intention of redirecting / to the login page. This works fine except that the redirection always points to an http://... url. E.g. A request to http://myserver.com/, gets redirected to https://myserver.com/, then it gets passed down to the docker nginx which returns a 301 with the following url: http://myserver.com/login/. I want it to be https://myserver.com/login/ or whatever schema the front-facing server may offer.
This is how I setup the front-facing nginx process:
server {
listen 443 ssl http2 default_server;
...
location / {
proxy_pass http://localhost:81;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_cache_bypass $http_upgrade;
proxy_redirect https:// http://;
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-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Scheme $scheme;
}
}
server {
listen 80 default_server;
...
location = / {
return 301 https://$http_host${request_uri};
}
}
Is this kind of redirection even possible?
Also, in case you wonder, I also tried all possible combinations of X-Forwarded-Proto, X-Scheme and proxy_redirect as other answers suggest, namely:
Nginx does redirect, not proxy
how to handle nginx reverse proxy https to http scheme redirect
One trick that I've found is that you can disable absolute redirects (e.g. instead of redirecting to http://localhost/your_url_with_trailing_slash_now it will redirect to /your_url_with_trailing_slash_now).
add the following to any point within the server block (of the nginx instance that does this 301 redirect, in your case the nginx-docker instance):
server {
absolute_redirect off;
...
More info can be found here: https://serverfault.com/questions/227742/prevent-port-change-on-redirect-in-nginx

How to set the ~/ path in Asp.net core

This answer: https://stackoverflow.com/a/10469032/3958875 indicates that the ~/ in urls will be set to the actual root of the application if the application is in a virtual directory.
However, I can't seem to find how I can set this path/value.
For example, I have the app behind nginx reverse proxy, so that the root of the app is here: www.mywebsite.com/app1/
Therefore I want all ~/ to be expanded to app1/. How can I accomplish this?
I tried app.UsePathBase("/app1"); in the Configure method in Startup.cs, which didn't seem to do anything.
My nginx config is like:
server {
server_name: apps.mywebsite.com
location / {
...
}
location /app1/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:5000/;
}
#Https stuff ...
}
Am I missing something that I can pass to asp.net from nginx?
~ will expand to the content root. That is the effectively the path base that is configured for an incoming request.
What app.UsePathBase() does is tell the application that when the incoming request starts with the specified prefix, then that will be used as the path base. So in your case, when the incoming request starts with /app1, then /app1 will be the path base, and ~ will be expanded to /app1.
If you look at your reverse proxy configuration, you can see however that the path /app1 is not actually passed to the application:
location /app1/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:5000/;
}
So when you access the page at /app1/foo, the request path that gets passed to the application will be just /foo. So the /app1 path base isn’t seen by the application and it won’t be able to respond appropriately.
What you need to do instead is actually pass the full path to the application. You then use app.UsePathBase() to configure that path base so that it gets interpreted correctly:
location /app1/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:5000/app1/;
}
app.UsePathBase("/app1");
Now, incoming requests at /app1/foo will translate to the same path within your application, the /app1 path base will be used and ~ should expand properly to /app1.
Turns out the error is indeed in my nginx configuration.
By following the documentation on asp and nginx here: https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-2.2
I changed my nginx config to:
...
location /app1/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
...
And ~ expanded properly.

How to configure ngnix for rails port?

Hello this is my first time deploying rails app to a ubuntu server so after I configured nginx and got the "welcome to nginx page" at a certain IP ... and when I start rails application I must enter the port in the IP address for example 165.217.84.11:3000 in order to access rails so how to make rails run default when I run only this IP 165.217.84.11
You can set the redirection from the 80 port (wich is the default) to the 3000 like this:
worker_processes 1;
events { worker_connections 1024; }
http {
client_max_body_size 10m;
sendfile on;
upstream rails {
server 165.217.84.11:3000;
}
server {
listen 80;
location / {
proxy_pass http://rails-app;
proxy_redirect off;
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 X-Forwarded-Ssl off;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Host $server_name;
}
}
}
So, when you access 165.217.84.11 in the browser you should see your rails project.
In general you have to setup your nginx to use puma socket file and then it will access the website using socket file instead of using TCP port (:3000 by the default).
Here is a nice tutorial: link
And here is a short explanation why you should use sockets.

Why I can't access to subdomain through nginx proxy pass?

I wanna to deploy my Ruby on Rails application in my local computer by Nginx and RoR web servers (like Unicorn, Thin or WEBrick).
As shown below, I wanna access to my web-app by post subdomain:
upstream sub {
server unix:/tmp/unicorn.subdomain.sock fail_timeout=0;
# server 127.0.0.1:3000;
}
server {
listen 80;
server_name post.subdomain.me;
access_log /var/www/subdomain/log/access.log;
error_log /var/www/subdomain/log/error.log;
root /var/www/subdomain;
index index.html;
location / {
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;
try_files /system/maintenance.html $uri $uri/index.html $uri.html #ruby;
}
location #ruby {
proxy_pass http://sub;
}
}
Everything is working fine and when I type post.subdomain.me I can see my RoR app.
Problem: When I use post.subdomain.me url I can't access to my subdomain (request.subdomain returns empty and request.host returns subdomain instaed of subdomain.me). But when I use post.subdomain.me:3000 every things work perfect (I lost half of my hairs to realize that). Why and How can I resolve it?
When you access app with port - you are accessing the rails server directly, not proxied by nginx, this is fine for debug, but usually is not well for production.
Probably host header is not passed over by client, $host defaults to nginx host
Try
location #ruby {
proxy_set_header Host $host;
proxy_pass http://sub;
}
And a 'hardcode'-way: proxy_set_header Host post.subdomain.me;
The proxy_set_header and proxy_redirect directives configure the proxy_pass directive and need to be within the same location block or inherited from the enclosing server block. You need to format your configuration file like this:
location / {
try_files /system/maintenance.html $uri $uri/index.html $uri.html #ruby;
}
location #ruby {
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://sub;
}
EDIT: Assuming that nginx is not passing the information to RoR correctly, as #Vasfed suggested, try other values for proxy_set_header Host. There are three other candidates, all with slightly different meanings.
proxy_set_header Host post.subdomain.me;
proxy_set_header Host $server_name;
proxy_set_header Host $host;

How to start faye server on a rails app deployed using dokku?

I've hosted my rails application on Digitalocean using Dokku. There's this need for my application to run real-time applications through Faye. I've been trying several ways like the shoreman plugin for Dokku and adding faye: bundle exec rackup faye.ru -s thin -E production to "Procfile" file. But no luck till now, need help on how I can get this Faye server running for my app.
You need to make several steps to have working faye server (e.g. on port 9292):
Your Procfile is OK
Expose port 9292 on Docker. I recommend install docker-options plugin and next dokku docker-options:add timer "-p 9292:9292"
Setup your app nginx.conf. Mine is here:
upstream app { server 127.0.0.1:49154; }
server {
listen [::]:80;
listen 80;
server_name app.dokku.mine;
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Request-Start $msec;
}
location /faye {
proxy_redirect off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache_bypass $http_pragma $http_authorization;
proxy_no_cache $http_pragma $http_authorization;
proxy_pass http://localhost:9292;
}
}
I suggest to install nginx-alt plugin because config is overwritten on every deploy.

Resources