mailcow / mailcow-dockerized

mailcow: dockerized - 🐮 + 🐋 = 💕
https://mailcow.email
GNU General Public License v3.0
8.95k stars 1.17k forks source link

Doc update wilders reverse proxy :-) #2351

Closed GlowManCZ closed 5 years ago

GlowManCZ commented 5 years ago

Hi @andryyy I thought it would not be a bad idea to add the jwilder / nginx-proxy settings documentation. The settings that are in the documentation are not working on 100% If you agree with me, I can write something about it

andryyy commented 5 years ago

The nginx reverse proxy settings are correct.

It is something different to use a dockerized Nginx like jwilders. You cannot copy and paste the docs here.

There are also several things you need to make sure don’t break. Like the unencrypted autoconfig or, if needed, the passing of acme-challenges, setting the correct headers etc.

If you think you didn’t break any of those, a PR is welcome. :)

GlowManCZ commented 5 years ago

I'm already working on the documentation. Should I close the issue and then make a pull request to docs ? Or I have to leave open ?

andryyy commented 5 years ago

Create a PR for us to check it there. :)

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Heziode commented 5 years ago

@GlowManCZ any progress? I am looking for a doc about it because I want to integrate mailcow-dockerized to my existing environment, that uses jwilder/nginx-proxy.

PMK commented 5 years ago

I would like this as well.

Happyfeet01 commented 5 years ago

Would be nice if an documentation exist. :-)

fstangenberger commented 5 years ago

@GlowManCZ any progress? I am looking for a doc about it because I want to integrate mailcow-dockerized to my existing environment, that uses jwilder/nginx-proxy.

@Heziode try this: set in mailcow.conf:

MAILCOW_HOSTNAME=${domain}
[...]
HTTP_PORT=8080
HTTP_BIND=127.0.0.1

HTTPS_PORT=8443
HTTPS_BIND=127.0.0.1
[...]
SKIP_LETS_ENCRYPT=y
[...]

in docker-compose.yml in section nginx-mailcow add in environment:

- HTTPS_METHOD=noredirect
- VIRTUAL_HOST=${domain},autoconfig.${domain},autodiscover.${domain}
- VIRTUAL_PORT=8080
- LETSENCRYPT_HOST=${domain},autoconfig.${domain},autodiscover.${domain}
- LETSENCRYPT_EMAIL=${yourEmailForLE}

and in section nginx-mailcow add in networks:

    mailcow-network:
      aliases:
        - nginx
    proxy-network:

and in section networks add:

networks:
  mailcow-network:
[...]
  proxy-network:
    external:
      name: ${your_nginx-proxy_network_name}
[...]
stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Heziode commented 5 years ago

@fstangenberger

Sorry for the delay.

With your config I can access to mailcow but in HTTP. Not in HTTPS.

fstangenberger commented 5 years ago

@Heziode https is served by nginx-proxy. In mailcow.conf is a hint in section HTTP/S Bindings:

# You should use HTTPS, but in case of SSL offloaded reverse proxies:
HTTP_PORT=8080
HTTP_BIND=127.0.0.1

HTTPS_PORT=8443
HTTPS_BIND=127.0.0.1

In all cases of the configuration an upstream is created by nginx-gen which points to http of mailcowdockerized_nginx-mailcow_1: server 172.19.0.20:8080; (in conf.d/default.conf)

These upstream is used for Port 80 and 443 as proxy_pass. The proxy authorizes the https request and send it via http to mailcow_nginx.

Heziode commented 5 years ago

when I try to acces trough HTTPS, I get an error 500.

fstangenberger commented 5 years ago

when I try to acces trough HTTPS, I get an error 500.

Are there log entries in nginx-proxy log for such a request? It seems, mailcow isn't in the network of nginx-proxy. 'proxy-network' is the name of the network of mailcow and nginx-proxy.

Heziode commented 5 years ago

To be sure about what we talking about, here is the architecture of my ecosystem:

ecosystem

When you talk about "nginx-proxy" do you mean "nginx-mailcow" or my "nginx-proxy" ?

When I try to access trough HTTPS, I see the request on nginx-proxy but it returns 500 here, nginx-mailcow log do not show everything.

When I try to access trough HTTP, I see the request on nginx-proxy and on nginx-mailcow logs (and I get the page).

fstangenberger commented 5 years ago

I mean nginx-proxy from jwilder. Are there an entry in conf.d/default.conf for https?

Heziode commented 5 years ago

The part of default.conf of nginx-proxy related to mailcow are:

# autoconfig.mail.mysite.local
upstream autoconfig.mail.mysite.local {
                                ## Can be connected with "proxy-network" network
                        # mailcowdockerized_nginx-mailcow_1
                        server 172.33.0.2:8080;
                                # Cannot connect to network of this container
                                server 127.0.0.1 down;
}
server {
        server_name autoconfig.mail.mysite.local;
        listen 80 ;
        access_log /var/log/nginx/access.log vhost;
        location / {
                proxy_pass http://autoconfig.mail.mysite.local;
        }
}
server {
        server_name autoconfig.mail.mysite.local;
        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;
}
# autodiscover.mail.mysite.local
upstream autodiscover.mail.mysite.local {
                                ## Can be connected with "proxy-network" network
                        # mailcowdockerized_nginx-mailcow_1
                        server 172.33.0.2:8080;
                                # Cannot connect to network of this container
                                server 127.0.0.1 down;
}
server {
        server_name autodiscover.mail.mysite.local;
        listen 80 ;
        access_log /var/log/nginx/access.log vhost;
        location / {
                proxy_pass http://autodiscover.mail.mysite.local;
        }
}
server {
        server_name autodiscover.mail.mysite.local;
        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;
}
# mail.mysite.local
upstream mail.mysite.local {
                                ## Can be connected with "proxy-network" network
                        # mailcowdockerized_nginx-mailcow_1
                        server 172.33.0.2:8080;
                                # Cannot connect to network of this container
                                server 127.0.0.1 down;
}
server {
        server_name mail.mysite.local;
        listen 80 ;
        access_log /var/log/nginx/access.log vhost;
        location / {
                proxy_pass http://mail.mysite.local;
        }
}
server {
        server_name mail.mysite.local;
        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;
}
fstangenberger commented 5 years ago

The same entry of "location /{proxy-pass ...}" of port 80 has to be in the section of port 443. Perhaps without "return 500"

Will have a look tomorrow on the laptop.

Heziode commented 5 years ago

Yep, but I'm not the one who makes the file, it's done automatically I can't change it manually, because it will be overwrited by the auto-conf.

The nginx-mailcow in docker-compose.yml:

nginx-mailcow:
      depends_on:
        - sogo-mailcow
        - php-fpm-mailcow
        - redis-mailcow
      image: nginx:mainline-alpine
      command: /bin/sh -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active &&
        envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active &&
        envsubst < /etc/nginx/conf.d/templates/server_name.template > /etc/nginx/conf.d/server_name.active &&
        envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active &&
        envsubst < /etc/nginx/conf.d/templates/sogo_eas.template > /etc/nginx/conf.d/sogo_eas.active &&
        . /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo_proxy_auth.active &&
        nginx -qt &&
        until ping phpfpm -c1 > /dev/null; do sleep 1; done &&
        until ping sogo -c1 > /dev/null; do sleep 1; done &&
        until ping redis -c1 > /dev/null; do sleep 1; done &&
        until ping rspamd -c1 > /dev/null; do sleep 1; done &&
        exec nginx -g 'daemon off;'"
      environment:
        #- HTTPS_METHOD=noredirect
        - HTTPS_METHOD=redirect
        - HTTP_FORWARDED_COUNT=1
        - VIRTUAL_HOST=${MAILCOW_HOSTNAME},autoconfig.${MAILCOW_HOSTNAME},autodiscover.${MAILCOW_HOSTNAME}
        - VIRTUAL_PORT=8080
        - LETSENCRYPT_HOST=${MAILCOW_HOSTNAME},autoconfig.${MAILCOW_HOSTNAME},autodiscover.${MAILCOW_HOSTNAME}
        - LETSENCRYPT_EMAIL=support@mysite.local
        - PORT=8080
        ##
        - HTTPS_PORT=${HTTPS_PORT:-443}
        - HTTP_PORT=${HTTP_PORT:-80}
        - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
        - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
        - TZ=${TZ}
        - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
      volumes:
        - ./data/web:/web:ro
        - ./data/conf/rspamd/dynmaps:/dynmaps:ro
        - ./data/assets/ssl/:/etc/ssl/mail/:ro
        - ./data/conf/nginx/:/etc/nginx/conf.d/:rw
        - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro
        - sogo-web-vol-1:/usr/lib/GNUstep/SOGo/
      ports:
        - "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
        - "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
      restart: always
      dns:
        - ${IPV4_NETWORK:-172.22.1}.254
      networks:
        mailcow-network:
          aliases:
            - nginx
        proxy-network:
fstangenberger commented 5 years ago

Try it with "HTTPS_METHOD=noredirect"

Heziode commented 5 years ago

This do not change everythings.

fstangenberger commented 5 years ago

You're right. HTTPS_METHOD doesn't matter in this case. Try without "PORT=8080"

Heziode commented 5 years ago

Likewise, it doesn't change anythings.

fstangenberger commented 5 years ago

In mailcow.conf you have both ports changed? 80 and 443 is served by nginx-proxy. So mailcow has to use other ones (8080, 8443).

fstangenberger commented 5 years ago

In default.conf of nginx-proxy: with "HTTPS_METHOD=redirect" port 80 only returns a 301 to https and port 443 has still:

        location / {
                proxy_pass http://${MAILCOW_HOSTNAME};
        }
Heziode commented 5 years ago

In mailcow.conf you have both ports changed? 80 and 443 is served by nginx-proxy. So mailcow has to use other ones (8080, 8443).

Yep, it is:

# ------------------------------
# HTTP/S Bindings
# ------------------------------

# You should use HTTPS, but in case of SSL offloaded reverse proxies:

HTTP_PORT=8080
#HTTP_BIND=0.0.0.0
HTTP_BIND=127.0.0.1

HTTPS_PORT=8443
#HTTPS_BIND=0.0.0.0
HTTPS_BIND=127.0.0.1

# And I have also change 
SKIP_LETS_ENCRYPT=y

In default.conf of nginx-proxy: with "HTTPS_METHOD=redirect" port 80 only returns a 301 to https and port 443 has still:

        location / {
                proxy_pass http://${MAILCOW_HOSTNAME};
        }

Yep, it must do this but it don't. The conf generated for my website and api work like you mention, but for mailcow, it doesn't generate the good config.

fstangenberger commented 5 years ago

Hm. HTTP_FORWARDED_COUNT are not used by mailcow, i believe. ${MAILCOW_HOSTNAME} is "mail.mysite.local" in mailcow.conf? Is 8443 used by another host? Have you altered nginx_gen.tmpl from nginx-proxy? When you delete default.conf what's the log of nginx_gen look like? Can you provide the docker-compose.yml of the nginx_proxy? Incl. nginx_gen.

fstangenberger commented 5 years ago

For an fast and graphical overview of all docker related stuff I recommend portainer: https://mailcow.github.io/mailcow-dockerized-docs/third_party-portainer/

Heziode commented 5 years ago

HTTP_FORWARDED_COUNT are not used by mailcow, i believe.

Ok.

${MAILCOW_HOSTNAME} is "mail.mysite.local" in mailcow.conf?

Yes, it is.

Is 8443 used by another host?

Nope, it is only used by nginx-mailcow.

Have you altered nginx_gen.tmpl from nginx-proxy?

No, I do not know this file. Where is it located ?

When you delete default.conf what's the log of nginx_gen look like?

Which default.conf ? Where is it located ?

Can you provide the docker-compose.yml of the nginx_proxy? Incl. nginx_gen.

What do you mean with nginx_gen ? nginx-mailcow or nginx-proxy ?

docker-compose.yml of my app:

version: '3.7'

services:
  api:
    build: .
    image: strapi/strapi
    hostname: mysite
    container_name: mysite
    environment:
      - APP_NAME=mysite
      - DATABASE_CLIENT=mongo
      - DATABASE_HOST=mysite-db
      - DATABASE_PORT=27017
      - DATABASE_NAME=mysite
      - DATABASE_USERNAME=root
      - DATABASE_PASSWORD=********
      - DATABASE_SSL=false
      - DATABASE_AUTHENTICATION_DATABASE=
      - HOST=api.mysite.local
      # Var related to reverse-proxy
      - VIRTUAL_HOST=api.mysite.local
      - HTTPS_METHOD=redirect
      - HTTP_FORWARDED_COUNT=1
      - LETSENCRYPT_HOST=api.mysite.local
      - LETSENCRYPT_EMAIL=support@mysite.local
      - PORT=1337
    ports:
      - 1337:1337
    volumes:
      - "${PWD}/mysite:/usr/src/api/mysite"
    networks:
      - proxy-network
    depends_on:
      - db
      - nginx-proxy
    links:
      - db

  db:
    image: mongo
    hostname: mysite-db
    container_name: mysite-db
    environment:
      - MONGO_INITDB_DATABASE=mysite
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=********
    volumes:
      - "${PWD}/data/db:/data/db"
    networks:
      - proxy-network
    ports:
      - 27017:27017

  nginx-proxy:
    image: jwilder/nginx-proxy:alpine
    hostname: mysite-proxy
    container_name: mysite-proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - "${PWD}/data/conf.d:/etc/nginx/conf.d/"
      - "${PWD}/data/nginx_html:/usr/share/nginx/html"
      - "${PWD}/data/vhost.d:/etc/nginx/vhost.d"
      - "${PWD}/data/certs:/etc/nginx/certs"
      - "${PWD}/data/dhparam.pem:/etc/nginx/dhparam/"
    networks:
      - proxy-network

networks:
  proxy-network:
    name: proxy-network
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: "172.33.0.0/16"

For an fast and graphical overview of all docker related stuff I recommend portainer

Indeed, it sounds very convinient, maybe also in addition of Traefik (I'm not using either of them right now).

fstangenberger commented 5 years ago

In the documentation of nginx-proxy of jwilder (https://github.com/jwilder/nginx-proxy), it says:

nginx-proxy sets up a container running nginx and docker-gen. docker-gen generates reverse proxy configs for nginx and reloads nginx when containers are started and stopped.

Please have a closer look at this documentation. In section "Multipe Ports":

If your container exposes multiple ports, nginx-proxy will default to the service running on port 80. If you need to specify a different port, you can set a VIRTUAL_PORT env var to select a different one.

You can try to set VIRTUAL_PORT=8443 instead of VIRTUAL_PORT=8080 in the docker-compose.yml of mailcow.

I use separate containers (section "Separate Containers"): nginx-proxy: image: nginx:latest nginx-gen:image: jwilder/docker-gen:latest nginx-le:image: jrcs/letsencrypt-nginx-proxy-companion:latest (for letsencrypt)

For my separate nginx_gen container I have to use nginx.tmpl like mentioned in the documentation.

Heziode commented 5 years ago

As you can see, I only use jwilder/nginx-proxy:alpine that wrappe all.

You can try to set VIRTUAL_PORT=8443 instead of VIRTUAL_PORT=8080 in the docker-compose.yml of mailcow.

When I do that, I get error 400 when I try to access from HTTP, with message: The plain HTTP request was sent to HTTPS port When I try to access from HTTPS, I get error 500.

fstangenberger commented 5 years ago

... and with VIRTUAL_PROTO=https ? http should redirect via 301 to https

fstangenberger commented 5 years ago

perhaps you can try to use separate containers

Heziode commented 5 years ago

... and with VIRTUAL_PROTO=https ? http should redirect via 301 to https

No change when I try to access from HTTPS.

I try to access from HTTP, I can access to mailcow, but not in HTTPS.

It give me this conf:

server {
        server_name mail.mysite.local;
        listen 80 ;
        access_log /var/log/nginx/access.log vhost;
        location / {
                proxy_pass https://mail.mysite.local;
        }
}
server {
        server_name mail.mysite.local;
        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;
}

perhaps you can try to use separate containers

Yep, but it is weird that it do not work like this…

fstangenberger commented 5 years ago

VIRTUAL_PROTO=https has to be set with VIRTUAL_PORT=8443

fstangenberger commented 5 years ago

Btw: To use the var LETSENCRYPT_HOST you have to use the jrcs/letsencrypt-nginx-proxy-companion container, haven't you?

Heziode commented 5 years ago

VIRTUAL_PROTO=https has to be set with VIRTUAL_PORT=8443

Of course, that's what I did.

Btw: To use the var LETSENCRYPT_HOST you have to use the jrcs/letsencrypt-nginx-proxy-companion container, haven't you?

In fact, I do not use letsencryt in dev. When I dev (so, in local), I use mkcert for local dev. In production, I will use let's encrypt.

fstangenberger commented 5 years ago

For live it's also better to use splitted nginx-proxy/ docker-gen. Then nginx-proxy is reachable from the internet while docker-gen with docker.sock ist not.

Actually, your default.conf is build with only one port. I don't see the reason, why. You can use an own conf for your site next to default.conf. There you can add "location /" for 443 like it's done for 80 in default.conf.

Heziode commented 5 years ago

With nginx-proxy + docker-gen and default nginx_gen.tmpl, I have the same result. 500 on HTTPS.

Heziode commented 5 years ago

I got it! I didn't put the certificates in the right place. I have put it in certs for nginx-mailcow, but I must put it the nginx exposed to the door, so nginx-proxy.

Right now, it's work. I need some experiment, but I (retry with the one nginx container) to check if certs is the only issue.

fstangenberger commented 5 years ago

Great! I use the jrcs/letsencrypt-nginx-proxy-companion and therefor I set a cronjob to sync the newest cert from nginx-proxy to mailcow: 0 3 * * * root /usr/bin/rsync -a ${NGINX_PROXY_ROOT}/certs/${MAILCOW_HOSTNAME}/fullchain.pem ${NGINX_PROXY_ROOT}/certs/${MAILCOW_HOSTNAME}/cert.pem ${NGINX_PROXY_ROOT}/certs/${MAILCOW_HOSTNAME}/key.pem ${MAILCOW_ROOT}/data/assets/ssl

Heziode commented 5 years ago

With single container, it works. So, we simply need to put certificates in the right place (reverse proxy exposed to internet), and add environement variable related to reverse proxy (the one that is exposed to internet) on the nginx-mailcow.

I use the jrcs/letsencrypt-nginx-proxy-companion and therefor I set a cronjob to sync the newest cert from nginx-proxy to mailcow:

Have you arrived to work with let's encrypt on localhost ?

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.