2012-02-25 19 views
52

Quiero tener mi controlador de API utilización de SSL, por lo que añade otra directiva Listen a mi nginx.conf¿Por qué recibo infinitos bucles de redirección con force_ssl en mi aplicación Rails?

upstream unicorn { 
    server unix:/tmp/unicorn.foo.sock fail_timeout=0; 
} 

server { 
    listen 80 default deferred; 
    listen 443 ssl default; 
    ssl_certificate /etc/ssl/certs/foo.crt; 
    ssl_certificate_key /etc/ssl/private/foo.key; 

    server_name foo; 
    root /var/apps/foo/current/public; 

    try_files $uri/system/maintenance.html $uri/index.html $uri @unicorn; 

    location @unicorn { 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    proxy_set_header Host $http_host; 
    proxy_redirect off; 
    proxy_pass http://unicorn; 
    } 

    error_page 502 503 /maintenance.html; 
    error_page 500 504 /500.html; 
    keepalive_timeout 5; 
} 

cual pasa el conftest nginx sin ningún problema. También he añadido una directiva force_ssl a mi ApiController

class ApiController < ApplicationController 
    force_ssl if Rails.env.production? 

    def auth 
    user = User.authenticate(params[:username], params[:password]) 
    respond_to do |format| 
     format.json do 
     if user 
      user.generate_api_key! unless user.api_key.present? 
      render json: { key: user.api_key } 
     else 
      render json: { error: 401 }, status: 401 
     end 
     end 
    end 
    end 

    def check 
    user = User.find_by_api_key(params[:api_key]) 
    respond_to do |format| 
     format.json do 
     if user 
      render json: { status: 'ok' } 
     else 
      render json: { status: 'failure' }, status: 401 
     end 
     end 
    end 
    end 
end 

el que funcionaba bien cuando no estaba usando SSL, pero ahora cuando intento curl --LI http://foo/api/auth.json, consigo adecuadamente redirigido a https, pero luego sigo siendo redirigida a http://foo/api/auth terminando en un bucle de redirección infinito.

Mis rutas simplemente tienen

get "api/auth" 
get "api/check" 

estoy usando Rails 3.2.1 sobre el Ruby 1.9.2 con Nginx 0.7.65

Respuesta

106

No reenviará ninguna información acerca de si esta solicitud fue o no una solicitud finalizada por HTTPS. Normalmente, en un servidor, el "ssl en"; directiva establecerá estos encabezados, pero está utilizando un bloque combinado.

rack (y force_ssl) determina SSL por:

  • Si la solicitud entró en el puerto 443 (esto no es probable que se pasa de nuevo a Unicorn de nginx)
  • Si ENV [ 'HTTPS'] == "en"
  • Si la cabecera X-reenviado-Proto == "HTTPS"

Ver the force_ssl source para la historia completa.

Dado que está utilizando un bloque combinado, desea utilizar el tercer formulario. Proveedores:

proxy_set_header X-Forwarded-Proto $scheme; 

en su servidor o ubicación del bloque per the nginx documentation.

Esto configurará el encabezado en "http" cuando ingrese en una solicitud del puerto 80, y lo establecerá en "https" cuando entre en una solicitud 443.

+1

Magia pura. :) En mi caso, estaba trasladando un sitio de Rails al uso de Varnish. El sitio era puro HTTPS, y como Varnish no es compatible con SSL y Passenger expone un socket en lugar de un puerto, se necesitaron dos configuraciones de servidor Nginx, una a cada lado de Varnish. El distanciamiento de la conexión del zócalo del pasajero de la configuración de HTTPS en el puerto 443 provocó un bucle de redirección. Y esto lo solucionó. ¡Gracias! –

16

intente configurar esta directiva en su nginx location @unicorn bloque:

proxy_set_header X-Forwarded-Proto https;

Tuve este mismo problema e investigué el controlador de middleware Rack (no force_ssl pero sí lar) Pude ver que esperaba que se estableciera el encabezado para determinar si la solicitud ya había sido procesada como SSL por nginx.

+4

Esto marcará todas las solicitudes como solicitudes SSL, incluso las solicitudes del puerto 80, ya que es un bloque de servidor combinado. –

+0

@ChrisHeald He enviado una edición para corregir eso. –

Cuestiones relacionadas