Browse Source

fix: Don't downgrade from https to http if certificate is missing

Before, if a site's certificate was not found, the site was served
over http rather than https.  Failing open like this is problematic
for sites where security is important.  Presumably the user set
`HTTPS_METHOD` to a non-`noredirect` value (or left it unset) for a
good reason; we should honor it even if it means serving error
messages.

WARNING: This change breaks compatibility.  Any vhost where all of the
following are true will fail after this change:

  * `HTTPS_METHOD` is either unset or set to a value other than
    `nohttps`.
  * The vhost does not have its own certificate (`default.crt` doesn't
    count).
  * Clients expect to be able to access the vhost by using plain http
    to nginx-proxy.

To get the previous behavior, set `HTTPS_METHOD` to `nohttps` for the
vhost.
Richard Hansen 2 năm trước cách đây
mục cha
commit
dfd4f54c61
42 tập tin đã thay đổi với 84 bổ sung19 xóa
  1. 5 5
      nginx.tmpl
  2. 2 0
      test/stress_tests/test_unreachable_network/docker-compose.yml
  3. 1 0
      test/test_DOCKER_HOST_unix_socket.yml
  4. 2 0
      test/test_composev2.yml
  5. 2 0
      test/test_custom/test_defaults-location.yml
  6. 2 0
      test/test_custom/test_defaults.yml
  7. 2 0
      test/test_custom/test_location-per-vhost.yml
  8. 2 0
      test/test_custom/test_per-vhost.yml
  9. 2 0
      test/test_custom/test_proxy-wide.yml
  10. 1 0
      test/test_default-host.yml
  11. 1 0
      test/test_default-root-none.yml
  12. 2 0
      test/test_dockergen/test_dockergen_v2.yml
  13. 2 0
      test/test_dockergen/test_dockergen_v3.yml
  14. 2 0
      test/test_events.yml
  15. 7 9
      test/test_fallback.py
  16. 2 0
      test/test_headers/test_http.yml
  17. 2 1
      test/test_http_port.yml
  18. 2 1
      test/test_internal/test_internal-per-vhost.yml
  19. 2 1
      test/test_internal/test_internal-per-vpath.yml
  20. 1 0
      test/test_ipv6.yml
  21. 2 0
      test/test_location-override.yml
  22. 1 0
      test/test_log_format.yml
  23. 2 0
      test/test_multiple-hosts.yml
  24. 2 0
      test/test_multiple-networks.yml
  25. 2 0
      test/test_multiple-ports/test_VIRTUAL_PORT-single-different-from-single-port.yml
  26. 2 0
      test/test_multiple-ports/test_VIRTUAL_PORT.yml
  27. 2 0
      test/test_multiple-ports/test_default-80.yml
  28. 2 0
      test/test_multiple-ports/test_single-port-not-80.yml
  29. 2 0
      test/test_nominal.yml
  30. 1 0
      test/test_raw-ip-vhost.yml
  31. 2 0
      test/test_server-down/test_load-balancing.yml
  32. 2 0
      test/test_server-down/test_no-server-down.yml
  33. 2 0
      test/test_server-down/test_server-down.yml
  34. 2 0
      test/test_upstream-name/test_predictable-name.yml
  35. 1 0
      test/test_upstream-name/test_sha1-name.yml
  36. 2 0
      test/test_vhost-empty-string.yml
  37. 2 0
      test/test_vhost-in-multiple-networks.yml
  38. 1 0
      test/test_virtual-path/test_custom_conf.yml
  39. 2 2
      test/test_virtual-path/test_forwarding.yml
  40. 2 0
      test/test_virtual-path/test_location_precedence.yml
  41. 2 0
      test/test_virtual-path/test_virtual_paths.yml
  42. 2 0
      test/test_wildcard_host.yml

+ 5 - 5
nginx.tmpl

@@ -349,8 +349,8 @@ proxy_set_header Proxy "";
 
 {{- /*
      * Precompute some information about each vhost.  This is done early because
-     * the creation of fallback servers depends on DEFAULT_HOST, HTTPS_METHOD,
-     * and whether there are any missing certs.
+     * the creation of fallback servers depends on DEFAULT_HOST and
+     * HTTPS_METHOD.
      */}}
 {{- range $vhost, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }}
     {{- $vhost := trim $vhost }}
@@ -389,7 +389,7 @@ proxy_set_header Proxy "";
     {{- $default_http_exists := false }}
     {{- $default_https_exists := false }}
     {{- range $vhost := $globals.vhosts }}
-        {{- $http := or (ne $vhost.https_method "nohttp") (not $vhost.cert_ok) }}
+        {{- $http := ne $vhost.https_method "nohttp" }}
         {{- $https := ne $vhost.https_method "nohttps" }}
         {{- $http_exists = or $http_exists $http }}
         {{- $https_exists = or $https_exists $https }}
@@ -493,7 +493,7 @@ server {
     {{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
     {{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
 
-    {{- if and $cert_ok (eq $https_method "redirect") }}
+    {{- if eq $https_method "redirect" }}
 server {
     server_name {{ $host }};
         {{- if $server_tokens }}
@@ -531,7 +531,7 @@ server {
     server_tokens {{ $server_tokens }};
     {{- end }}
     {{ $globals.access_log }}
-    {{- if or (eq $https_method "nohttps") (not $cert_ok) (eq $https_method "noredirect") }}
+    {{- if or (eq $https_method "nohttps") (eq $https_method "noredirect") }}
     listen {{ $globals.external_http_port }} {{ $default_server }};
         {{- if $globals.enable_ipv6 }}
     listen [::]:{{ $globals.external_http_port }} {{ $default_server }};

+ 2 - 0
test/stress_tests/test_unreachable_network/docker-compose.yml

@@ -12,6 +12,8 @@ services:
     image: nginxproxy/nginx-proxy:test
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
+    environment:
+      HTTPS_METHOD: nohttps
 
   webA:
     networks:

+ 1 - 0
test/test_DOCKER_HOST_unix_socket.yml

@@ -21,3 +21,4 @@ sut:
     - /var/run/docker.sock:/f00.sock:ro
   environment:
     DOCKER_HOST: unix:///f00.sock
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_composev2.yml

@@ -4,6 +4,8 @@ services:
     image: nginxproxy/nginx-proxy:test
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
+    environment:
+      HTTPS_METHOD: nohttps
 
   web:
     image: web

+ 2 - 0
test/test_custom/test_defaults-location.yml

@@ -4,6 +4,8 @@ nginx-proxy:
     - /var/run/docker.sock:/tmp/docker.sock:ro
     - ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/default_location:ro
     - ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.local_location:ro
+  environment:
+    HTTPS_METHOD: nohttps
 
 web1:
   image: web

+ 2 - 0
test/test_custom/test_defaults.yml

@@ -5,6 +5,8 @@ services:
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
       - ./my_custom_proxy_settings.conf:/etc/nginx/proxy.conf:ro
+    environment:
+      HTTPS_METHOD: nohttps
 
   web1:
     image: web

+ 2 - 0
test/test_custom/test_location-per-vhost.yml

@@ -5,6 +5,8 @@ services:
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
       - ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.local_location:ro
+    environment:
+      HTTPS_METHOD: nohttps
 
   web1:
     image: web

+ 2 - 0
test/test_custom/test_per-vhost.yml

@@ -5,6 +5,8 @@ services:
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
       - ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.local:ro
+    environment:
+      HTTPS_METHOD: nohttps
 
   web1:
     image: web

+ 2 - 0
test/test_custom/test_proxy-wide.yml

@@ -5,6 +5,8 @@ services:
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
       - ./my_custom_proxy_settings.conf:/etc/nginx/conf.d/my_custom_proxy_settings.conf:ro
+    environment:
+      HTTPS_METHOD: nohttps
 
   web1:
     image: web

+ 1 - 0
test/test_default-host.yml

@@ -15,3 +15,4 @@ sut:
     - /var/run/docker.sock:/tmp/docker.sock:ro
   environment:
     DEFAULT_HOST: web1.tld
+    HTTPS_METHOD: nohttps

+ 1 - 0
test/test_default-root-none.yml

@@ -5,6 +5,7 @@ services:
       - /var/run/docker.sock:/tmp/docker.sock:ro
     environment:
       DEFAULT_ROOT: none
+      HTTPS_METHOD: nohttps
   web:
     image: web
     expose:

+ 2 - 0
test/test_dockergen/test_dockergen_v2.yml

@@ -15,6 +15,8 @@ services:
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
       - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
+    environment:
+      HTTPS_METHOD: nohttps
 
   web:
     image: web

+ 2 - 0
test/test_dockergen/test_dockergen_v3.yml

@@ -13,6 +13,8 @@ services:
       - /var/run/docker.sock:/tmp/docker.sock:ro
       - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
       - nginx_conf:/etc/nginx/conf.d
+    environment:
+      HTTPS_METHOD: nohttps
 
   web:
     image: web

+ 2 - 0
test/test_events.yml

@@ -2,3 +2,5 @@ nginxproxy:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 7 - 9
test/test_fallback.py

@@ -44,7 +44,7 @@ CONNECTION_REFUSED_RE = re.compile("Connection refused")
     ("withdefault.yml", "https://https-only.nginx-proxy.test/", 200, None),
     ("withdefault.yml", "http://http-only.nginx-proxy.test/", 200, None),
     ("withdefault.yml", "https://http-only.nginx-proxy.test/", 503, None),
-    ("withdefault.yml", "http://missing-cert.nginx-proxy.test/", 200, None),
+    ("withdefault.yml", "http://missing-cert.nginx-proxy.test/", 301, None),
     ("withdefault.yml", "https://missing-cert.nginx-proxy.test/", 500, None),
     ("withdefault.yml", "http://unknown.nginx-proxy.test/", 503, None),
     ("withdefault.yml", "https://unknown.nginx-proxy.test/", 503, None),
@@ -55,7 +55,7 @@ CONNECTION_REFUSED_RE = re.compile("Connection refused")
     ("nodefault.yml", "https://https-only.nginx-proxy.test/", 200, None),
     ("nodefault.yml", "http://http-only.nginx-proxy.test/", 200, None),
     ("nodefault.yml", "https://http-only.nginx-proxy.test/", None, INTERNAL_ERR_RE),
-    ("nodefault.yml", "http://missing-cert.nginx-proxy.test/", 200, None),
+    ("nodefault.yml", "http://missing-cert.nginx-proxy.test/", 301, None),
     ("nodefault.yml", "https://missing-cert.nginx-proxy.test/", None, INTERNAL_ERR_RE),
     ("nodefault.yml", "http://unknown.nginx-proxy.test/", 503, None),
     ("nodefault.yml", "https://unknown.nginx-proxy.test/", None, INTERNAL_ERR_RE),
@@ -69,15 +69,13 @@ CONNECTION_REFUSED_RE = re.compile("Connection refused")
     ("nohttp-on-app.yml", "https://https-only.nginx-proxy.test/", 200, None),
     ("nohttp-on-app.yml", "http://unknown.nginx-proxy.test/", None, CONNECTION_REFUSED_RE),
     ("nohttp-on-app.yml", "https://unknown.nginx-proxy.test/", 503, None),
-    # Same as nohttp.yml, except there is a vhost with a missing cert.  This causes its
-    # HTTPS_METHOD=nohttp setting to effectively become HTTPS_METHOD=noredirect.  This means that
-    # there will be a plain http server solely to support that vhost, so http requests to other
-    # vhosts get a 503, not a connection refused error.
-    ("nohttp-with-missing-cert.yml", "http://https-only.nginx-proxy.test/", 503, None),
+    # Same as nohttp.yml, except there is a vhost with a missing cert.  The missing cert should not
+    # cause that vhost to downgrade from https to http.
+    ("nohttp-with-missing-cert.yml", "http://https-only.nginx-proxy.test/", None, CONNECTION_REFUSED_RE),
     ("nohttp-with-missing-cert.yml", "https://https-only.nginx-proxy.test/", 200, None),
-    ("nohttp-with-missing-cert.yml", "http://missing-cert.nginx-proxy.test/", 200, None),
+    ("nohttp-with-missing-cert.yml", "http://missing-cert.nginx-proxy.test/", None, CONNECTION_REFUSED_RE),
     ("nohttp-with-missing-cert.yml", "https://missing-cert.nginx-proxy.test/", 500, None),
-    ("nohttp-with-missing-cert.yml", "http://unknown.nginx-proxy.test/", 503, None),
+    ("nohttp-with-missing-cert.yml", "http://unknown.nginx-proxy.test/", None, CONNECTION_REFUSED_RE),
     ("nohttp-with-missing-cert.yml", "https://unknown.nginx-proxy.test/", 503, None),
     # HTTPS_METHOD=nohttps on nginx-proxy, HTTPS_METHOD unset on the app container.
     ("nohttps.yml", "http://http-only.nginx-proxy.test/", 200, None),

+ 2 - 0
test/test_headers/test_http.yml

@@ -20,3 +20,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 1
test/test_http_port.yml

@@ -11,4 +11,5 @@ sut:
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
   environment:
-    HTTP_PORT: 8080
+    HTTP_PORT: 8080
+    HTTPS_METHOD: nohttps

+ 2 - 1
test/test_internal/test_internal-per-vhost.yml

@@ -20,4 +20,5 @@ sut:
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
     - ./network_internal.conf:/etc/nginx/network_internal.conf:ro
-
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 1
test/test_internal/test_internal-per-vpath.yml

@@ -24,4 +24,5 @@ sut:
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
     - ./network_internal.conf:/etc/nginx/network_internal.conf:ro
-
+  environment:
+    HTTPS_METHOD: nohttps

+ 1 - 0
test/test_ipv6.yml

@@ -35,5 +35,6 @@ services:
       - /var/run/docker.sock:/tmp/docker.sock:ro
     environment:
       ENABLE_IPV6: "true"
+      HTTPS_METHOD: nohttps
     networks:
       - net1

+ 2 - 0
test/test_location-override.yml

@@ -4,6 +4,8 @@ services:
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
       - ./test_location-override.vhost.d:/etc/nginx/vhost.d:ro
+    environment:
+      HTTPS_METHOD: nohttps
 
   explicit-root:
     image: web

+ 1 - 0
test/test_log_format.yml

@@ -12,4 +12,5 @@ sut:
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
   environment:
+    HTTPS_METHOD: nohttps
     LOG_FORMAT: "$$remote_addr - $$remote_user [$$time_local] \"$$request\" $$status $$body_bytes_sent \"$$http_referer\" \"$$http_user_agent\" request_time=$$request_time $$upstream_response_time"

+ 2 - 0
test/test_multiple-hosts.yml

@@ -11,3 +11,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_multiple-networks.yml

@@ -16,6 +16,8 @@ services:
       - net2
       - net3a
       - net3b
+    environment:
+      HTTPS_METHOD: nohttps
 
   web1:
     image: web

+ 2 - 0
test/test_multiple-ports/test_VIRTUAL_PORT-single-different-from-single-port.yml

@@ -12,3 +12,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_multiple-ports/test_VIRTUAL_PORT.yml

@@ -12,3 +12,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_multiple-ports/test_default-80.yml

@@ -11,3 +11,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_multiple-ports/test_single-port-not-80.yml

@@ -11,3 +11,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_nominal.yml

@@ -35,3 +35,5 @@ services:
       - /var/run/docker.sock:/tmp/docker.sock:ro
     networks:
       - net1
+    environment:
+      HTTPS_METHOD: nohttps

+ 1 - 0
test/test_raw-ip-vhost.yml

@@ -39,6 +39,7 @@ services:
     image: nginxproxy/nginx-proxy:test
     environment:
       ENABLE_IPV6: "true"
+      HTTPS_METHOD: nohttps
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
     networks:

+ 2 - 0
test/test_server-down/test_load-balancing.yml

@@ -27,3 +27,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_server-down/test_no-server-down.yml

@@ -10,3 +10,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_server-down/test_server-down.yml

@@ -11,3 +11,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_upstream-name/test_predictable-name.yml

@@ -13,3 +13,5 @@ services:
     image: nginxproxy/nginx-proxy:test
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
+    environment:
+      HTTPS_METHOD: nohttps

+ 1 - 0
test/test_upstream-name/test_sha1-name.yml

@@ -14,4 +14,5 @@ services:
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
     environment:
+      HTTPS_METHOD: nohttps
       SHA1_UPSTREAM_NAME: "true"

+ 2 - 0
test/test_vhost-empty-string.yml

@@ -3,6 +3,8 @@ services:
     image: nginxproxy/nginx-proxy:test
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
+    environment:
+      HTTPS_METHOD: nohttps
   web1:
     image: web
     expose:

+ 2 - 0
test/test_vhost-in-multiple-networks.yml

@@ -12,6 +12,8 @@ services:
       - /var/run/docker.sock:/tmp/docker.sock:ro
     networks:
       - net1
+    environment:
+      HTTPS_METHOD: nohttps
 
   web:
     image: web

+ 1 - 0
test/test_virtual-path/test_custom_conf.yml

@@ -40,6 +40,7 @@ sut:
   image: nginxproxy/nginx-proxy:test
   environment:
     DEFAULT_ROOT: 418
+    HTTPS_METHOD: nohttps
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
     - ./foo.conf:/etc/nginx/vhost.d/foo.nginx-proxy.test:ro

+ 2 - 2
test/test_virtual-path/test_forwarding.yml

@@ -12,6 +12,6 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
-    - ./certs:/etc/nginx/certs:ro
   environment:
-    - DEFAULT_ROOT=301 http://$$host/web1$$request_uri
+    DEFAULT_ROOT: 301 http://$$host/web1$$request_uri
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_virtual-path/test_location_precedence.yml

@@ -35,3 +35,5 @@ sut:
     - ./default.conf:/etc/nginx/vhost.d/default_location:ro
     - ./host.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_location:ro
     - ./path.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_99f2db0ed8aa95dbb5b87fca79c7eff2ff6bb8bd_location:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_virtual-path/test_virtual_paths.yml

@@ -40,3 +40,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps

+ 2 - 0
test/test_wildcard_host.yml

@@ -35,3 +35,5 @@ sut:
   image: nginxproxy/nginx-proxy:test
   volumes:
     - /var/run/docker.sock:/tmp/docker.sock:ro
+  environment:
+    HTTPS_METHOD: nohttps