Merge 307c4667a9 into 0d9cd2b45c
This commit is contained in:
commit
290378eb2f
67
README.md
67
README.md
@ -102,6 +102,8 @@ By default, docker uses IPv6-to-IPv4 NAT. This means all client connections from
|
||||
|
||||
If you need to support multiple virtual hosts for a container, you can separate each entry with commas. For example, `foo.bar.com,baz.bar.com,bar.com` and each host will be setup the same.
|
||||
|
||||
Do **not** put any space before of after each comma.
|
||||
|
||||
### Virtual Ports
|
||||
|
||||
When your container exposes only one port, nginx-proxy will default to this port, else to port 80.
|
||||
@ -113,6 +115,71 @@ For each host defined into `VIRTUAL_HOST`, the associated virtual port is retrie
|
||||
1. From the container's exposed port if there is only one
|
||||
1. From the default port 80 when none of the above methods apply
|
||||
|
||||
### Multiport Syntax
|
||||
|
||||
There are services which expose more than one port. In this case you can set the `VIRTUAL_PORT` variable using multiport syntax:
|
||||
```
|
||||
VIRTUAL_PORT = [ <virtual_port> | <multiport> ];
|
||||
multiport = port, { ",", port };
|
||||
port = <virtual_port> [ ":", <virtual_path> [ ":", <virtual_dest> ]];
|
||||
```
|
||||
|
||||
`<virtual_port>`, `<virtual_path>`, and `<virtual_dest>` accept the same values that `VIRTUAL_PORT`, `VIRTUAL_PATH`, and `VIRTUAL_DEST` do.
|
||||
|
||||
Example:
|
||||
```
|
||||
VIRTUAL_HOST: "multiport.example.com"
|
||||
VIRTUAL_PORT: "9220:~ ^/(admin|fonts?|images|webmin)/,10901,20901:/ws2p,30901:/gva/playground"
|
||||
```
|
||||
would produce one nginx `upstream` definition per port, and as many `location` blocs:
|
||||
```
|
||||
# multiport.example.com:10901
|
||||
upstream multiport.example.com-10901 {
|
||||
## Can be connected with "docker-gen-bridge" network
|
||||
# blah
|
||||
server 172.29.0.5:10901;
|
||||
}
|
||||
# multiport.example.com:20901/ws2p
|
||||
upstream multiport.example.com-5c7ebef820fe004e45e3af1d0c47971594d028b2-20901 {
|
||||
## Can be connected with "docker-gen-bridge" network
|
||||
# blah
|
||||
server 172.29.0.5:20901;
|
||||
}
|
||||
# multiport.example.com:30901/gva/playground
|
||||
upstream multiport.example.com-1f02ce2421b17d828edaabfc3014360891bb0be3-30901 {
|
||||
## Can be connected with "docker-gen-bridge" network
|
||||
# blah
|
||||
server 172.29.0.5:30901;
|
||||
}
|
||||
# multiport.example.com:9220~ ^/(admin|fonts?|images|webmin)/
|
||||
upstream multiport.example.com-cae8bfc2ea1fe6bb6fda08727ab065e8fed98aa2-9220 {
|
||||
## Can be connected with "docker-gen-bridge" network
|
||||
# blah
|
||||
server 172.29.0.5:9220;
|
||||
}
|
||||
server {
|
||||
server_name multiport.example.com;
|
||||
listen 80 ;
|
||||
access_log /var/log/nginx/access.log vhost;
|
||||
location / {
|
||||
proxy_pass http://multiport.example.com-10901;
|
||||
}
|
||||
location /ws2p {
|
||||
proxy_pass http://multiport.example.com-5c7ebef820fe004e45e3af1d0c47971594d028b2-20901;
|
||||
}
|
||||
location /gva/playground {
|
||||
proxy_pass http://multiport.example.com-1f02ce2421b17d828edaabfc3014360891bb0be3-30901;
|
||||
}
|
||||
location ~ ^/(admin|fonts?|images|webmin)/ {
|
||||
proxy_pass http://multiport.example.com-cae8bfc2ea1fe6bb6fda08727ab065e8fed98aa2-9220;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As with the `VIRTUAL_PATH` it is possible to define per path location configuration files.
|
||||
|
||||
**Important note:** All `VIRTUAL_PATH` variables will be ignored for any virtual host appearing in a at least one container where `VIRTUAL_PORT` uses the multiport syntax, .
|
||||
|
||||
### Wildcard Hosts
|
||||
|
||||
You can also use wildcards at the beginning and the end of host name, like `*.bar.com` or `foo.bar.*`. Or even a regular expression, which can be very useful in conjunction with a wildcard DNS service like [nip.io](https://nip.io) or [sslip.io](https://sslip.io), using `~^foo\.bar\..*\.nip\.io` will match `foo.bar.127.0.0.1.nip.io`, `foo.bar.10.0.2.2.nip.io` and all other given IPs. More information about this topic can be found in the nginx documentation about [`server_names`](http://nginx.org/en/docs/http/server_names.html).
|
||||
|
||||
110
nginx.tmpl
110
nginx.tmpl
@ -110,7 +110,8 @@
|
||||
# exposed ports:{{ range sortObjectsByKeysAsc $.container.Addresses "Port" }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }}
|
||||
{{- $default_port := when (eq (len $.container.Addresses) 1) (first $.container.Addresses).Port "80" }}
|
||||
# default port: {{ $default_port }}
|
||||
{{- $port := or $.container.Env.VIRTUAL_PORT $default_port }}
|
||||
{{- $current_virtual_port := when (ne $.virtual_port "") $.virtual_port (coalesce $.container.Env.VIRTUAL_PORT "") }}
|
||||
{{- $port := when (ne $current_virtual_port "") $current_virtual_port $default_port }}
|
||||
# using port: {{ $port }}
|
||||
{{- $addr_obj := where $.container.Addresses "Port" $port | first }}
|
||||
{{- if and $addr_obj $addr_obj.HostPort }}
|
||||
@ -179,7 +180,7 @@
|
||||
include {{ $override }};
|
||||
{{- else }}
|
||||
{{- $keepalive := first (keys (groupByLabel .Containers "com.github.nginx-proxy.nginx-proxy.keepalive")) }}
|
||||
location {{ .Path }} {
|
||||
location {{ when (ne .Path "") .Path "/" }} {
|
||||
{{- if eq .NetworkTag "internal" }}
|
||||
# Only allow traffic from internal clients
|
||||
include /etc/nginx/network_internal.conf;
|
||||
@ -219,6 +220,7 @@
|
||||
{{- end }}
|
||||
|
||||
{{- define "upstream" }}
|
||||
{{- $virtual_port := .VirtualPort }}
|
||||
upstream {{ .Upstream }} {
|
||||
{{- $server_found := false }}
|
||||
{{- $loadbalance := first (keys (groupByLabel .Containers "com.github.nginx-proxy.nginx-proxy.loadbalance")) }}
|
||||
@ -231,7 +233,7 @@ upstream {{ .Upstream }} {
|
||||
{{- $args := dict "globals" $.globals "container" $container }}
|
||||
{{- template "container_ip" $args }}
|
||||
{{- $ip := $args.ip }}
|
||||
{{- $args := dict "container" $container }}
|
||||
{{- $args := dict "container" $container "virtual_port" $virtual_port }}
|
||||
{{- template "container_port" $args }}
|
||||
{{- $port := $args.port }}
|
||||
{{- if $ip }}
|
||||
@ -353,7 +355,10 @@ proxy_set_header Proxy "";
|
||||
* and whether there are any missing certs.
|
||||
*/}}
|
||||
{{- range $vhost, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }}
|
||||
{{- $vhost := trim $vhost }}
|
||||
{{- if (ne (trim $vhost) $vhost) }}
|
||||
# WARNING: virtual host '{{ $vhost }}' is not trimmed
|
||||
{{- $vhost = trim $vhost }}
|
||||
{{- end }}
|
||||
{{- if not $vhost }}
|
||||
{{- /* Ignore containers with VIRTUAL_HOST set to the empty string. */}}
|
||||
{{- continue }}
|
||||
@ -447,12 +452,40 @@ server {
|
||||
{{- $is_regexp := hasPrefix "~" $host }}
|
||||
{{- $upstream_name := when (or $is_regexp $globals.sha1_upstream_name) (sha1 $host) $host }}
|
||||
|
||||
{{- $paths := groupBy $containers "Env.VIRTUAL_PATH" }}
|
||||
{{- /*
|
||||
* Split containers between legacy VIRTUAL_PORT syntax and multiport one
|
||||
*/}}
|
||||
{{- $multiport_syntax_containers := list }}
|
||||
{{- range $virtual_port, $vp_containers := groupBy $containers "Env.VIRTUAL_PORT" }}
|
||||
{{- if (or (gt (len (split $virtual_port ":")) 1) (gt (len (split $virtual_port ",")) 1)) }}
|
||||
{{- $multiport_syntax_containers = concat $multiport_syntax_containers $vp_containers }}
|
||||
{{- range $container := $vp_containers }}
|
||||
{{- $containers = without $containers $container }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- $virtualPorts := groupByMulti $multiport_syntax_containers "Env.VIRTUAL_PORT" "," }}
|
||||
{{- $nVirtualPorts := len $virtualPorts }}
|
||||
|
||||
{{- /*
|
||||
* Ignore VIRTUAL_PATH variables when using ultiport syntax
|
||||
*/}}
|
||||
{{- $paths := dict }}
|
||||
{{- if (eq $nVirtualPorts 0) }}
|
||||
{{- $paths = groupBy $containers "Env.VIRTUAL_PATH" }}
|
||||
{{- end }}
|
||||
{{- $nPaths := len $paths }}
|
||||
{{- if eq $nPaths 0 }}
|
||||
|
||||
{{- /*
|
||||
* No VIRTUAL_PORT, no VIRTUAL_PATH
|
||||
*/}}
|
||||
{{- if (and (eq $nPaths 0) (gt (len $containers) 0)) }}
|
||||
{{- $paths = dict "/" $containers }}
|
||||
{{- end }}
|
||||
|
||||
{{- /*
|
||||
* Loops over VIRTUAL_PATHs
|
||||
*/}}
|
||||
{{- range $path, $containers := $paths }}
|
||||
{{- $upstream := $upstream_name }}
|
||||
{{- if gt $nPaths 0 }}
|
||||
@ -460,7 +493,28 @@ server {
|
||||
{{- $upstream = printf "%s-%s" $upstream $sum }}
|
||||
{{- end }}
|
||||
# {{ $host }}{{ $path }}
|
||||
{{ template "upstream" (dict "globals" $globals "Upstream" $upstream "Containers" $containers) }}
|
||||
{{- template "upstream" (dict "globals" $globals "Upstream" $upstream "Containers" $containers "VirtualPort" "") }}
|
||||
{{- end }}
|
||||
|
||||
{{- /*
|
||||
* Loops over multiport syntax containers
|
||||
*/}}
|
||||
{{- range $vp_spec, $containers := $virtualPorts }}
|
||||
{{- $vp_data := split $vp_spec ":" }}
|
||||
{{- $vp_port := index $vp_data 0 }}
|
||||
{{- $vp_path := "" }}
|
||||
{{- $upstream := "" }}
|
||||
{{- if (ge (len $vp_data) 2) }}
|
||||
{{- $vp_path = index $vp_data 1 }}
|
||||
{{- end }}
|
||||
{{- if (ne $vp_path "") }}
|
||||
{{- $sum := sha1 $vp_path }}
|
||||
{{- $upstream = printf "%s-%s-%s" $upstream_name $sum $vp_port }}
|
||||
{{- else }}
|
||||
{{- $upstream = printf "%s-%s" $upstream_name $vp_port }}
|
||||
{{- end }}
|
||||
# {{ $host }}:{{ $vp_port }}{{ $vp_path }}
|
||||
{{- template "upstream" (dict "globals" $globals "Upstream" $upstream "Containers" $containers "VirtualPort" $vp_port)}}
|
||||
{{- end }}
|
||||
|
||||
{{- /*
|
||||
@ -597,6 +651,7 @@ server {
|
||||
include /etc/nginx/vhost.d/default;
|
||||
{{- end }}
|
||||
|
||||
{{- $needs_default_root_response := ne $globals.default_root_response "none" }}
|
||||
{{- range $path, $containers := $paths }}
|
||||
{{- /*
|
||||
* Get the VIRTUAL_PROTO defined by containers w/ the same
|
||||
@ -617,8 +672,47 @@ server {
|
||||
{{- $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }}
|
||||
{{- end }}
|
||||
{{- template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag "Containers" $containers) }}
|
||||
{{- if (eq $path "/") }}
|
||||
{{- $needs_default_root_response = false }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and (not (contains $paths "/")) (ne $globals.default_root_response "none")}}
|
||||
|
||||
{{- range $vp_spec, $containers := $virtualPorts }}
|
||||
{{- $vp_data := split $vp_spec ":" }}
|
||||
{{- $vp_port := index $vp_data 0 }}
|
||||
{{- $vp_path := "" }}
|
||||
{{- $vp_dest := "" }}
|
||||
{{- $upstream := "" }}
|
||||
{{- if (ge (len $vp_data) 2) }}
|
||||
{{- $vp_path = index $vp_data 1 }}
|
||||
{{- if (ge (len $vp_data) 3) }}
|
||||
{{- $vp_dest = index $vp_data 2 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if (ne $vp_path "") }}
|
||||
{{- $sum := sha1 $vp_path }}
|
||||
{{- $upstream = printf "%s-%s-%s" $upstream_name $sum $vp_port }}
|
||||
{{- else }}
|
||||
{{- $upstream = printf "%s-%s" $upstream_name $vp_port }}
|
||||
{{- end }}
|
||||
{{- if (or (eq $vp_path "") (eq $vp_path "/")) }}
|
||||
{{- $needs_default_root_response = false }}
|
||||
{{- end }}
|
||||
{{- /*
|
||||
* Get the VIRTUAL_PROTO defined by containers w/ the same
|
||||
* vhost-vpath, falling back to "http".
|
||||
*/}}
|
||||
{{- $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }}
|
||||
|
||||
{{- /*
|
||||
* Get the NETWORK_ACCESS defined by containers w/ the same vhost,
|
||||
* falling back to "external".
|
||||
*/}}
|
||||
{{- $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }}
|
||||
{{- template "location" (dict "Path" $vp_path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $vp_dest "NetworkTag" $network_tag "Containers" $containers) }}
|
||||
{{- end }}
|
||||
|
||||
{{- if $needs_default_root_response }}
|
||||
location / {
|
||||
return {{ $globals.default_root_response }};
|
||||
}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def test_web_no_slash_location(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://web.nginx-proxy.tld/")
|
||||
assert r.status_code == 405
|
||||
|
||||
def test_web_rout_to_slash_port(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://web.nginx-proxy.tld/which-port")
|
||||
assert r.status_code == 200
|
||||
assert "answer from port 83\n" in r.text
|
||||
|
||||
def test_web1_answers_on_slash_location(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://web1.nginx-proxy.tld/")
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_web1_no_virtual_path(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://web1.nginx-proxy.tld/which-port")
|
||||
assert r.status_code == 404
|
||||
|
||||
def test_web1_port_80_is_served_by_location_slash_80(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://web1.nginx-proxy.tld/80/port")
|
||||
assert r.status_code == 200
|
||||
assert "answer from port 80\n" in r.text
|
||||
|
||||
def test_web1_port_81_is_served_by_location_slash_81(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://web1.nginx-proxy.tld/81/port")
|
||||
assert r.status_code == 200
|
||||
assert "answer from port 81\n" in r.text
|
||||
@ -0,0 +1,28 @@
|
||||
web:
|
||||
image: web
|
||||
expose:
|
||||
- "83"
|
||||
environment:
|
||||
WEB_PORTS: "83"
|
||||
VIRTUAL_HOST: "web.nginx-proxy.tld,web1.nginx-proxy.tld"
|
||||
VIRTUAL_PORT: "83"
|
||||
VIRTUAL_PATH: "/which-port"
|
||||
VIRTUAL_DEST: "/port"
|
||||
|
||||
web1:
|
||||
image: web
|
||||
expose:
|
||||
- "80"
|
||||
- "81"
|
||||
environment:
|
||||
WEB_PORTS: "80 81"
|
||||
VIRTUAL_HOST: "web1.nginx-proxy.tld"
|
||||
VIRTUAL_PORT: "80:/80:/,81:/81:/"
|
||||
|
||||
sut:
|
||||
image: nginxproxy/nginx-proxy:test
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./web.nginx-proxy.tld_022792f37cc2c58102bdf79316aebed215e0de21_location:/etc/nginx/vhost.d/web.nginx-proxy.tld_022792f37cc2c58102bdf79316aebed215e0de21_location
|
||||
environment:
|
||||
DEFAULT_ROOT: "405"
|
||||
26
test/test_multiport_syntax/test_multiport_syntax.py
Normal file
26
test/test_multiport_syntax/test_multiport_syntax.py
Normal file
@ -0,0 +1,26 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def test_port_80_is_server_by_location_root(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://web.nginx-proxy.tld/port")
|
||||
assert r.status_code == 200
|
||||
assert "answer from port 80\n" in r.text
|
||||
|
||||
def test_port_81_is_server_by_location_slash81(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://web.nginx-proxy.tld/81/port")
|
||||
assert r.status_code == 200
|
||||
assert "answer from port 81\n" in r.text
|
||||
|
||||
def test_port_82_is_server_by_location_slash82_with_dest_slashport(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://web.nginx-proxy.tld/82")
|
||||
assert r.status_code == 200
|
||||
assert "answer from port 82\n" in r.text
|
||||
|
||||
def test_port_83_is_server_by_regex_location_slash83_with_rewrite_in_custom_location_file(docker_compose, nginxproxy):
|
||||
# The custom location file with rewrite is requested because when
|
||||
# location is specified using a regex then proxy_pass should be
|
||||
# specified without a URI
|
||||
# see http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
|
||||
r = nginxproxy.get("http://web.nginx-proxy.tld/83/port")
|
||||
assert r.status_code == 200
|
||||
assert "answer from port 83\n" in r.text
|
||||
17
test/test_multiport_syntax/test_multiport_syntax.yml
Normal file
17
test/test_multiport_syntax/test_multiport_syntax.yml
Normal file
@ -0,0 +1,17 @@
|
||||
web:
|
||||
image: web
|
||||
expose:
|
||||
- "80"
|
||||
- "81"
|
||||
- "82"
|
||||
- "83"
|
||||
environment:
|
||||
WEB_PORTS: "80 81 82 83"
|
||||
VIRTUAL_HOST: "web.nginx-proxy.tld"
|
||||
VIRTUAL_PORT: "80,81:/81:/,82:/82:/port,83:~ ^/[8][3]"
|
||||
|
||||
sut:
|
||||
image: nginxproxy/nginx-proxy:test
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./web.nginx-proxy.tld_022792f37cc2c58102bdf79316aebed215e0de21_location:/etc/nginx/vhost.d/web.nginx-proxy.tld_022792f37cc2c58102bdf79316aebed215e0de21_location
|
||||
@ -0,0 +1 @@
|
||||
rewrite ^/83/(.*)$ /$1 break;
|
||||
Loading…
Reference in New Issue
Block a user