2014-11-18 26 views
7

Mam aplikację Rails, która działa w kontenerze Docker, któremu przypisano adres IP 172.17.0.3. Żądania przychodzące na maszynę hosta 51.x.x.x są przesyłane do aplikacji szyny pod numerem 172.17.0.3. Dokładniej, to było zrobione jako takie:Aplikacja Port-forwarded Rails w Dockerze wydaje się powodować wyjątek CSRF

docker run -p 8080:8080 rails_app 

Jednak Rails app rzuca Can't verify CSRF token authenticity błąd, gdy użytkownik próbuje uzyskać dostęp do niektórych stron. Podejrzewam, że Rails uważa, że ​​przychodzące żądanie jest atakiem, ponieważ adres IP miejsca docelowego nie pasuje do IP aplikacji Rails - tj. Żądania użytkowników są kierowane do komputera hosta 51.x.x.x, natomiast rzeczywiste położenie Railsów to 172.17.0.3

Czy jest jakiś sposób, aby powiedzieć Railsom, że te wnioski są uzasadnione? Jako dodatkową informację używam devise do uwierzytelniania i unicorn jako serwera.

Niektórzy z was mogą ulec pokusie sugerowania zmiany protect_from_forgery with: :exception na :null_session, ale aplikacja działa dobrze, gdy nie jest umieszczona za serwerem proxy. Poza tym część logiki nie zadziała, gdy zmienię tę część, ponieważ myślę, że ustawienie jest sprzeczne ze sposobem obsługi sesji użytkownika.

Jest to układ z mojej sieci:

(user from public network) ----> (proxy) ----> (rails app on a private network) 
     (202.x.x.x)   (51.x.x.x)    (172.x.x.x) 

EDIT: Aplikacja jest w development ustawień. Oto błąd, który mam w plikach log/development.log.

Started POST "/register" for 202.x.x.x at 2014-11-18 02:27:11 +0000 
Processing by UsersController#create as HTML 
    Parameters: {"utf8"=>"✓", "authenticity_token"=>"aBG3nIAKK1ALMJ1DDYFlMkmqISMBMZc3iLmaeD2byG8=", "user"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}} 
Can't verify CSRF token authenticity 
Completed 422 Unprocessable Entity in 2ms 

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken): 
    actionpack (4.1.4) lib/action_controller/metal/request_forgery_protection.rb:176:in `handle_unverified_request' 
    actionpack (4.1.4) lib/action_controller/metal/request_forgery_protection.rb:202:in `handle_unverified_request' 
    devise (3.4.0) lib/devise/controllers/helpers.rb:251:in `handle_unverified_request' 
    actionpack (4.1.4) lib/action_controller/metal/request_forgery_protection.rb:197:in `verify_authenticity_token' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:424:in `block in make_lambda' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:160:in `call' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:160:in `block in halting' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:166:in `call' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:166:in `block in halting' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:149:in `call' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:149:in `block in halting_and_conditional' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:149:in `call' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:149:in `block in halting_and_conditional' 
activesupport (4.1.4) lib/active_support/callbacks.rb:86:in `run_callbacks' 
    actionpack (4.1.4) lib/abstract_controller/callbacks.rb:19:in `process_action' 
    actionpack (4.1.4) lib/action_controller/metal/rescue.rb:29:in `process_action' 
    actionpack (4.1.4) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action' 
    activesupport (4.1.4) lib/active_support/notifications.rb:159:in `block in instrument' 
    activesupport (4.1.4) lib/active_support/notifications/instrumenter.rb:20:in `instrument' 
    activesupport (4.1.4) lib/active_support/notifications.rb:159:in `instrument' 
    actionpack (4.1.4) lib/action_controller/metal/instrumentation.rb:30:in `process_action' 
    actionpack (4.1.4) lib/action_controller/metal/params_wrapper.rb:250:in `process_action' 
    activerecord (4.1.4) lib/active_record/railties/controller_runtime.rb:18:in `process_action' 
    actionpack (4.1.4) lib/abstract_controller/base.rb:136:in `process' 
    actionview (4.1.4) lib/action_view/rendering.rb:30:in `process' 
    actionpack (4.1.4) lib/action_controller/metal.rb:196:in `dispatch' 
    actionpack (4.1.4) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch' 
    actionpack (4.1.4) lib/action_controller/metal.rb:232:in `block in action' 
    actionpack (4.1.4) lib/action_dispatch/routing/route_set.rb:82:in `call' 
    actionpack (4.1.4) lib/action_dispatch/routing/route_set.rb:82:in `dispatch' 
    actionpack (4.1.4) lib/action_dispatch/routing/route_set.rb:50:in `call' 
    actionpack (4.1.4) lib/action_dispatch/routing/mapper.rb:45:in `call' 
    actionpack (4.1.4) lib/action_dispatch/journey/router.rb:71:in `block in call' 
    actionpack (4.1.4) lib/action_dispatch/journey/router.rb:59:in `each' 
    actionpack (4.1.4) lib/action_dispatch/journey/router.rb:59:in `call' 
    actionpack (4.1.4) lib/action_dispatch/routing/route_set.rb:678:in `call' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' 
    omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' 
    warden (1.2.3) lib/warden/manager.rb:35:in `block in call' 
    warden (1.2.3) lib/warden/manager.rb:34:in `catch' 
    warden (1.2.3) lib/warden/manager.rb:34:in `call' 
    rack (1.5.2) lib/rack/etag.rb:23:in `call' 
    rack (1.5.2) lib/rack/conditionalget.rb:35:in `call' 
    rack (1.5.2) lib/rack/head.rb:11:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/params_parser.rb:27:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/flash.rb:254:in `call' 
    rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context' 
    rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/cookies.rb:560:in `call' 
    activerecord (4.1.4) lib/active_record/query_cache.rb:36:in `call' 
    activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:621:in `call' 
    activerecord (4.1.4) lib/active_record/migration.rb:380:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call' 
    activesupport (4.1.4) lib/active_support/callbacks.rb:82:in `run_callbacks' 
    actionpack (4.1.4) lib/action_dispatch/middleware/callbacks.rb:27:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/reloader.rb:73:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/remote_ip.rb:76:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call' 
    railties (4.1.4) lib/rails/rack/logger.rb:38:in `call_app' 
    railties (4.1.4) lib/rails/rack/logger.rb:20:in `block in call' 
    activesupport (4.1.4) lib/active_support/tagged_logging.rb:68:in `block in tagged' 
    activesupport (4.1.4) lib/active_support/tagged_logging.rb:26:in `tagged' 
    activesupport (4.1.4) lib/active_support/tagged_logging.rb:68:in `tagged' 
    railties (4.1.4) lib/rails/rack/logger.rb:20:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/request_id.rb:21:in `call' 
    rack (1.5.2) lib/rack/methodoverride.rb:21:in `call' 
    rack (1.5.2) lib/rack/runtime.rb:17:in `call' 
    activesupport (4.1.4) lib/active_support/cache/strategy/local_cache_middleware.rb:26:in `call' 
    rack (1.5.2) lib/rack/lock.rb:17:in `call' 
    actionpack (4.1.4) lib/action_dispatch/middleware/static.rb:64:in `call' 
    rack-cors (0.2.9) lib/rack/cors.rb:54:in `call' 
    rack (1.5.2) lib/rack/sendfile.rb:112:in `call' 
    railties (4.1.4) lib/rails/engine.rb:514:in `call' 
    railties (4.1.4) lib/rails/application.rb:144:in `call' 
    rack (1.5.2) lib/rack/lint.rb:49:in `_call' 
    rack (1.5.2) lib/rack/lint.rb:37:in `call' 
    rack (1.5.2) lib/rack/showexceptions.rb:24:in `call' 
    rack (1.5.2) lib/rack/commonlogger.rb:33:in `call' 
    sinatra (1.4.5) lib/sinatra/base.rb:217:in `call' 
    rack (1.5.2) lib/rack/chunked.rb:43:in `call' 
    rack (1.5.2) lib/rack/content_length.rb:14:in `call' 
    unicorn (4.8.3) lib/unicorn/http_server.rb:576:in `process_client' 
    unicorn (4.8.3) lib/unicorn/http_server.rb:670:in `worker_loop' 
    unicorn (4.8.3) lib/unicorn/http_server.rb:525:in `spawn_missing_workers' 
    unicorn (4.8.3) lib/unicorn/http_server.rb:140:in `start' 
    unicorn (4.8.3) bin/unicorn:126:in `<top (required)>' 
+0

Czy kiedykolwiek tego dowiedzieć? Właśnie zbudowałem środowisko produkcyjne na maszynie wirtualnej Ubuntu za pomocą kontenerów dokera ... kolby, mysql, nginx i uwsgi. Otrzymuję również błędy tokenów CRSF na formularzach. – Chockomonkey

+0

Po prostu wymyśliłem coś podobnego ... Jeśli masz zdefiniowaną SERVER_NAME w swojej konfiguracji, spróbuj ją usunąć. – Chockomonkey

+0

@Chockomonkey Czy możesz rozwinąć o co ci chodzi? Masz na myśli, czy masz "SERVER_NAME" zdefiniowaną jako zmienną środowiskową? –

Odpowiedz

1

Z pobieżnej lekturze 'protect_from_forgery method', znalazły się następujące:

def protect_from_forgery(options = {}) 
    self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session) 
    self.request_forgery_protection_token ||= :authenticity_token 
    prepend_before_action :verify_authenticity_token, options 
    append_after_action :verify_same_origin_request 
    end 

który ma przed działaniem zwrotnego o nazwie 'verify_authenticity_token'. Jeśli spojrzymy na źródła znalazły się następujące:

def verify_authenticity_token 
    mark_for_same_origin_verification! 

    if !verified_request? 
     logger.warn "Can't verify CSRF token authenticity" if logger 
     handle_unverified_request 
    end 
    end 

Stamtąd możemy zauważyć, że nazywa to „verified_request?”.

def verified_request? 
    !protect_against_forgery? || request.get? || request.head? || 
     form_authenticity_token == params[request_forgery_protection_token] || 
     form_authenticity_token == request.headers['X-CSRF-Token'] 
    end 

Biorąc pod uwagę charakter podniesionego wyjątku, uważam, że jeden lub więcej z tych warunków nie są spełnione. Nie sądzę, że ma to coś wspólnego z adresowaniem IP.

0

Jeśli twoja aplikacja Railsowa przemawia za pośrednictwem serwera proxy bez protokołu SSL, może to być problem polegający na tym, że twój ActiveRecord :: SessionStore rzuca dopasowanie z powodu tego scenariusza.

Nasza poprawka było dokonać niepewny przechowywania sesji:

OurApplication::Application.config.session_store :active_record_store, secure: false

EDIT: Nadal nie ma jeszcze naprawić ... Jesteśmy prawdopodobnie będzie musiał dokonać wypowiedzenia SSL w aplikacjach, w przeciwieństwie do pełnomocnika w tym zakresie.

Tak więc dla nas problem nie miał nic wspólnego z SSL. Mieliśmy wywołanie javascript na pierwszym ładowaniu strony, które próbowało wykonać handshake wobec usługi backendu (poprzez POST), ale nie skonfigurowaliśmy poprawnie naszego HAProxy, aby przekierować połączenia do tej usługi, więc zamiast tego POST był uderzanie w szyny.Mimo że Railsy zwróciły 404 dla trasy, to także zresetowały sesję z powodu brakującego tokenu CSRF w żądaniu. Naprawienie routingu HAProxy naprawiło problem.

Nasz scenariusz prawdopodobnie nie ma prawie nic wspólnego z twoimi, aw Railsach 4, domyślnym zachowaniem jest protect_from_forgery podniesienie wyjątku zamiast zresetowania sesji. Och, i my też ostatecznie trzeba ustawić sesji do magazynu niepewny:

OurApplication::Application.config.session_store :active_record_store, secure: false