2012-02-25 3 views
52

Voglio avere la mia API uso del controller SSL, così ho aggiunto un altro ascolto direttiva alla mia nginx.confPerché sto ottenendo un ciclo di reindirizzamento infinito con force_ssl nella mia app 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; 
} 

che passa il conftest nginx senza problemi. Ho anche aggiunto una direttiva force_ssl alla mia 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 

che ha funzionato bene quando non stavo usando SSL, ma ora quando provo a curl --LI http://foo/api/auth.json, vengo correttamente reindirizzato a https, ma poi io continuo a ottenere reindirizzato a http://foo/api/auth termina con un ciclo di reindirizzamento infinito.

miei percorsi hanno semplicemente

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

sto usando Rails 3.2.1 su Ruby 1.9.2 con nginx 0.7.65

risposta

106

Non stai inoltrando alcuna informazione sul fatto che questa richiesta fosse o meno una richiesta terminata da HTTPS. Normalmente, in un server, "ssl on;" direttiva imposta queste intestazioni, ma stai utilizzando un blocco combinato.

Rack (e force_ssl) determina SSL da:

  • Se la richiesta è venuto in sulla porta 443 (questo non è probabile essere passato al Unicorn da nginx)
  • Se ENV [ 'HTTPS'] == "on"
  • Se l'X-Forwarded-Proto intestazione == "HTTPS"

Vedere the force_ssl source per la storia completa.

Poiché si utilizza un blocco combinato, si desidera utilizzare il terzo modulo. Prova:

proxy_set_header X-Forwarded-Proto $scheme; 

nel vostro server o posizione di blocco per the nginx documentation.

Questo imposterà l'intestazione su "http" quando entri in una richiesta di porta 80 e la imposti su "https" quando entri in una richiesta 443.

+1

Pura magia. :) Nel mio caso stavo spostando un sito di Rails per usare Varnish. Il sito era puro HTTPS, e poiché Varnish non supporta SSL e Passenger espone un socket piuttosto che una porta, erano necessarie due configurazioni del server Nginx, una su entrambi i lati di Varnish. La distanza dalla connessione del socket Passenger dalla configurazione HTTPS sulla porta 443 ha causato un loop di reindirizzamento. E questo l'ha risolto. Grazie! –

16

provare ad impostare questa direttiva nel nginx location @unicorn blocco:

proxy_set_header X-Forwarded-Proto https;

Ho riscontrato lo stesso problema e ho analizzato il gestore del middleware Rack (non force_ssl ma simi lar) Ho potuto vedere che si aspettava che quell'intestazione fosse impostata per determinare se la richiesta era già stata elaborata come SSL da nginx.

+4

Questo contrassegna tutte le richieste come richieste SSL, anche le richieste di porta 80, poiché si tratta di un blocco server combinato. –

+0

@ChrisHeald Ho inviato una modifica per correggerlo. –