1
0
Fork 0
mirror of https://github.com/nginx-proxy/nginx-proxy synced 2024-05-09 17:16:09 +02:00

Compare commits

...

7 Commits

Author SHA1 Message Date
pini-gh 23dcf3105c
Merge bd1aeda540 into 94fb8459cd 2024-03-12 20:55:59 +00:00
Gilles Filippini bd1aeda540 Add multiport syntax for variable VIRTUAL_PORT
Syntax:
  VIRTUAL_PORT = [ <virtual_port> | <multiport> ];
  multiport    = port, { ",",  port };
  port         = <virtual_port> [ ":", <virtual_path> [ ":", <virtual_dest> ]];

Example with multiport syntax:
  VIRTUAL_HOST: "multiport.example.com"
  VIRTUAL_PORT: "9220:~ ^/(admin|fonts?|images|webmin)/,10901,20901:/ws2p,30901:/gva/playground"

Produces:
  # multiport.example.com:10901
  upstream multiport.example.com-10901 {
      # Exposed ports: [{   10901  tcp } {   20901  tcp } {   30901  tcp } {   9220  tcp }]
      # Default virtual port: 80
      # VIRTUAL_PORT: 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 {
      # Exposed ports: [{   10901  tcp } {   20901  tcp } {   30901  tcp } {   9220  tcp }]
      # Default virtual port: 80
      # VIRTUAL_PORT: 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 {
      # Exposed ports: [{   10901  tcp } {   20901  tcp } {   30901  tcp } {   9220  tcp }]
      # Default virtual port: 80
      # VIRTUAL_PORT: 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 {
      # Exposed ports: [{   10901  tcp } {   20901  tcp } {   30901  tcp } {   9220  tcp }]
      # Default virtual port: 80
      # VIRTUAL_PORT: 9220
      ## Can be connected with "docker-gen-bridge" network
      # blah
      server 172.29.0.5:9220;
  }
  server {
      server_name multiport.example.com;
      access_log /var/log/nginx/access.log vhost;
      listen 80 ;
      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;
      }
  }

This feature is discussed in that upstream issue:
https://github.com/nginx-proxy/nginx-proxy/issues/1504
2024-03-12 21:40:50 +01:00
Nicolas Duchon 94fb8459cd
Merge pull request #2410 from pini-gh/pini-refactor-template-3
refactor: add 'ports' dict to the data structure
2024-03-12 21:35:11 +01:00
Nicolas Duchon c7bf75609b
Merge pull request #2411 from nginx-proxy/dependabot/pip/test/requirements/pytest-8.1.1
ci: bump pytest from 8.1.0 to 8.1.1 in /test/requirements
2024-03-12 21:12:40 +01:00
Nicolas Duchon 3c3b3675c1
Merge pull request #2413 from Huge/patch-1
Update README.md to use 1.5 version and link DockerHub
2024-03-12 21:11:50 +01:00
Huge 16b84ea1b5
Update README.md to use 1.5 version and link DockerHub 2024-03-12 14:23:56 +01:00
dependabot[bot] 6441daf25b
ci: bump pytest from 8.1.0 to 8.1.1 in /test/requirements
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.1.0 to 8.1.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.1.0...8.1.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 04:19:22 +00:00
9 changed files with 273 additions and 22 deletions

View File

@ -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

View File

@ -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).

View File

@ -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

View File

@ -1,4 +1,4 @@
backoff==2.2.1
docker==7.0.0
pytest==8.1.0
pytest==8.1.1
requests==2.31.0

View File

@ -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

View File

@ -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"

View 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

View File

@ -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

View File

@ -0,0 +1 @@
rewrite ^/83/(.*)$ /$1 break;