mirror of
https://github.com/nginx-proxy/nginx-proxy
synced 2024-05-09 09:06:16 +02:00
Compare commits
6 Commits
aa22257d66
...
bd1aeda540
Author | SHA1 | Date | |
---|---|---|---|
Gilles Filippini | bd1aeda540 | ||
Nicolas Duchon | 94fb8459cd | ||
Nicolas Duchon | c7bf75609b | ||
Nicolas Duchon | 3c3b3675c1 | ||
Huge | 16b84ea1b5 | ||
dependabot[bot] | 6441daf25b |
|
@ -18,7 +18,7 @@ docker run --detach \
|
|||
--name nginx-proxy \
|
||||
--publish 80:80 \
|
||||
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
|
||||
nginxproxy/nginx-proxy:1.4
|
||||
nginxproxy/nginx-proxy:1.5
|
||||
```
|
||||
|
||||
Then start any containers (here an nginx container) you want proxied with an env var `VIRTUAL_HOST=subdomain.yourdomain.com`
|
||||
|
@ -48,7 +48,7 @@ The nginx-proxy images are available in two flavors.
|
|||
This image is based on the nginx:mainline image, itself based on the debian slim image.
|
||||
|
||||
```console
|
||||
docker pull nginxproxy/nginx-proxy:1.4
|
||||
docker pull nginxproxy/nginx-proxy:1.5
|
||||
```
|
||||
|
||||
#### Alpine based version (`-alpine` suffix)
|
||||
|
@ -56,14 +56,14 @@ docker pull nginxproxy/nginx-proxy:1.4
|
|||
This image is based on the nginx:alpine image.
|
||||
|
||||
```console
|
||||
docker pull nginxproxy/nginx-proxy:1.4-alpine
|
||||
docker pull nginxproxy/nginx-proxy:1.5-alpine
|
||||
```
|
||||
|
||||
#### :warning: a note on `latest` and `alpine`:
|
||||
|
||||
It is not recommended to use the `latest` (`nginxproxy/nginx-proxy`, `nginxproxy/nginx-proxy:latest`) or `alpine` (`nginxproxy/nginx-proxy:alpine`) tag for production setups.
|
||||
|
||||
Those tags points to the latest commit in the `main` branch. They do not carry any promise of stability, and using them will probably put your nginx-proxy setup at risk of experiencing uncontrolled updates to non backward compatible versions (or versions with breaking changes). You should always specify the version you want to use explicitly to ensure your setup doesn't break when the image is updated.
|
||||
[Those tags point](https://hub.docker.com/r/nginxproxy/nginx-proxy/tags) to the latest commit in the `main` branch. They do not carry any promise of stability, and using them will probably put your nginx-proxy setup at risk of experiencing uncontrolled updates to non backward compatible versions (or versions with breaking changes). You should always specify the version you want to use explicitly to ensure your setup doesn't break when the image is updated.
|
||||
|
||||
### Additional documentation
|
||||
|
||||
|
|
|
@ -50,6 +50,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.
|
||||
|
@ -61,6 +63,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).
|
||||
|
|
111
nginx.tmpl
111
nginx.tmpl
|
@ -347,6 +347,7 @@ upstream {{ $vpath.upstream }} {
|
|||
* - "Containers": List of container's RuntimeContainer struct.
|
||||
* - "Upstream_name"
|
||||
* - "Has_virtual_paths": boolean
|
||||
* - "Multiport_syntax": boolean
|
||||
* - "Path"
|
||||
*
|
||||
* The return values will be added to the dot dict with keys:
|
||||
|
@ -373,6 +374,12 @@ upstream {{ $vpath.upstream }} {
|
|||
{{- $upstream = printf "%s-%s" $upstream $sum }}
|
||||
{{- $dest = or (first (groupByKeys $.Containers "Env.VIRTUAL_DEST")) "" }}
|
||||
{{- end }}
|
||||
{{- if $.Multiport_syntax }}
|
||||
{{- if (not (eq $.Path "/")) }}
|
||||
{{- $sum := sha1 $.Path }}
|
||||
{{- $upstream = printf "%s-%s" $upstream $sum }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- $_ := set $ "proto" $proto }}
|
||||
{{- $_ := set $ "network_tag" $network_tag }}
|
||||
{{- $_ := set $ "upstream" $upstream }}
|
||||
|
@ -381,6 +388,30 @@ upstream {{ $vpath.upstream }} {
|
|||
{{- $_ := set $ "keepalive" $keepalive }}
|
||||
{{- end }}
|
||||
|
||||
{{- /*
|
||||
* Template used as a function to parse a port specification from
|
||||
* multiport syntax.
|
||||
* The results (path, port, dest) are returned via the provided do dict.
|
||||
*/}}
|
||||
{{- define "parse_multiport_syntax_port_spec" }}
|
||||
{{- $vp_data := split $.spec ":" }}
|
||||
{{- $vp_port := index $vp_data 0 }}
|
||||
{{- $vp_path := "/" }}
|
||||
{{- $vp_dest := "" }}
|
||||
{{- if (gt (len $vp_data) 1) }}
|
||||
{{- $vp_path = index $vp_data 1 }}
|
||||
{{- if (empty $vp_path) }}
|
||||
{{- $vp_path = "/" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if (gt (len $vp_data) 2) }}
|
||||
{{- $vp_dest = index $vp_data 2 }}
|
||||
{{- end }}
|
||||
{{- $_ := set $ "path" $vp_path }}
|
||||
{{- $_ := set $ "port" $vp_port }}
|
||||
{{- $_ := set $ "dest" $vp_dest }}
|
||||
{{- end }}
|
||||
|
||||
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
|
||||
# scheme used to connect to this server
|
||||
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
|
||||
|
@ -535,27 +566,73 @@ proxy_set_header Proxy "";
|
|||
{{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
|
||||
{{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
|
||||
|
||||
|
||||
{{- $tmp_paths := groupBy $containers "Env.VIRTUAL_PATH" }}
|
||||
{{- $has_virtual_paths := gt (len $tmp_paths) 0}}
|
||||
{{- if not $has_virtual_paths }}
|
||||
{{- $tmp_paths = dict "/" $containers }}
|
||||
{{- $vhost_multiport_syntax := false }}
|
||||
{{- range $vp_spec, $_ := groupBy $containers "Env.VIRTUAL_PORT" }}
|
||||
{{- if (or (gt (len (split $vp_spec ":")) 1) (gt (len (split $vp_spec ",")) 1)) }}
|
||||
{{- $vhost_multiport_syntax = true }}
|
||||
{{- break }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- $paths := dict }}
|
||||
{{- if $vhost_multiport_syntax }}
|
||||
{{- range $vp_spec, $containers := groupByMulti $containers "Env.VIRTUAL_PORT" "," }}
|
||||
{{- $vp_spec = trim $vp_spec }}
|
||||
{{- if not $vp_spec }}
|
||||
{{- continue }}
|
||||
{{- end }}
|
||||
{{- $args := dict "spec" $vp_spec }}
|
||||
{{- template "parse_multiport_syntax_port_spec" $args }}
|
||||
{{- $vp_path := $args.path }}
|
||||
{{- $vp_port := $args.port }}
|
||||
{{- $vp_dest := $args.dest }}
|
||||
|
||||
{{- range $path, $containers := $tmp_paths }}
|
||||
{{- $args := dict "Containers" $containers "Path" $path "Upstream_name" $upstream_name "Has_virtual_paths" $has_virtual_paths }}
|
||||
{{- template "get_path_info" $args }}
|
||||
{{- $_ := set $paths $path (dict
|
||||
"ports" (dict "legacy" $containers)
|
||||
"dest" $args.dest
|
||||
"proto" $args.proto
|
||||
"network_tag" $args.network_tag
|
||||
"upstream" $args.upstream
|
||||
"loadbalance" $args.loadbalance
|
||||
"keepalive" $args.keepalive
|
||||
) }}
|
||||
{{- $path_data := when (hasKey $paths $vp_path) (get $paths $vp_path) (dict) }}
|
||||
{{- $path_ports := when (hasKey $path_data "ports") (get $path_data "ports") (dict) }}
|
||||
{{- $path_port_containers := when (hasKey $path_ports $vp_port) (get $path_ports $vp_port) (list) }}
|
||||
{{- $path_port_containers = concat $path_port_containers $containers }}
|
||||
|
||||
{{- $_ := set $path_ports $vp_port $path_port_containers }}
|
||||
{{- $_ := set $path_data "ports" $path_ports }}
|
||||
{{- if (not (hasKey $path_data "dest")) }}
|
||||
{{- $_ := set $path_data "dest" $vp_dest }}
|
||||
{{- end }}
|
||||
{{- $_ := set $paths $vp_path $path_data }}
|
||||
{{- end }}
|
||||
{{- range $path, $path_data := $paths }}
|
||||
{{- $path_containers := list }}
|
||||
{{- range $port, $port_containers := $path_data.ports }}
|
||||
{{ $path_containers = concat $path_containers $port_containers }}
|
||||
{{- end }}
|
||||
{{- $args := dict "Containers" $path_containers "Path" $path "Upstream_name" $upstream_name "Has_virtual_paths" false "Multiport_syntax" true }}
|
||||
{{- template "get_path_info" $args }}
|
||||
{{- $_ := set $path_data "proto" $args.proto }}
|
||||
{{- $_ := set $path_data "network_tag" $args.network_tag }}
|
||||
{{- $_ := set $path_data "upstream" $args.upstream }}
|
||||
{{- $_ := set $path_data "loadbalance" $args.loadbalance }}
|
||||
{{- $_ := set $path_data "keepalive" $args.keepalive }}
|
||||
{{- $_ := set $paths $path $path_data }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- $tmp_paths := groupBy $containers "Env.VIRTUAL_PATH" }}
|
||||
{{- $has_virtual_paths := gt (len $tmp_paths) 0}}
|
||||
{{- if not $has_virtual_paths }}
|
||||
{{- $tmp_paths = dict "/" $containers }}
|
||||
{{- end }}
|
||||
|
||||
{{- range $path, $containers := $tmp_paths }}
|
||||
{{- $args := dict "Containers" $containers "Path" $path "Upstream_name" $upstream_name "Has_virtual_paths" $has_virtual_paths "Multiport_syntax" false }}
|
||||
{{- template "get_path_info" $args }}
|
||||
{{- $_ := set $paths $path (dict
|
||||
"ports" (dict "legacy" $containers)
|
||||
"dest" (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "")
|
||||
"proto" $args.proto
|
||||
"network_tag" $args.network_tag
|
||||
"upstream" $args.upstream
|
||||
"loadbalance" $args.loadbalance
|
||||
"keepalive" $args.keepalive
|
||||
) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- $_ := set $globals.vhosts $hostname (dict
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
backoff==2.2.1
|
||||
docker==7.0.0
|
||||
pytest==8.1.0
|
||||
pytest==8.1.1
|
||||
requests==2.31.0
|
||||
|
|
|
@ -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,31 @@
|
|||
version: "2"
|
||||
|
||||
services:
|
||||
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"
|
|
@ -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
|
|
@ -0,0 +1,20 @@
|
|||
version: "2"
|
||||
|
||||
services:
|
||||
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