ソースを参照

Merge pull request #1179 from harvdogg/master

Allow complete override of location blocks
Nicolas Duchon 2 年 前
コミット
1462ff019d

+ 26 - 0
README.md

@@ -491,6 +491,32 @@ ln -s /path/to/vhost.d/www.example.com /path/to/vhost.d/example.com
 
 If you want most of your virtual hosts to use a default single `location` block configuration and then override on a few specific ones, add those settings to the `/etc/nginx/vhost.d/default_location` file. This file will be used on any virtual host which does not have a `/etc/nginx/vhost.d/{VIRTUAL_HOST}_location` file associated with it.
 
+#### Overriding `location` blocks
+
+The `${VIRTUAL_HOST}_${PATH_HASH}_location`, `${VIRTUAL_HOST}_location`, and `default_location` files documented above make it possible to *augment* the generated [`location` block(s)](https://nginx.org/en/docs/http/ngx_http_core_module.html#location) in a virtual host.  In some circumstances, you may need to *completely override* the `location` block for a particular combination of virtual host and path.  To do this, create a file whose name follows this pattern:
+
+```
+/etc/nginx/vhost.d/${VIRTUAL_HOST}_${PATH_HASH}_location_override
+```
+
+where `${VIRTUAL_HOST}` is the name of the virtual host (the `VIRTUAL_HOST` environment variable) and `${PATH_HASH}` is the SHA-1 hash of the path, as [described above](#per-virtual_path-location-configuration).
+
+For convenience, the `_${PATH_HASH}` part can be omitted if the path is `/`:
+
+```
+/etc/nginx/vhost.d/${VIRTUAL_HOST}_location_override
+```
+
+When an override file exists, the `location` block that is normally created by `nginx-proxy` is not generated.  Instead, the override file is included via the [nginx `include` directive](https://nginx.org/en/docs/ngx_core_module.html#include).
+
+You are responsible for providing a suitable `location` block in your override file as required for your service.  By default, `nginx-proxy` uses the `VIRTUAL_HOST` name as the upstream name for your application's Docker container; see [here](#unhashed-vs-sha1-upstream-names) for details.  As an example, if your container has a `VIRTUAL_HOST` value of `app.example.com`, then to override the location block for `/` you would create a file named `/etc/nginx/vhost.d/app.example.com_location_override` that contains something like this:
+
+```
+location / {
+    proxy_pass http://app.example.com;
+}
+```
+
 #### Per-VIRTUAL_HOST `server_tokens` configuration
 Per virtual-host `servers_tokens` directive can be configured by passing appropriate value to the `SERVER_TOKENS` environment variable. Please see the [nginx http_core module configuration](https://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens) for more details.
 

+ 21 - 13
nginx.tmpl

@@ -76,38 +76,46 @@
 {{- end }}
 
 {{- define "location" }}
+    {{- $override := printf "/etc/nginx/vhost.d/%s_%s_location_override" .Host (sha1 .Path) }}
+    {{- if and (eq .Path "/") (not (exists $override)) }}
+        {{- $override = printf "/etc/nginx/vhost.d/%s_location_override" .Host }}
+    {{- end }}
+    {{- if exists $override }}
+    include {{ $override }};
+    {{- else }}
     location {{ .Path }} {
-    {{- if eq .NetworkTag "internal" }}
+        {{- if eq .NetworkTag "internal" }}
         # Only allow traffic from internal clients
         include /etc/nginx/network_internal.conf;
-    {{- end }}
+        {{- end }}
 
-    {{- if eq .Proto "uwsgi" }}
+        {{- if eq .Proto "uwsgi" }}
         include uwsgi_params;
         uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }};
-    {{- else if eq .Proto "fastcgi" }}
+        {{- else if eq .Proto "fastcgi" }}
         root {{ trim .VhostRoot }};
         include fastcgi_params;
         fastcgi_pass {{ trim .Upstream }};
-    {{- else if eq .Proto "grpc" }}
+        {{- else if eq .Proto "grpc" }}
         grpc_pass {{ trim .Proto }}://{{ trim .Upstream }};
-    {{- else }}
+        {{- else }}
         proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}{{ trim .Dest }};
-    {{- end }}
+        {{- end }}
 
-    {{- if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }}
+        {{- if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }}
         auth_basic "Restricted {{ .Host }}";
         auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" .Host) }};
-    {{- end }}
+        {{- end }}
 
-    {{- if (exists (printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) )) }}
+        {{- if (exists (printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) )) }}
         include {{ printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) }};
-    {{- else if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }}
+        {{- else if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }}
         include {{ printf "/etc/nginx/vhost.d/%s_location" .Host}};
-    {{- else if (exists "/etc/nginx/vhost.d/default_location") }}
+        {{- else if (exists "/etc/nginx/vhost.d/default_location") }}
         include /etc/nginx/vhost.d/default_location;
-    {{- end }}
+        {{- end }}
     }
+    {{- end }}
 {{- end }}
 
 {{- define "upstream" }}

+ 39 - 0
test/test_location-override.py

@@ -0,0 +1,39 @@
+def test_explicit_root_nohash(docker_compose, nginxproxy):
+    r = nginxproxy.get("http://explicit-root-nohash.nginx-proxy.test/port")
+    assert r.status_code == 418
+    r = nginxproxy.get("http://explicit-root-nohash.nginx-proxy.test/foo/port")
+    assert r.status_code == 200
+    assert r.text == "answer from port 82\n"
+
+def test_explicit_root_hash(docker_compose, nginxproxy):
+    r = nginxproxy.get("http://explicit-root-hash.nginx-proxy.test/port")
+    assert r.status_code == 418
+    r = nginxproxy.get("http://explicit-root-hash.nginx-proxy.test/foo/port")
+    assert r.status_code == 200
+    assert r.text == "answer from port 82\n"
+
+def test_explicit_root_hash_and_nohash(docker_compose, nginxproxy):
+    r = nginxproxy.get("http://explicit-root-hash-and-nohash.nginx-proxy.test/port")
+    assert r.status_code == 418
+    r = nginxproxy.get("http://explicit-root-hash-and-nohash.nginx-proxy.test/foo/port")
+    assert r.status_code == 200
+    assert r.text == "answer from port 82\n"
+
+def test_explicit_nonroot(docker_compose, nginxproxy):
+    r = nginxproxy.get("http://explicit-nonroot.nginx-proxy.test/port")
+    assert r.status_code == 200
+    assert r.text == "answer from port 81\n"
+    r = nginxproxy.get("http://explicit-nonroot.nginx-proxy.test/foo/port")
+    assert r.status_code == 418
+
+def test_implicit_root_nohash(docker_compose, nginxproxy):
+    r = nginxproxy.get("http://implicit-root-nohash.nginx-proxy.test/port")
+    assert r.status_code == 418
+
+def test_implicit_root_hash(docker_compose, nginxproxy):
+    r = nginxproxy.get("http://implicit-root-hash.nginx-proxy.test/port")
+    assert r.status_code == 418
+
+def test_implicit_root_hash_and_nohash(docker_compose, nginxproxy):
+    r = nginxproxy.get("http://implicit-root-hash-and-nohash.nginx-proxy.test/port")
+    assert r.status_code == 418

+ 3 - 0
test/test_location-override.vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override

@@ -0,0 +1,3 @@
+location /foo/ {
+    return 418;
+}

+ 4 - 0
test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override

@@ -0,0 +1,4 @@
+# This file should trump the file without the hash.
+location / {
+    return 418;
+}

+ 4 - 0
test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_location_override

@@ -0,0 +1,4 @@
+# The file with the hash should trump this file.
+location / {
+    return 503;
+}

+ 3 - 0
test/test_location-override.vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override

@@ -0,0 +1,3 @@
+location / {
+    return 418;
+}

+ 3 - 0
test/test_location-override.vhost.d/explicit-root-nohash.nginx-proxy.test_location_override

@@ -0,0 +1,3 @@
+location / {
+    return 418;
+}

+ 4 - 0
test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override

@@ -0,0 +1,4 @@
+# This file should trump the file without the hash.
+location / {
+    return 418;
+}

+ 4 - 0
test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_location_override

@@ -0,0 +1,4 @@
+# The file with the hash should trump this file.
+location / {
+    return 503;
+}

+ 3 - 0
test/test_location-override.vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override

@@ -0,0 +1,3 @@
+location / {
+    return 418;
+}

+ 3 - 0
test/test_location-override.vhost.d/implicit-root-nohash.nginx-proxy.test_location_override

@@ -0,0 +1,3 @@
+location / {
+    return 418;
+}

+ 44 - 0
test/test_location-override.yml

@@ -0,0 +1,44 @@
+services:
+  sut:
+    image: nginxproxy/nginx-proxy:test
+    volumes:
+      - /var/run/docker.sock:/tmp/docker.sock:ro
+      - ./test_location-override.vhost.d:/etc/nginx/vhost.d:ro
+
+  explicit-root:
+    image: web
+    expose:
+      - "81"
+    environment:
+      WEB_PORTS: "81"
+      VIRTUAL_HOST: >-
+        explicit-root-nohash.nginx-proxy.test,
+        explicit-root-hash.nginx-proxy.test,
+        explicit-root-hash-and-nohash.nginx-proxy.test,
+        explicit-nonroot.nginx-proxy.test
+      VIRTUAL_PATH: /
+  explicit-foo:
+    image: web
+    expose:
+      - "82"
+    environment:
+      WEB_PORTS: "82"
+      VIRTUAL_HOST: >-
+        explicit-root-nohash.nginx-proxy.test,
+        explicit-root-hash.nginx-proxy.test,
+        explicit-root-hash-and-nohash.nginx-proxy.test,
+        explicit-nonroot.nginx-proxy.test
+      VIRTUAL_PATH: /foo/
+      VIRTUAL_DEST: /
+
+  # Same as explicit-root except VIRTUAL_PATH is left unset.
+  implicit-root:
+    image: web
+    expose:
+      - "83"
+    environment:
+      WEB_PORTS: "83"
+      VIRTUAL_HOST: >-
+        implicit-root-nohash.nginx-proxy.test,
+        implicit-root-hash.nginx-proxy.test,
+        implicit-root-hash-and-nohash.nginx-proxy.test,