diff --git a/README.md b/README.md index 51d7b80..1728467 100644 --- a/README.md +++ b/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. diff --git a/nginx.tmpl b/nginx.tmpl index 83f9222..f61cd20 100644 --- a/nginx.tmpl +++ b/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" }} diff --git a/test/test_location-override.py b/test/test_location-override.py new file mode 100644 index 0000000..cbccbd9 --- /dev/null +++ b/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 diff --git a/test/test_location-override.vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override b/test/test_location-override.vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override new file mode 100644 index 0000000..f955c57 --- /dev/null +++ b/test/test_location-override.vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override @@ -0,0 +1,3 @@ +location /foo/ { + return 418; +} diff --git a/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override b/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override new file mode 100644 index 0000000..f289d30 --- /dev/null +++ b/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; +} diff --git a/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_location_override b/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_location_override new file mode 100644 index 0000000..4993313 --- /dev/null +++ b/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; +} diff --git a/test/test_location-override.vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override b/test/test_location-override.vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override new file mode 100644 index 0000000..cbbf1e1 --- /dev/null +++ b/test/test_location-override.vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override @@ -0,0 +1,3 @@ +location / { + return 418; +} diff --git a/test/test_location-override.vhost.d/explicit-root-nohash.nginx-proxy.test_location_override b/test/test_location-override.vhost.d/explicit-root-nohash.nginx-proxy.test_location_override new file mode 100644 index 0000000..cbbf1e1 --- /dev/null +++ b/test/test_location-override.vhost.d/explicit-root-nohash.nginx-proxy.test_location_override @@ -0,0 +1,3 @@ +location / { + return 418; +} diff --git a/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override b/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override new file mode 100644 index 0000000..f289d30 --- /dev/null +++ b/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; +} diff --git a/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_location_override b/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_location_override new file mode 100644 index 0000000..4993313 --- /dev/null +++ b/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; +} diff --git a/test/test_location-override.vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override b/test/test_location-override.vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override new file mode 100644 index 0000000..cbbf1e1 --- /dev/null +++ b/test/test_location-override.vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override @@ -0,0 +1,3 @@ +location / { + return 418; +} diff --git a/test/test_location-override.vhost.d/implicit-root-nohash.nginx-proxy.test_location_override b/test/test_location-override.vhost.d/implicit-root-nohash.nginx-proxy.test_location_override new file mode 100644 index 0000000..cbbf1e1 --- /dev/null +++ b/test/test_location-override.vhost.d/implicit-root-nohash.nginx-proxy.test_location_override @@ -0,0 +1,3 @@ +location / { + return 418; +} diff --git a/test/test_location-override.yml b/test/test_location-override.yml new file mode 100644 index 0000000..f36b206 --- /dev/null +++ b/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,