diff options
-rw-r--r-- | ext/unicorn_http/unicorn_http.rl | 12 | ||||
-rw-r--r-- | lib/unicorn/configurator.rb | 9 | ||||
-rw-r--r-- | lib/unicorn/http_server.rb | 8 | ||||
-rwxr-xr-x | t/t0016-trust-x-forwarded-false.sh | 30 | ||||
-rwxr-xr-x | t/t0017-trust-x-forwarded-true.sh | 30 | ||||
-rw-r--r-- | test/unit/test_http_parser_xftrust.rb | 8 |
6 files changed, 87 insertions, 10 deletions
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl index cee0e0b..f9dc158 100644 --- a/ext/unicorn_http/unicorn_http.rl +++ b/ext/unicorn_http/unicorn_http.rl @@ -30,7 +30,7 @@ * whether or not to trust X-Forwarded-Proto and X-Forwarded-SSL when * setting rack.url_scheme */ -static VALUE x_forwarded_trust = Qtrue; +static VALUE trust_x_forward = Qtrue; static unsigned long keepalive_requests = 100; /* same as nginx */ @@ -64,7 +64,7 @@ static VALUE set_ka_req(VALUE self, VALUE val) static VALUE set_xftrust(VALUE self, VALUE val) { if (Qtrue == val || Qfalse == val) - x_forwarded_trust = val; + trust_x_forward = val; else rb_raise(rb_eTypeError, "must be true or false"); @@ -77,7 +77,7 @@ static VALUE set_xftrust(VALUE self, VALUE val) */ static VALUE xftrust(VALUE self) { - return x_forwarded_trust; + return trust_x_forward; } /* keep this small for Rainbows! since every client has one */ @@ -457,7 +457,7 @@ static void set_url_scheme(VALUE env, VALUE *server_port) VALUE scheme = rb_hash_aref(env, g_rack_url_scheme); if (NIL_P(scheme)) { - if (x_forwarded_trust == Qfalse) { + if (trust_x_forward == Qfalse) { scheme = g_http; } else { scheme = rb_hash_aref(env, g_http_x_forwarded_ssl); @@ -888,8 +888,8 @@ void Init_unicorn_http(void) rb_define_singleton_method(cHttpParser, "keepalive_requests", ka_req, 0); rb_define_singleton_method(cHttpParser, "keepalive_requests=", set_ka_req, 1); - rb_define_singleton_method(cHttpParser, "x_forwarded_trust=", set_xftrust, 1); - rb_define_singleton_method(cHttpParser, "x_forwarded_trust?", xftrust, 0); + rb_define_singleton_method(cHttpParser, "trust_x_forwarded=", set_xftrust, 1); + rb_define_singleton_method(cHttpParser, "trust_x_forwarded?", xftrust, 0); init_common_fields(); SET_GLOBAL(g_http_host, "HOST"); diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb index d522c54..2415dda 100644 --- a/lib/unicorn/configurator.rb +++ b/lib/unicorn/configurator.rb @@ -42,6 +42,7 @@ class Unicorn::Configurator :preload_app => false, :rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1), :client_body_buffer_size => Unicorn::Const::MAX_BODY, + :trust_x_forwarded => true, } #:startdoc: @@ -448,6 +449,14 @@ class Unicorn::Configurator set[:user] = [ user, group ] end + # Sets whether or not the parser will trust X-Forwarded-Proto and + # X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly. + # Rainbows!/Zbatery installations facing untrusted clients directly + # should set this to +false+. This is +true+ by default. + def trust_x_forwarded(bool) + set_bool(:trust_x_forwarded, bool) + end + # expands "unix:path/to/foo" to a socket relative to the current path # expands pathnames of sockets if relative to "~" or "~username" # expands "*:port and ":port" to "0.0.0.0:port" diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb index 29b34d6..6f02f29 100644 --- a/lib/unicorn/http_server.rb +++ b/lib/unicorn/http_server.rb @@ -372,6 +372,14 @@ class Unicorn::HttpServer Unicorn::TeeInput.client_body_buffer_size = bytes end + def trust_x_forwarded + Unicorn::HttpParser.trust_x_forwarded? + end + + def trust_x_forwarded=(bool) + Unicorn::HttpParser.trust_x_forwarded = bool + end + private # wait for a signal hander to wake us up and then consume the pipe diff --git a/t/t0016-trust-x-forwarded-false.sh b/t/t0016-trust-x-forwarded-false.sh new file mode 100755 index 0000000..3163690 --- /dev/null +++ b/t/t0016-trust-x-forwarded-false.sh @@ -0,0 +1,30 @@ +#!/bin/sh +. ./test-lib.sh +t_plan 5 "trust_x_forwarded=false configuration test" + +t_begin "setup and start" && { + unicorn_setup + echo "trust_x_forwarded false" >> $unicorn_config + unicorn -D -c $unicorn_config env.ru + unicorn_wait_start +} + +t_begin "spoofed request with X-Forwarded-Proto does not trigger" && { + curl -H 'X-Forwarded-Proto: https' http://$listen/ | \ + grep -F '"rack.url_scheme"=>"http"' +} + +t_begin "spoofed request with X-Forwarded-SSL does not trigger" && { + curl -H 'X-Forwarded-SSL: on' http://$listen/ | \ + grep -F '"rack.url_scheme"=>"http"' +} + +t_begin "killing succeeds" && { + kill $unicorn_pid +} + +t_begin "check stderr has no errors" && { + check_stderr +} + +t_done diff --git a/t/t0017-trust-x-forwarded-true.sh b/t/t0017-trust-x-forwarded-true.sh new file mode 100755 index 0000000..11103c5 --- /dev/null +++ b/t/t0017-trust-x-forwarded-true.sh @@ -0,0 +1,30 @@ +#!/bin/sh +. ./test-lib.sh +t_plan 5 "trust_x_forwarded=true configuration test" + +t_begin "setup and start" && { + unicorn_setup + echo "trust_x_forwarded true " >> $unicorn_config + unicorn -D -c $unicorn_config env.ru + unicorn_wait_start +} + +t_begin "spoofed request with X-Forwarded-Proto sets 'https'" && { + curl -H 'X-Forwarded-Proto: https' http://$listen/ | \ + grep -F '"rack.url_scheme"=>"https"' +} + +t_begin "spoofed request with X-Forwarded-SSL sets 'https'" && { + curl -H 'X-Forwarded-SSL: on' http://$listen/ | \ + grep -F '"rack.url_scheme"=>"https"' +} + +t_begin "killing succeeds" && { + kill $unicorn_pid +} + +t_begin "check stderr has no errors" && { + check_stderr +} + +t_done diff --git a/test/unit/test_http_parser_xftrust.rb b/test/unit/test_http_parser_xftrust.rb index 8c3db40..c396bc4 100644 --- a/test/unit/test_http_parser_xftrust.rb +++ b/test/unit/test_http_parser_xftrust.rb @@ -5,11 +5,11 @@ include Unicorn class HttpParserXFTrustTest < Test::Unit::TestCase def setup - assert HttpParser.x_forwarded_trust? + assert HttpParser.trust_x_forward? end def test_xf_trust_false_xfp - HttpParser.x_forwarded_trust = false + HttpParser.trust_x_forward = false parser = HttpParser.new parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n" \ "X-Forwarded-Proto: https\r\n\r\n" @@ -21,7 +21,7 @@ class HttpParserXFTrustTest < Test::Unit::TestCase end def test_xf_trust_false_xfs - HttpParser.x_forwarded_trust = false + HttpParser.trust_x_forward = false parser = HttpParser.new parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n" \ "X-Forwarded-SSL: on\r\n\r\n" @@ -33,6 +33,6 @@ class HttpParserXFTrustTest < Test::Unit::TestCase end def teardown - HttpParser.x_forwarded_trust = true + HttpParser.trust_x_forward = true end end |