about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--ext/unicorn_http/unicorn_http.rl12
-rw-r--r--lib/unicorn/configurator.rb9
-rw-r--r--lib/unicorn/http_server.rb8
-rwxr-xr-xt/t0016-trust-x-forwarded-false.sh30
-rwxr-xr-xt/t0017-trust-x-forwarded-true.sh30
-rw-r--r--test/unit/test_http_parser_xftrust.rb8
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