mirror of
https://github.com/thib8956/nginx-proxy
synced 2025-02-23 17:28:14 +00:00
Merge pull request #2499 from nginx-proxy/ipv6
feat: basic implementation of IPv6 for IPv6 docker networks
This commit is contained in:
commit
b6c8851794
9
.github/workflows/test.yml
vendored
9
.github/workflows/test.yml
vendored
@ -36,6 +36,15 @@ jobs:
|
||||
pip install -r python-requirements.txt
|
||||
working-directory: test/requirements
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Pull nginx:alpine image
|
||||
run: docker pull nginx:alpine
|
||||
|
||||
- name: Build Docker web server image
|
||||
run: make build-webserver
|
||||
|
||||
|
@ -618,7 +618,19 @@ If the default certificate is also missing, nginx-proxy will:
|
||||
|
||||
## IPv6 Support
|
||||
|
||||
You can activate the IPv6 support for the nginx-proxy container by passing the value `true` to the `ENABLE_IPV6` environment variable:
|
||||
### IPv6 Docker Networks
|
||||
|
||||
nginx-proxy support both IPv4 and IPv6 on Docker networks.
|
||||
|
||||
By default nginx-proxy will prefer IPv4: if a container can be reached over both IPv4 and IPv6, only its IPv4 will be used.
|
||||
|
||||
This can be changed globally by setting the environment variable `PREFER_IPV6_NETWORK` to `true` on the proxy container: with this setting the proxy will only use IPv6 for containers that can be reached over both IPv4 and IPv6.
|
||||
|
||||
IPv4 and IPv6 are never both used at the same time on containers that use both IP stacks to avoid artificially inflating the effective round robin weight of those containers.
|
||||
|
||||
### Listening on IPv6
|
||||
|
||||
By default the nginx-proxy container will only listen on IPv4. To enable listening on IPv6 too, set the `ENABLE_IPV6` environment variable to `true`:
|
||||
|
||||
```console
|
||||
docker run -d -p 80:80 -e ENABLE_IPV6=true -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy
|
||||
|
43
nginx.tmpl
43
nginx.tmpl
@ -23,6 +23,7 @@
|
||||
{{- $_ := set $config "trust_downstream_proxy" ($globals.Env.TRUST_DOWNSTREAM_PROXY | default "true" | parseBool) }}
|
||||
{{- $_ := set $config "enable_access_log" ($globals.Env.DISABLE_ACCESS_LOGS | default "false" | parseBool | not) }}
|
||||
{{- $_ := set $config "enable_ipv6" ($globals.Env.ENABLE_IPV6 | default "false" | parseBool) }}
|
||||
{{- $_ := set $config "prefer_ipv6_network" ($globals.Env.PREFER_IPV6_NETWORK | default "false" | parseBool) }}
|
||||
{{- $_ := set $config "ssl_policy" ($globals.Env.SSL_POLICY | default "Mozilla-Intermediate") }}
|
||||
{{- $_ := set $config "enable_debug_endpoint" ($globals.Env.DEBUG_ENDPOINT | default "false") }}
|
||||
{{- $_ := set $config "hsts" ($globals.Env.HSTS | default "max-age=31536000") }}
|
||||
@ -76,7 +77,8 @@
|
||||
* The return value will be added to the dot dict with key "ip".
|
||||
*/}}
|
||||
{{- define "container_ip" }}
|
||||
{{- $ip := "" }}
|
||||
{{- $ipv4 := "" }}
|
||||
{{- $ipv6 := "" }}
|
||||
# networks:
|
||||
{{- range sortObjectsByKeysAsc $.container.Networks "Name" }}
|
||||
{{- /*
|
||||
@ -91,17 +93,17 @@
|
||||
{{- /* Handle containers in host nework mode */}}
|
||||
{{- if (index $.globals.networks "host") }}
|
||||
# both container and proxy are in host network mode, using localhost IP
|
||||
{{- $ip = "127.0.0.1" }}
|
||||
{{- $ipv4 = "127.0.0.1" }}
|
||||
{{- continue }}
|
||||
{{- end }}
|
||||
{{- range sortObjectsByKeysAsc $.globals.CurrentContainer.Networks "Name" }}
|
||||
{{- if and . .Gateway (not .Internal) }}
|
||||
# container is in host network mode, using {{ .Name }} gateway IP
|
||||
{{- $ip = .Gateway }}
|
||||
{{- $ipv4 = .Gateway }}
|
||||
{{- break }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $ip }}
|
||||
{{- if $ipv4 }}
|
||||
{{- continue }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@ -111,26 +113,41 @@
|
||||
{{- end }}
|
||||
{{- /*
|
||||
* Do not emit multiple `server` directives for this container if it
|
||||
* is reachable over multiple networks. This avoids accidentally
|
||||
* inflating the effective round-robin weight of a server due to the
|
||||
* redundant upstream addresses that nginx sees as belonging to
|
||||
* is reachable over multiple networks or multiple IP stacks. This avoids
|
||||
* accidentally inflating the effective round-robin weight of a server due
|
||||
* to the redundant upstream addresses that nginx sees as belonging to
|
||||
* distinct servers.
|
||||
*/}}
|
||||
{{- if $ip }}
|
||||
{{- if or $ipv4 $ipv6 }}
|
||||
# {{ .Name }} (ignored; reachable but redundant)
|
||||
{{- continue }}
|
||||
{{- end }}
|
||||
# {{ .Name }} (reachable)
|
||||
{{- if and . .IP }}
|
||||
{{- $ip = .IP }}
|
||||
{{- else }}
|
||||
# /!\ No IP for this network!
|
||||
{{- $ipv4 = .IP }}
|
||||
{{- end }}
|
||||
{{- if and . .GlobalIPv6Address }}
|
||||
{{- $ipv6 = .GlobalIPv6Address }}
|
||||
{{- end }}
|
||||
{{- if and (empty $ipv4) (empty $ipv6) }}
|
||||
# /!\ No IPv4 or IPv6 for this network!
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
# (none)
|
||||
{{- end }}
|
||||
# IP address: {{ if $ip }}{{ $ip }}{{ else }}(none usable){{ end }}
|
||||
{{- $_ := set $ "ip" $ip }}
|
||||
{{ if and $ipv6 $.globals.config.prefer_ipv6_network }}
|
||||
# IPv4 address: {{ if $ipv4 }}{{ $ipv4 }} (ignored; reachable but IPv6 prefered){{ else }}(none usable){{ end }}
|
||||
# IPv6 address: {{ $ipv6 }}
|
||||
{{- $_ := set $ "ip" (printf "[%s]" $ipv6) }}
|
||||
{{- else }}
|
||||
# IPv4 address: {{ if $ipv4 }}{{ $ipv4 }}{{ else }}(none usable){{ end }}
|
||||
# IPv6 address: {{ if $ipv6 }}{{ $ipv6 }}{{ if $ipv4 }} (ignored; reachable but IPv4 prefered){{ end }}{{ else }}(none usable){{ end }}
|
||||
{{- if $ipv4 }}
|
||||
{{- $_ := set $ "ip" $ipv4 }}
|
||||
{{- else if $ipv6}}
|
||||
{{- $_ := set $ "ip" (printf "[%s]" $ipv6) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- /*
|
||||
|
19
test/test_ipv6/test_ipv6_prefer_ipv4_network.py
Normal file
19
test/test_ipv6/test_ipv6_prefer_ipv4_network.py
Normal file
@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def test_forwards_to_ipv4_only_network(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://ipv4only.nginx-proxy.tld/port")
|
||||
assert r.status_code == 200
|
||||
assert r.text == "answer from port 80\n"
|
||||
|
||||
|
||||
def test_forwards_to_dualstack_network(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://dualstack.nginx-proxy.tld")
|
||||
assert r.status_code == 200
|
||||
assert "Welcome to nginx!" in r.text
|
||||
|
||||
|
||||
def test_dualstack_network_prefer_ipv4_config(docker_compose, nginxproxy):
|
||||
conf = nginxproxy.get_conf().decode('ASCII')
|
||||
assert "IPv6 address: fd00:cafe:face:feed::2 (ignored; reachable but IPv4 prefered)" in conf
|
||||
assert "server 172.16.20.2:80;" in conf
|
45
test/test_ipv6/test_ipv6_prefer_ipv4_network.yml
Normal file
45
test/test_ipv6/test_ipv6_prefer_ipv4_network.yml
Normal file
@ -0,0 +1,45 @@
|
||||
version: "2"
|
||||
|
||||
networks:
|
||||
ipv4net:
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.16.10.0/24
|
||||
dualstacknet:
|
||||
enable_ipv6: true
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.16.20.0/24
|
||||
- subnet: fd00:cafe:face:feed::/64
|
||||
|
||||
services:
|
||||
ipv4only:
|
||||
image: web
|
||||
expose:
|
||||
- "80"
|
||||
environment:
|
||||
WEB_PORTS: 80
|
||||
VIRTUAL_HOST: ipv4only.nginx-proxy.tld
|
||||
networks:
|
||||
ipv4net:
|
||||
ipv4_address: 172.16.10.2
|
||||
|
||||
dualstack:
|
||||
image: nginx:alpine
|
||||
environment:
|
||||
VIRTUAL_HOST: dualstack.nginx-proxy.tld
|
||||
networks:
|
||||
dualstacknet:
|
||||
ipv4_address: 172.16.20.2
|
||||
ipv6_address: fd00:cafe:face:feed::2
|
||||
|
||||
sut:
|
||||
image: nginxproxy/nginx-proxy:test
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
networks:
|
||||
ipv4net:
|
||||
ipv4_address: 172.16.10.3
|
||||
dualstacknet:
|
||||
ipv4_address: 172.16.20.3
|
||||
ipv6_address: fd00:cafe:face:feed::3
|
19
test/test_ipv6/test_ipv6_prefer_ipv6_network.py
Normal file
19
test/test_ipv6/test_ipv6_prefer_ipv6_network.py
Normal file
@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def test_forwards_to_ipv4_only_network(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://ipv4only.nginx-proxy.tld/port")
|
||||
assert r.status_code == 200
|
||||
assert r.text == "answer from port 80\n"
|
||||
|
||||
|
||||
def test_forwards_to_dualstack_network(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://dualstack.nginx-proxy.tld")
|
||||
assert r.status_code == 200
|
||||
assert "Welcome to nginx!" in r.text
|
||||
|
||||
|
||||
def test_dualstack_network_prefer_ipv6_config(docker_compose, nginxproxy):
|
||||
conf = nginxproxy.get_conf().decode('ASCII')
|
||||
assert "IPv4 address: 172.16.20.2 (ignored; reachable but IPv6 prefered)" in conf
|
||||
assert "server [fd00:cafe:face:feed::2]:80;" in conf
|
47
test/test_ipv6/test_ipv6_prefer_ipv6_network.yml
Normal file
47
test/test_ipv6/test_ipv6_prefer_ipv6_network.yml
Normal file
@ -0,0 +1,47 @@
|
||||
version: "2"
|
||||
|
||||
networks:
|
||||
ipv4net:
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.16.10.0/24
|
||||
dualstacknet:
|
||||
enable_ipv6: true
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.16.20.0/24
|
||||
- subnet: fd00:cafe:face:feed::/64
|
||||
|
||||
services:
|
||||
ipv4only:
|
||||
image: web
|
||||
expose:
|
||||
- "80"
|
||||
environment:
|
||||
WEB_PORTS: 80
|
||||
VIRTUAL_HOST: ipv4only.nginx-proxy.tld
|
||||
networks:
|
||||
ipv4net:
|
||||
ipv4_address: 172.16.10.2
|
||||
|
||||
dualstack:
|
||||
image: nginx:alpine
|
||||
environment:
|
||||
VIRTUAL_HOST: dualstack.nginx-proxy.tld
|
||||
networks:
|
||||
dualstacknet:
|
||||
ipv4_address: 172.16.20.2
|
||||
ipv6_address: fd00:cafe:face:feed::2
|
||||
|
||||
sut:
|
||||
image: nginxproxy/nginx-proxy:test
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
environment:
|
||||
PREFER_IPV6_NETWORK: "true"
|
||||
networks:
|
||||
ipv4net:
|
||||
ipv4_address: 172.16.10.3
|
||||
dualstacknet:
|
||||
ipv4_address: 172.16.20.3
|
||||
ipv6_address: fd00:cafe:face:feed::3
|
Loading…
x
Reference in New Issue
Block a user