mirror of
				https://github.com/thib8956/nginx-proxy
				synced 2025-11-03 18:49:20 +00:00 
			
		
		
		
	Merge pull request #2558 from nginx-proxy/fix/wildcard-cert-select
fix: wildcard certificates should only work one level deep
This commit is contained in:
		@@ -403,7 +403,7 @@ docker run --detach \
 | 
			
		||||
 | 
			
		||||
## SSL Support
 | 
			
		||||
 | 
			
		||||
SSL is supported using single host, wildcard and SNI certificates using naming conventions for certificates or optionally specifying a cert name (for SNI) as an environment variable.
 | 
			
		||||
SSL is supported using single host, wildcard and SAN certificates using naming conventions for certificates or optionally [specifying a cert name as an environment variable](#san-certificates).
 | 
			
		||||
 | 
			
		||||
To enable SSL:
 | 
			
		||||
 | 
			
		||||
@@ -444,9 +444,11 @@ docker run -e DHPARAM_SKIP=true ....
 | 
			
		||||
 | 
			
		||||
### Wildcard Certificates
 | 
			
		||||
 | 
			
		||||
Wildcard certificates and keys should be named after the domain name with a `.crt` and `.key` extension. For example `VIRTUAL_HOST=foo.bar.com` would use cert name `bar.com.crt` and `bar.com.key`.
 | 
			
		||||
Wildcard certificates and keys should be named after the parent domain name with a `.crt` and `.key` extension. For example:
 | 
			
		||||
- `VIRTUAL_HOST=foo.bar.com` would use cert name `bar.com.crt` and `bar.com.key` if `foo.bar.com.crt` and `foo.bar.com.key` are not available
 | 
			
		||||
- `VIRTUAL_HOST=sub.foo.bar.com` use cert name `foo.bar.com.crt` and `foo.bar.com.key` if `sub.foo.bar.com.crt` and `sub.foo.bar.com.key` are not available, but won't use `bar.com.crt` and `bar.com.key`.
 | 
			
		||||
 | 
			
		||||
### SNI
 | 
			
		||||
### SAN Certificates
 | 
			
		||||
 | 
			
		||||
If your certificate(s) supports multiple domain names, you can start a container with `CERT_NAME=<name>` to identify the certificate to be used. For example, a certificate for `*.foo.com` and `*.bar.com` could be named `shared.crt` and `shared.key`. A container running with `VIRTUAL_HOST=foo.bar.com` and `CERT_NAME=shared` will then use this shared cert.
 | 
			
		||||
 | 
			
		||||
@@ -614,6 +616,21 @@ If the default certificate is also missing, nginx-proxy will:
 | 
			
		||||
> [!NOTE]
 | 
			
		||||
> Prior to version `1.7`, nginx-proxy never trusted the default certificate: when the default certificate was present, virtual hosts that did not have a usable per-virtual-host cert used the default cert but always returned a 500 error over HTTPS. If you want to restore this behaviour, you can do it globally by setting the enviroment variable `TRUST_DEFAULT_CERT` to `false` on the proxy container, or per-virtual-host by setting the label `com.github.nginx-proxy.nginx-proxy.trust-default-cert`to `false` on a proxied container.
 | 
			
		||||
 | 
			
		||||
### Certificate selection
 | 
			
		||||
 | 
			
		||||
Summarizing all the above informations, nginx-proxy will select the certificate for a given virtual host using the following sequence:
 | 
			
		||||
1. if `CERT_NAME` is used, nginx-proxy will use the corresponding certificate if it exists (eg `foor.bar.com` → `CERT_NAME.crt`), or disable HTTPS for this virtual host if it does not. See [SAN certificates](#san-certificates).
 | 
			
		||||
2. if a certificate exactly matching the virtual host hostname exist, nginx-proxy will use it (eg `foo.bar.com` → `foo.bar.com.crt`).
 | 
			
		||||
3. if the virtual host hostname is a subdomain (eg `foo.bar.com` but not `bar.com`) and a certificate exactly matching its parent domain exist  , nginx-proxy will use it (eg `foor.bar.com` → `bar.com.crt`). See [wildcard certificates](#wildcard-certificates).
 | 
			
		||||
4. if the default certificate (`default.crt`) exist and is trusted, nginx-proxy will use it (eg `foor.bar.com` → `default.crt`). See [default and missing certificate](#default-and-missing-certificate).
 | 
			
		||||
5. if the default certificate does not exist or isn't trusted, nginx-proxy will disable HTTPS for this virtual host (eg `foor.bar.com` → no HTTPS).
 | 
			
		||||
 | 
			
		||||
> [!IMPORTANT]
 | 
			
		||||
>  Using `CERT_NAME` take precedence over the certificate selection process, meaning nginx-proxy will not auto select a correct certificate in step 2 trough 5 if `CERT_NAME` was used with an incorrect value or without corresponding certificate.
 | 
			
		||||
 | 
			
		||||
> [!NOTE]
 | 
			
		||||
> In all the above cases, if a private key file corresponding to the selected certificate (eg `foo.bar.com.key` for the `foor.bar.com.crt` certificate) does not exist, HTTPS will be disabled for this virtual host.
 | 
			
		||||
 | 
			
		||||
⬆️ [back to table of contents](#table-of-contents)
 | 
			
		||||
 | 
			
		||||
## IPv6 Support
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								nginx.tmpl
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								nginx.tmpl
									
									
									
									
									
								
							@@ -688,13 +688,25 @@ proxy_set_header Proxy "";
 | 
			
		||||
        {{ $vhost_containers = concat $vhost_containers $vpath_containers }}
 | 
			
		||||
    {{- end }}
 | 
			
		||||
 | 
			
		||||
    {{- $certName := groupByKeys $vhost_containers "Env.CERT_NAME" | first }}
 | 
			
		||||
    {{- $vhostCert := closest (dir "/etc/nginx/certs") (printf "%s.crt" $hostname) }}
 | 
			
		||||
    {{- $vhostCert = trimSuffix ".crt" $vhostCert }}
 | 
			
		||||
    {{- $vhostCert = trimSuffix ".key" $vhostCert }}
 | 
			
		||||
    {{- $userIdentifiedCert := groupByKeys $vhost_containers "Env.CERT_NAME" | first }}
 | 
			
		||||
    
 | 
			
		||||
    {{- $vhostCert := "" }}
 | 
			
		||||
    {{- if exists (printf "/etc/nginx/certs/%s.crt" $hostname) }}
 | 
			
		||||
        {{- $vhostCert = $hostname }}
 | 
			
		||||
    {{- end }}
 | 
			
		||||
 | 
			
		||||
    {{- $parentVhostCert := "" }}
 | 
			
		||||
    {{- if gt ($hostname | sprigSplit "." | len) 2 }}
 | 
			
		||||
        {{- $parentHostname := ($hostname | sprigSplitn "." 2)._1 }}
 | 
			
		||||
        {{- if exists (printf "/etc/nginx/certs/%s.crt" $parentHostname) }}
 | 
			
		||||
            {{- $parentVhostCert = $parentHostname }}
 | 
			
		||||
        {{- end }}
 | 
			
		||||
    {{- end }}
 | 
			
		||||
    
 | 
			
		||||
    {{- $trust_default_cert := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.trust-default-cert" | keys | first | default $globals.config.trust_default_cert | parseBool }}
 | 
			
		||||
    {{- $cert := and $trust_default_cert $globals.config.default_cert_ok | ternary "default" "" }}
 | 
			
		||||
    {{- $cert = or $certName $vhostCert $cert }}
 | 
			
		||||
    {{- $defaultCert := and $trust_default_cert $globals.config.default_cert_ok | ternary "default" "" }}
 | 
			
		||||
    
 | 
			
		||||
    {{- $cert := or $userIdentifiedCert $vhostCert $parentVhostCert $defaultCert }}
 | 
			
		||||
    {{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }}
 | 
			
		||||
 | 
			
		||||
    {{- $enable_debug_endpoint := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.debug-endpoint" | keys | first | default $globals.config.enable_debug_endpoint | parseBool }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								test/test_ssl/test_cert_selection.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								test/test_ssl/test_cert_selection.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import json
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize("host,expected_cert_ok,expected_cert", [
 | 
			
		||||
    ("nginx-proxy.tld", True, "nginx-proxy.tld"),
 | 
			
		||||
    ("web1.nginx-proxy.tld", True, "nginx-proxy.tld"),
 | 
			
		||||
    ("sub.web1.nginx-proxy.tld", False, ""),
 | 
			
		||||
    ("web2.nginx-proxy.tld", True, "web2.nginx-proxy.tld"),
 | 
			
		||||
])
 | 
			
		||||
def test_certificate_selection(
 | 
			
		||||
    docker_compose,
 | 
			
		||||
    nginxproxy,
 | 
			
		||||
    host: str,
 | 
			
		||||
    expected_cert_ok: bool,
 | 
			
		||||
    expected_cert: str,
 | 
			
		||||
):
 | 
			
		||||
    r = nginxproxy.get(f"http://{host}/nginx-proxy-debug")
 | 
			
		||||
    assert r.status_code == 200
 | 
			
		||||
    try:
 | 
			
		||||
        jsonResponse = json.loads(r.text)
 | 
			
		||||
    except ValueError as err:
 | 
			
		||||
        pytest.fail("Failed to parse debug endpoint response as JSON:: %s" % err, pytrace=False)
 | 
			
		||||
    assert jsonResponse["vhost"]["cert_ok"] == expected_cert_ok
 | 
			
		||||
    assert jsonResponse["vhost"]["cert"] == expected_cert
 | 
			
		||||
							
								
								
									
										33
									
								
								test/test_ssl/test_cert_selection.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								test/test_ssl/test_cert_selection.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
services:
 | 
			
		||||
  base:
 | 
			
		||||
      image: web
 | 
			
		||||
      environment:
 | 
			
		||||
        WEB_PORTS: "80"
 | 
			
		||||
        VIRTUAL_HOST: "nginx-proxy.tld"
 | 
			
		||||
 | 
			
		||||
  web1:
 | 
			
		||||
      image: web
 | 
			
		||||
      environment:
 | 
			
		||||
        WEB_PORTS: "80"
 | 
			
		||||
        VIRTUAL_HOST: "web1.nginx-proxy.tld"
 | 
			
		||||
  
 | 
			
		||||
  sub-web1:
 | 
			
		||||
      image: web
 | 
			
		||||
      environment:
 | 
			
		||||
        WEB_PORTS: "80"
 | 
			
		||||
        VIRTUAL_HOST: "sub.web1.nginx-proxy.tld"
 | 
			
		||||
 | 
			
		||||
  web2:
 | 
			
		||||
      image: web
 | 
			
		||||
      environment:
 | 
			
		||||
        WEB_PORTS: "80"
 | 
			
		||||
        VIRTUAL_HOST: "web2.nginx-proxy.tld"
 | 
			
		||||
 | 
			
		||||
  sut:
 | 
			
		||||
    image: nginxproxy/nginx-proxy:test
 | 
			
		||||
    volumes:
 | 
			
		||||
      - /var/run/docker.sock:/tmp/docker.sock:ro
 | 
			
		||||
      - ./certs:/etc/nginx/certs:ro
 | 
			
		||||
      - ./acme_root:/usr/share/nginx/html:ro
 | 
			
		||||
    environment:
 | 
			
		||||
      DEBUG_ENDPOINT: "true"
 | 
			
		||||
		Reference in New Issue
	
	Block a user