Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added VIRTUAL_HOST_ALIAS support #1369

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

dannycarrera
Copy link

This feature adds support for VIRTUAL_HOST_ALIAS and is in response to issues #180, #958 and #1204.

You can add aliases that will redirect (301) to the first entry in VIRTUAL_HOST by adding the VIRTUAL_HOST_ALIAS env var:

$ docker run -e VIRTUAL_HOST=example.com -e VIRTUAL_HOST_ALIAS=www.example.com,old.example.com ...

This will setup the following redirects:

  • http://www.example.comhttp://example.com
  • http://old.example.comhttp://example.com

If you are using letsencrypt-nginx-proxy-companion for SSL support, then you would run:

$ docker run    -e VIRTUAL_HOST=example.com \
                -e VIRTUAL_HOST_ALIAS=www.example.com,old.example.com
                -e LETSENCRYPT_HOST=example.com,www.example.com,old.example.com
                ...

This will setup the following redirects:

  • http://example.comhttps://example.com
  • http://www.example.comhttps://example.com
  • http://old.example.comhttps://example.com
  • https://www.example.comhttps://example.com
  • https://old.example.comhttps://example.com

@radoslav-stefanov
Copy link

Do you guys gave any plans to merge this? I would like to use it, but without having to build my own image.

@dannycarrera
Copy link
Author

Do you guys gave any plans to merge this? I would like to use it, but without having to build my own image.

@radoslav-stefanov you can use dannycarrera/nginx-proxy until this gets merged

@Brammm
Copy link

Brammm commented Feb 18, 2020

Very interested in seeing this merged as well.

@sgabe
Copy link
Contributor

sgabe commented Feb 18, 2020

I think this PR should be updated according to #1338.

cauethenorio added a commit to cauethenorio/nginx-gateway that referenced this pull request Mar 12, 2020
nginx.tmpl Outdated
{{ end }}
access_log /var/log/nginx/access.log vhost;
return 301 https://{{ $first_host }}$request_uri;
}
Copy link

@aiomaster aiomaster May 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the http and the https server blocks can be merged into a single one.
Besides that it would be good to support the new variables fo http and https port. (rebase to current master to get these)
The new block could look something like that:

server {
	server_name {{ $host_alias }};
        {{ if eq $https_method "redirect" }}
        listen {{ $external_http_port }} {{ $default_server }};
        {{ end }}
	listen {{ $external_https_port }} ssl http2 {{ $default_server }};
	{{ if $enable_ipv6 }}
        {{ if eq $https_method "redirect" }}
        listen [::]:{{ $external_http_port }} {{ $default_server }};
        {{ end }}
	listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }};
	{{ end }}
	access_log /var/log/nginx/access.log vhost;
# ... (rest of the ssl block)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @aiomaster . I've merged the 2 blocks together.

Do you also suggest replacing all other instances where http and https hardcoded ports with the new variables?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the ports 80 and 443 should be replaced with the corresponding variables everywhere. My snippet was just an example and not a complete full review. Thanks for updating this.

@jheinitz
Copy link

jheinitz commented Jun 2, 2020

Hi,

nice feature. Does it also work when I have multiple entries in VIRTUAL_HOST like

VIRTUAL_HOST=app1.domain1.tld,app1.domain2.tld

This is how I use it usually because my DNS for the domains point to the same IP and I combine more names in on VIRTUAL_HOST.

Cheers

Jens

@dannycarrera
Copy link
Author

@jheinitz yes, it works with multiple entries separated by a comma.

@eriktelepovsky
Copy link

Perfect. Any estimate of merging this?

@amcsi
Copy link

amcsi commented Jun 24, 2020

@dannycarrera please see aiomaster's review of your PR 😇

@dannycarrera
Copy link
Author

I think this PR should be updated according to #1338.

Thanks for the suggestion @sgabe.

I added the changes, though I'm not sure how to test it. Any guidance would be appreciated.

@MarkErik
Copy link

MarkErik commented Jul 3, 2020

Hi @dannycarrera thank you for working on this. I am encountering a problem when using the new template file.

I am using a 3 container setup (nginx, docker-gen, and JrCs LE container) , and am using the .tmpl file from:
curl https://raw.githubusercontent.com/dannycarrera/nginx-proxy/feature/virtual_host_alias/nginx.tmpl > nginx.tmpl

My objective is to have the non-www URL redirect to the www URL, with the minimum of redirects, and have that happen at the first entry point (the nginx-proxy).

I have added the following lines to my app docker-compose:
environment:
VIRTUAL_HOST: www.URL.com
VIRTUAL_HOST_ALIAS: URL.com
LETSENCRYPT_HOST: www.URL.com,URL.com
LETSENCRYPT_EMAIL: user@URL.com

When I bring up my app docker-compose, I can see the following errors:

/etc/nginx/certs/www.URL.com /app
Reloading nginx docker-gen (using separate container nginx-gen)...
Reloading nginx (using separate container 01ad98b237a38fd9a708bb45f9446fa2d15e70a545c5d61cef1317ea75024260)...
Creating/renewal www.URL.com certificates... (www.URL.com URL.com)
2020-07-03 15:26:43,713:INFO:simp_le:1450: Generating new certificate private key
2020-07-03 15:26:48,367:ERROR:simp_le:1417: CA marked some of the authorizations as invalid, which likely means it could not access http://example.com/.well-known/acme-challenge/X. Did you set correct path in -d example.com:path or --default_root? Are all your domains accessible from the internet? Please check your domains' DNS entries, your host's network/firewall setup and your webserver config. If a domain's DNS entry has both A and AAAA fields set up, some CAs such as Let's Encrypt will perform the challenge validation over IPv6. If your DNS provider does not answer correctly to CAA records request, Let's Encrypt won't issue a certificate for your domain (see https://letsencrypt.org/docs/caa/). Failing authorizations: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5641949224
Traceback (most recent call last):
File "/usr/lib/python3.8/site-packages/simp_le.py", line 1406, in finalize_order
finalized_order = client.poll_and_finalize(order)
File "/usr/lib/python3.8/site-packages/acme/client.py", line 709, in poll_and_finalize
orderr = self.poll_authorizations(orderr, deadline)
File "/usr/lib/python3.8/site-packages/acme/client.py", line 733, in poll_authorizations
raise errors.ValidationError(failed)
acme.errors.ValidationError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/lib/python3.8/site-packages/simp_le.py", line 1479, in persist_new_data
order = finalize_order(client, order)
File "/usr/lib/python3.8/site-packages/simp_le.py", line 1433, in finalize_order
raise Error('Challenge validation has failed, see error log.')
simp_le.Error: Challenge validation has failed, see error log.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/lib/python3.8/site-packages/simp_le.py", line 1601, in main
return main_with_exceptions(cli_args)
File "/usr/lib/python3.8/site-packages/simp_le.py", line 1585, in main_with_exceptions
persist_new_data(args, existing_data)
File "/usr/lib/python3.8/site-packages/simp_le.py", line 1495, in persist_new_data
persist_data(args, existing_data, new_data=IOPlugin.Data(
File "/usr/lib/python3.8/site-packages/simp_le.py", line 1164, in persist_data
plugin.save(new_data)
File "/usr/lib/python3.8/site-packages/simp_le.py", line 562, in save
key = self.dump_key(data.key)
File "/usr/lib/python3.8/site-packages/simp_le.py", line 460, in dump_key
return OpenSSL.crypto.dump_privatekey(self.typ, data.wrapped).strip()
AttributeError: 'NoneType' object has no attribute 'wrapped'

Unhandled error has happened, traceback is above

Debugging tips: -v improves output verbosity. Help is available under --help.
/app
Sleep for 3600s

@MarkErik
Copy link

MarkErik commented Jul 3, 2020

I hope that adding a few more details can be useful, and diagnose whether the issue is with my config, or a bug in the proposed solution.

I wanted to note that prior to trying the new template file, I was not having problems with the services running on my server, getting certificates. However, none of the services had more than 1 URL.

If I examine my default.conf file that is created, and look at one of the previous services the blocks are as follows:

# comments.URL.com
upstream comments.URL.com {
				## Can be connected with "connect-to-proxy" network
			# ghost-comments
			server 172.19.0.2:8080;
				# Cannot connect to network of this container
				server 127.0.0.1 down;
				# Cannot connect to network of this container
				server 127.0.0.1 down;
				# Cannot connect to network of this container
				server 127.0.0.1 down;
}
server {
	server_name comments.URL.com;
	listen 80 ;
	access_log /var/log/nginx/access.log vhost;
	# Do not HTTPS redirect Let'sEncrypt ACME challenge
	location /.well-known/acme-challenge/ {
		auth_basic off;
		allow all;
		root /usr/share/nginx/html;
		try_files $uri =404;
		break;
	}
	location / {
		return 301 https://$host$request_uri;
	}
}
server {
	server_name comments.URL.com;
	listen 443 ssl http2 ;
	access_log /var/log/nginx/access.log vhost;
	ssl_session_timeout 5m;
	ssl_session_cache shared:SSL:50m;
	ssl_session_tickets off;
	ssl_certificate /etc/nginx/certs/comments.URL.com.crt;
	ssl_certificate_key /etc/nginx/certs/comments.URL.com.key;
	ssl_dhparam /etc/nginx/certs/comments.URL.com.dhparam.pem;
	ssl_stapling on;
	ssl_stapling_verify on;
	ssl_trusted_certificate /etc/nginx/certs/comments.URL.com.chain.pem;
	add_header Strict-Transport-Security "max-age=31536000" always;
	include /etc/nginx/vhost.d/default;
	location / {
		proxy_pass http://comments.URL.com;
	}
}

The blocks that are created using the new tmpl file look quite different (Is that because it encountered errors when the service came up?):

# www.URL.com
upstream www.URL.com {
				## Can be connected with "connect-to-proxy" network
			# nginx-ghost-robot
			server 172.19.0.7:80;
				# Cannot connect to network of this container
				server 127.0.0.1 down;
				# Cannot connect to network of this container
				server 127.0.0.1 down;
				# Cannot connect to network of this container
				server 127.0.0.1 down;
}
server {
	server_name www.URL.com;
	listen 80 ;
	access_log /var/log/nginx/access.log vhost;
	include /etc/nginx/vhost.d/www.URL.com;
	location / {
		proxy_pass http://www.URL.com;
	}
}
server {
	server_name www.URL.com;
	listen 443 ssl http2 ;
	access_log /var/log/nginx/access.log vhost;
	return 500;
	ssl_certificate /etc/nginx/certs/default.crt;
	ssl_certificate_key /etc/nginx/certs/default.key;
}
# VIRTUAL_HOST_ALIAS
# First Host www.URL.com
#Alias:  URL.com
server {
	server_name URL.com;
	listen 80 ;
	access_log /var/log/nginx/access.log vhost;
	include /etc/nginx/vhost.d/default;
	# Do not HTTPS redirect Let'sEncrypt ACME challenge
	location /.well-known/acme-challenge/ {
		auth_basic off;
		allow all;
		root /usr/share/nginx/html;
		try_files $uri =404;
		break;
	}
	location / {
		return 301 https://www.URL.com$request_uri;
	}
}
server {
	server_name URL.com;
	listen 443 ssl http2 ;
	access_log /var/log/nginx/access.log vhost;
	return 500;
	ssl_certificate /etc/nginx/certs/default.crt;
	ssl_certificate_key /etc/nginx/certs/default.key;
}

@dannycarrera
Copy link
Author

dannycarrera commented Jul 5, 2020

@MarkErik unfortunately I'm not an expert with nginx. The only suggestions I have are:

  1. Use a 2 container setup. Replace docker-gen and LetsEncrypt with letsencrypt-nginx-proxy-companion. It's essentially docker-gen and LE combined.
  2. Ensure you have DNS records for both URL.com and www.URL.com pointing to your nginx server and that it's reachable by Let's Encrypt

@MarkErik
Copy link

MarkErik commented Jul 5, 2020

Hi @dannycarrera thank you for your response.

My goal is to have a solution to redirecting URLs with the minimum of redirects, and this seemed like the right approach to handle it at the proxy level. However, in the meantime I have adopted a workaround solution using a second nginx container at the app service level that simply redirects incoming requests to the desired URL.

Regarding your suggestion 1, is that the configuration you tested with? (The two containers for the proxy being a plain Nginx container and the LetsE-Nginx-Proxy-companion)

@sgabe
Copy link
Contributor

sgabe commented Jul 5, 2020

@MarkErik You will need to tweak the template as in #750 to detect the proxy container. The 3 containers setup gives you more flexibility, but also requires more effort. I am using a 3 containers setup with a customized version of an updated template file available at https://github.com/sgabe/nginx-proxy/blob/master/nginx.tmpl which also includes this modification and might help you.

@MarkErik
Copy link

@sgabe thank you for responding and pointing me at the other .tmpl file. I looked it over and I'm worried that since I am not too familiar with the structure of the file, and that as it has some additions, and is also a few commits behind the master, that I would not be able and comfortable to put it into my setup. I'll stick with an additional 'redirect' container for now, and keep an eye on the idea of a new .tmpl file.

@shiz0
Copy link

shiz0 commented Jul 31, 2020

@dannycarrera Thank you for this PR. IMO this is an essential feature if one wants to run this in production.
I merged it into a fork of the current nginx-proxy master and encountered the problem, @MarkErik described above.
Tried to test it as described in
https://github.com/nginx-proxy/docker-letsencrypt-nginx-proxy-companion/blob/master/docs/Invalid-authorizations.md

IIRC:

  • It would return a 404 for the non alias http request, if aliases were set.
  • If the vhost container was started without aliases, it would create the first certs.
  • If it was then restarted with additional aliases, it would also create the others, but afterwards redirect the alias http requests, eventually break renewals.

I'd like to put some minor changes up for discussion:
https://github.com/shiz0/nginx-proxy/commits/virtual_host_alias/nginx.tmpl

I tested this like above and it seemed ok to me.
I have never seen Go or such template before though and my experience with nginx is also somewhat limited,
so I'd appreciate if someone could look into it, as I cannot definitely say that it might not have any side effects anywhere.

@dannycarrera If you like the changes, feel free to please integrate them in your PR, which will hopefully going to be merged sometime.
@MarkErik You can try my file, If you like:
https://github.com/shiz0/nginx-proxy/blob/virtual_host_alias/nginx.tmpl
It's basically the current nginx-proxy master plus the alias stuff and might work for you.

@MarkErik
Copy link

MarkErik commented Aug 6, 2020

@shiz0 I've been using: https://github.com/cusspvz/redirect.docker to do the redirect (I include it as a service in my project's docker-compose file) and it has been sufficient as a solution.

If you are curious here are the Nginx portions of my Ghost blog's docker-compose (the outside network is the one that is shared between the proxy and the containers that need to communicate with it):

  nginx-ghost:
    depends_on: 
      - ghost
    image: nginx:latest
    container_name: nginx-ghost
    restart: unless-stopped
    environment: 
      VIRTUAL_HOST: URL.com
      LETSENCRYPT_HOST: URL.com
      LETSENCRYPT_EMAIL:person@URL.com
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./conf.d:/etc/nginx/conf.d:ro
      - ./nginx-logs:/var/log/nginx
      - ./ghost_content:/var/lib/ghost/content:ro
    networks:
      - default
      - outside

ghost-redirect:
    depends_on:
      - nginx-ghost
    build: ./redirect
    container_name: ghost-redirect
    restart: unless-stopped
    environment:
      VIRTUAL_HOST: www.URL.com
      HTTPS_METHOD: noredirect
      LETSENCRYPT_HOST: www.URL.com
      LETSENCRYPT_EMAIL: person@URL.com
      REDIRECT: https://URL.com
    networks:
      - outside

@shiz0
Copy link

shiz0 commented Feb 11, 2021

@dannycarrera Since this reply has taken me almost two months, I can say I know too well. :-p
Thank you! 👍 :-)
@jwilder Any chance of merging this? Or any specific concerns about these changes? There are already some people running it like this now, but I am sure there'd be people (e.g. me) who would be willing to run specific tests if needed.

@perarg
Copy link

perarg commented Feb 14, 2021

+1 for merging this feature

@buchdag buchdag added kind/feature-request Issue requesting a new feature status/pr-needs-tests This PR needs new or additional test(s) labels Apr 5, 2021
@buchdag buchdag added type/feat PR for a new feature and removed kind/feature-request Issue requesting a new feature labels Apr 29, 2021
@uquerx
Copy link

uquerx commented Jul 8, 2021

Hello All,

Maybe somebody can help me? I also would like to do an redirection but anyway, doesn't work.
I well using letsencrypt-nginx-proxy-companion.
So when I use this option in my docker-compose file it's woks well:

environment:
            - VIRTUAL_HOST=my-domain.ch
            - LETSENCRYPT_HOST=my-domain.ch
            ...

But when I'm trying with VIRUTAL_HOST_ALIAS nothing works:

environment:
            - VIRTUAL_HOST=my-domain.ch
            - VIRTUAL_HOST_ALIAS=www.my-domain.ch,mydomain.ch,www.mydomain.ch
            - LETSENCRYPT_HOST=my-domain.ch,www.my-domain.ch,mydomain.ch,www.mydomain.ch
            ...

With this last config, all my site lose certificate and are not available. Or worst, for mydomain.ch I got an 503 service temporarily unavailable from NGINX

About My DNS, all are an A field with server IP. (no CNAME are use)

Maybe someone has an idea what I'm doing wrong?

Thanks for your help.

@uquerx
Copy link

uquerx commented Jul 8, 2021

Hello All,

Maybe somebody can help me? I also would like to do an redirection but anyway, doesn't work.
I well using letsencrypt-nginx-proxy-companion.
So when I use this option in my docker-compose file it's woks well:

environment:
            - VIRTUAL_HOST=my-domain.ch
            - LETSENCRYPT_HOST=my-domain.ch
            ...

But when I'm trying with VIRUTAL_HOST_ALIAS nothing works:

environment:
            - VIRTUAL_HOST=my-domain.ch
            - VIRTUAL_HOST_ALIAS=www.my-domain.ch,mydomain.ch,www.mydomain.ch
            - LETSENCRYPT_HOST=my-domain.ch,www.my-domain.ch,mydomain.ch,www.mydomain.ch
            ...

With this last config, all my site lose certificate and are not available. Or worst, for mydomain.ch I got an 503 service temporarily unavailable from NGINX

About My DNS, all are an A field with server IP. (no CNAME are use)

Maybe someone has an idea what I'm doing wrong?

Thanks for your help.

I found my issue there:
Edit nginx.tmpl

@daiyam
Copy link

daiyam commented Jul 29, 2021

You should try the version from @shiz0: https://github.com/shiz0/nginx-proxy/blob/virtual_host_alias/nginx.tmpl I don't have those issues with it.

@cynicated
Copy link

@buchdag - This has been open for a while. I believe there is still interest in this feature, but it's held up by lack of tests (based on the pr-needs-tests label). If testing were written, could this get merged?

@buchdag
Copy link
Member

buchdag commented Feb 14, 2022

@cynicated yep, useful features like this one will be merged if they have proper tests and documentation (provided they don't introduce backward incompatible behaviour of course). However the VIRTUAL_PATH feature should be merged soonish and will probably create merge conflicts in this PR.

@ArthegaAsdweri
Copy link

+1. Domain Aliases should be a core feature.

@razielanarki
Copy link

for what it's worth, i'm also +1 ing this pr. domain aliases out-of-the box (without maintainng a priv.fork) would be very useful

@SKeeneCode
Copy link

Is there a dockerhub repo with an image of this MR I can pull from? I pulled this MR myself and ran docker build but the image it produced didn't run on my droplet.

Also +1 this PR. I've tried like, 5 different ways to redirect www traffic to non-www for my domain and none of them have worked so far so hoping this can do it!

@dannycarrera
Copy link
Author

@SKeeneCode hub.docker.com/r/dannycarrera/nginx-proxy

Is there a dockerhub repo with an image of this MR I can pull from?

@paloha
Copy link

paloha commented Nov 28, 2022

Hello @dannycarrera is it nowadays safe to use the dannycarrera/nginx-proxy to get the VIRTUAL_HOST_ALIAS feature, given that the fork is 295 commits behind nginx-proxy:main?

UPDATE: it seems not. We are using jwilder/nginx-proxy with jrcs/letsencrypt-nginx-proxy-companion and when I changed jwilder/nginx-proxy to dannycarrera/nginx-proxy and then did sudo docker-compose up -d --force-recreate, our apps were not reachable anymore.

@shiz0
Copy link

shiz0 commented Nov 29, 2022

@paloha You could try my version, (https://github.com/shiz0/nginx-proxy)
I just updated/synced the fork with this repo, as I also was 90 something commits behind.
I currently do not have a prebuilt image though, so you'd have to build it yourself.
Or you could try jwilder/nginx-proxy with my https://github.com/shiz0/nginx-proxy/blob/master/nginx.tmpl ... I did not track development recently, but last time I checked, that worked very well.

@dannycarrera
Copy link
Author

@paloha I can't promise a date, but I will make some time and implement the necessary tests to get this PR merged. I will also update the temporary forked docker repo until the merge happens. Any direction would be appreciated re: testing and if any other updates are required. @shiz0 @buchdag

@paloha
Copy link

paloha commented Dec 6, 2022

@shiz0 & @dannycarrera thanks a lot for your replies. Before you replied, I already solved it by using the cusspvz/redirect image because I was in a hurry. It works so I am not touching it now. But I am sure people after me will benefit from the updated dockers. Still I think the VIRTUAL_HOST_ALIAS is an important feature to have. Looking forward.

@buchdag buchdag added the scope/host-aliases Issue or PR related to alias redirection label Dec 26, 2022
@buchdag buchdag self-assigned this Dec 26, 2022
@Leny1996
Copy link

@buchdag any chance to merge this? It's almost 4 years

@timheerwagen
Copy link

@paloha You could try my version, (https://github.com/shiz0/nginx-proxy) I just updated/synced the fork with this repo, as I also was 90 something commits behind. I currently do not have a prebuilt image though, so you'd have to build it yourself. Or you could try jwilder/nginx-proxy with my https://github.com/shiz0/nginx-proxy/blob/master/nginx.tmpl ... I did not track development recently, but last time I checked, that worked very well.

Unfortunately, this no longer works with the latest version of nginx-proxy and a few nginx warnings are issued.
For example "listen ... http2" directive is deprecated, use the "http2" directive instead in /etc/nginx/conf.d/default.conf

In my opinion, using another container for redirecting is not a good solution.

I and certainly many others would be very happy if this feature were to become a reality soon. Unfortunately I can't help much, so many thanks in advance!

@shiz0
Copy link

shiz0 commented Feb 7, 2024

Oof.. yeah I did not update my fork for a while and it's like 300 commits behind with lot of conflicts.
I'll try to find time and see if the original changes can be ported into the current version.

# Conflicts:
#	README.md
#	nginx.tmpl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope/host-aliases Issue or PR related to alias redirection status/pr-needs-tests This PR needs new or additional test(s) type/feat PR for a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet