Sirviendo contenido estático protegido usando nginx para velocidad y servidor Rails para autenticación

Problema

Un usuario necesita acceder a un contenido estático protegido (por ejemplo, una imagen) que debe ser servido en https por un servidor web (para una velocidad de descarga mucho más rápida) solo después de que la solicitud haya sido autenticada y autorizada con éxito por un servidor Rails.

La puesta en marcha

Asumamos lo siguiente:

  • Nginx : nginx se ejecuta como un servidor proxy inverso y web. Todas las solicitudes al servidor Rails se pasan al puerto 3000 (es decir, el puerto predeterminado para el servidor Rails)
  • Delgado : servidor de rieles que se ejecuta en el puerto 3000
  • Directorio de rieles : el directorio raíz del proyecto de rieles se encuentra en/var/rails/rails_app
  • Directorio donde se llama a los recursos estáticos protegidos :/var/rails/rails_app/images_fs_dir
  • URL para acceder a los recursos estáticos protegidos :http://myhost.com/images_uri_dir

Solución

¿Por qué utilizar nginx para ofrecer contenido estático?

Porque nginx es 5-20 veces más rápido que delgado cuando se trata de ofrecer contenido estático.

Flujo lógico:

  • La solicitud se hace a http://myhost.com/images_uri_dir/companyA/bar_chart.png
  • La solicitud se redirige a https://myhost.com/images_uri_dir/companyA/bar_chart.png(ver reescribir ^ https: // $ host $ request_uri permanente)
  • Nginx recibe la solicitud, hace coincidir la ruta de la solicitud con sus reglas de mapeo en nginx.conf, agrega los encabezados HTTP X-Accel-Redirect y X-Accel-Mapping, y envía la solicitud a http://localhost:3000/images_uri_dir/companyA/bar_chart.png(ver proxy_pass en nginx.conf)
  • Thin recibe la solicitud, busca routes.rby envía la solicitud aImagesController#show
  • El método before_filter en el controlador realiza la autenticación y autorización adecuadas
  • config / environment / production.rb le dice al servidor de rails que busque X-Accel-Redirect y X-Accel-Mapping en una solicitud
  • Rails compara la ruta del archivo que se pasa para enviar el archivo con lo que se define en el lado izquierdo del valor X-Accel-Mapping (es decir, / var / rails / rails app / = / images fs dir /). Si los 2 valores coinciden, el controlador devuelve la solicitud a nginx. De lo contrario, el controlador enviará el archivo al cliente.
  • Nginx recibe la solicitud y mira el lado derecho del valor de X-Accel-Mapping (es decir, / var / rails / rails app / = / images fs_dir /). Dado que la solicitud es interna al host, nginx carga y envía el archivo de vuelta al cliente (ver interno en nginx.conf)

El directorio raíz del proyecto Rails:

$ cd /var/rails/rails_app
$ ls

images_fs_dir

$ ls
-R # List files recursively
.:
customerA customerB


./customerA
bar_chart
.png
pie_chart
.png

./customerB
bar_chart
.png
pie_chart
.png
...

El archivo nginx debería verse así:

events {
worker_connections
1024;
}

http
{
include
/etc/nginx/mime.types;
index index
.html index.htm;

default_type application
/octet-stream;

upstream rails
{
server localhost
:3000;
}

server
{
listen
80;
server_name localhost
;

# Direct all http requests to https.
location
/ {
rewrite
^ https://$host$request_uri permanent;
}
}

server
{
listen
443 default_server ssl;
server_name localhost
;
root
/var/rails/rails_app;

ssl on
;
ssl_certificate
/etc/ssl/certs/my_cert.crt;
ssl_certificate_key
/etc/ssl/keys/my_key.key;

# Protected directory
# Note: rails will handle /images_uri_dir requests
location
/images_fs_dir/ {
alias /var/rails/rails_app/; # Append the path with /
internal; # Can't access this directory from direct access from the web
}

location
/ {
proxy_redirect off
;

proxy_set_header
Host $http_host;
proxy_set_header X
-Real-IP $remote_addr;
proxy_set_header X
-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header X
-Sendfile-Type X-Accel-Redirect;
proxy_set_header X
-Accel-Mapping /var/rails/rails_app/=/images_fs_dir/;

# Make sure you open and close your X-Accel-Mapping value with /

proxy_pass http
://rails;
}
}
}

config / route.rb debería verse así:

# ...

get
'/images_uri_dir/:customer_id/:category.png' => 'images#show'

#...

config / environment / production.rb debería verse así:

# ...

# This redirects the request back to nginx to load and serve the static content after a success authentication/authorization

RailsApp::Application.configure do
config
.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
config
.middleware.insert(0, Rack::Sendfile, config.action_dispatch.x_sendfile_header)

# ...

app / controllers / images_controller.rb

# ...

before_filter
:validate_user

def validate_user
# Authentication and authorization logic here
end

def show
file_path
= "#{Rails.root}/images_fs_dir/#{customer_id}/#{category}.png"

send_file
(file_path, type: 'image/png', disposition: 'inline')
end

def images_params
params.permit(:customer_id, :category)
end

# ...