hardware / mailserver

:warning: UNMAINTAINED - Simple and full-featured mail server using Docker
https://store.docker.com/community/images/hardware/mailserver
MIT License
1.29k stars 324 forks source link

Can we avoid manual steps around nginx and let's encrypt SSL? #188

Closed AndrewSav closed 6 years ago

AndrewSav commented 7 years ago

I was wondering if it would be possible for this project to publish a docker-compose file that would take care of let's encrypt certs creation and renewal automatically? For those people using letsencrypt.

And for everyone, can we use something that manages adding sites to nginx reverse proxy automatically without need for manual steps?

Something like this: https://github.com/jwilder/nginx-proxy and this: https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion ?

hardware commented 7 years ago

It's possible yes but I don't think that's the purpose of this docker image, unlike mailinabox for example, hardware/mailserver isn't a mailserver ready out-of-box with automatic reverse proxy, dns and ssl configuration. Everyone is free to choose and use what is needed beside this docker image depending on the sysadmin needs. Postfixadmin, Rainloop, NSD, Nginx, letsencrypt...etc all of this components are all optional.

Edit: I never used jwilder's reverse proxy companion. You can PR another docker-compose file if you want. I will take a look when I will have time.

ksylvan commented 7 years ago

My Ansible playbooks add the letsencrypt SSL creation on top of this awesome project.

hardware commented 7 years ago

Or use ksylvan's awesome ansible playbooks :p

ksylvan commented 7 years ago

@AndrewSav https://github.com/jwilder/nginx-proxy is a very interesting approach.

In my use cases I also have hosts that serve normal nginx setups... but I guess that can be handled by a normal nginx:alpine container (so that I can still serve up the www.foo.bar/some_file.txt and www.foo.bar/~user/foo.txt requests).

AndrewSav commented 7 years ago

@ksylvan so are you running ansible in a cron job or something, to keep the certs up to date? Intuitively it does not feel that ansible is the right tool for this job, it is traditionally runs when configuration changes not regularly. But then again, I might be thinking about it all wrong.

What I mean that it looks like the playbook only address a part of my initial query. If we break down it as

  1. Nginx setup
  2. SSL Setup
  3. SSL update

it only solves 2 and partially 1. Partially, because if you want to run some more software behind the same reverse proxy, you have to do it yourself, as the playbook is limited to configuring mailserver only.

AndrewSav commented 7 years ago

@hardware I understand your answer. You are not interested in making improvements here yourself but you will be happy to review a PR if any. I guess this answers the question. Thanks. Feel free to close this if you do not need it.

ksylvan commented 7 years ago

No. The playbooks simply deploy a server with a working web and mail stack. Running Ansible inside a cron job would be crazy. :-)

In the process, they create the initial LetsEncrypt SSL certs and also set up an auto-renewal mechanism. Specifically, your running server will run a cron job to auto-renew your SSL certs when they are near expiry.

AndrewSav commented 7 years ago

@ksylvan oh, that's neat! Did not realize that!

So the actual problem, I'm trying to solve is ability to easily migrate my setup somewhere else when I need it it. (At the same time being able to host other apps behind the same proxy, being able to migrate them too and have auto renewals for all of them) In this regards, the initial configuration is not that important for me, because it is already set up ;)

So for me the most promising avenue seems to be a docker-compose file that I can use to down on the old installation copy the volumes to the other one, and up in the new place. Do you think your playbook still might be more convenient in this scenario?

But yeah, @hardware is right there is no one size fits all, so I want this and you want that and someone else wants something completely different, so it makes sense to provide a foundation (as this project does) for other people to build upon.

System administration is fiddly.

ksylvan commented 7 years ago

@AndrewSav specifically, all I do is set up a secure server (set up firewall, fix ssh so no password logins, no root login, etc.), install Docker CE on it, and then set up this docker-based stack.

On your deployed server, even certbot and docker-compose are run as scripts that utilize underlying docker images.

So, it's a minimal server that sets you up with a bunch of services. You can even run a wordpress install as your web server if you want (and @outbackdingo is adding the ability to run Opencart as store.yourdomain.tld).

AndrewSav commented 7 years ago

@ksylvan - I expanded a bit my previous message and there is a question there for your opinion on how your playbook fits my scenario ;) Thanks!

ksylvan commented 7 years ago
So the actual problem, I'm trying to solve is ability to easily migrate my setup somewhere else
when I need it it. (At the same time being able to host other apps behind the same proxy,
being able to migrate them too and have auto renewals for all of them) In this regards,
the initial configuration is not that important for me, because it is already set up ;)

So for me the most promising avenue seems to be a docker-compose file that I can use to
down on the old installation copy the volumes to the other one, and up in the new place.
Do you think your playbook still might be more convenient in this scenario?

Yes. Easily. Here is how I would do it.

  1. Instantiate a new VPS instance (Debian 9, etc.)

  2. Bring down docker on the old server, copy the entire /mnt/docker directory to new server.

  3. On my control host (where Ansible is run), hack /etc/hosts file so that the server name is pointing to the new IP address.

  4. Run my playbooks against the new server, this will ensure that docker and the software stack, firewall settings, etc. are fixed. You should have your new stack completely running, with all the modifications you made over time preserved.

  5. Fix the DNS settings to point to the new IP address. Also will have to fix the SPF record (I think that's the only one you'd need to fix, since the DKIM keys are saved in /mnt/docker hierarchy).

ksylvan commented 7 years ago

The only caveat is that if you add other apps behind that nginx proxy, you just want to make sure that all the persistent data ends up in /mnt/docker in some logical fashion so that you don't have to grab files/directories scattered around your system.

One final note: I never change the docker-compose.yml on the deployed system. I let that be generated and installed by Ansible.

denji commented 7 years ago

https://traefik.io docker ready let's encrypt server ( https://github.com/containous/traefik ).

sknight80 commented 6 years ago

I might be too late in the party here, but I am using https://github.com/jwilder/nginx-proxy in the mail server that hardware has here. The nginx-proxy provides me the certificate that I feed in the mail server container. Also, the webmail and the postfix containers using the SSL from the nginx-proxy container and the proxy container handles the traffic between the postfix admin and the webmail.

I put everything together in a docker-composer file. If you need an example happy to share.

ghost commented 6 years ago

please do share quite interesting stuff here... i used the docker-mailserver ansible runbook worked like a charm for me

On Sat, Nov 11, 2017 at 4:07 PM, Istvan Szabo notifications@github.com wrote:

I might be too late in the party here, but I am using https://github.com/jwilder/nginx-proxy in the mail server that hardware has here. The nginx-proxy provides me the certificate that I feed in the mail server container. Also, the webmail and the postfix containers using the SSL from the nginx-proxy container and the proxy container handles the traffic between the postfix admin and the webmail.

I put everything together in a docker-composer file. If you need an example happy to share.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hardware/mailserver/issues/188#issuecomment-343671355, or mute the thread https://github.com/notifications/unsubscribe-auth/ABXFTlhXtS8U4R5ob-53lRjOXfyaAz7hks5s1bg4gaJpZM4QYkGR .

sknight80 commented 6 years ago

Here is my docker-compose yaml file:

services:
  mailserver:
    image: hardware/mailserver:1.1-stable
    container_name: mailserver
    domainname: <FQDN>
    hostname: mail
    restart: always
    networks:
      - mailnet
    links:
      - "mariadb:mariadb"
    ports:
      - "25:25"     # SMTP                - Required
      - "143:143"   # IMAP       STARTTLS - Optional - For webmails/desktop clients
      - "587:587"   # Submission STARTTLS - Optional - For webmails/desktop clients
      - "993:993"   # IMAPS      SSL/TLS  - Optional - For webmails/desktop clients
      - "11334:11334" # RSPAMD WEBINTERFACE
    environment:
      - DBPASS=<DBPASS>
      - RSPAMD_PASSWORD=<RSPAMDPWD>
      - ADD_DOMAINS=<IHAVEOTHERDOMAINSHERE, for example: example.com,newword.com>
    volumes:
      - /mnt/docker/mail:/var/mail
      - /mnt/docker/mail/opendkim:/etc/opendkim/keys
      - /usr/local/etc/proxy/certs/:/etc/letsencrypt
    depends_on:
      - mariadb
      - redis
 # Redis
  redis:
    image: redis:3.2-alpine
    container_name: redis
    command: redis-server --appendonly yes
    networks:
      - mailnet
    volumes:
      - /mnt/docker/redis/db:/data
 # Administration interface
  postfixadmin:
    image: hardware/postfix:latest
    container_name: postfixadmin
    domainname: <FQDN>
    hostname: mail
    restart: always
    networks:
      - proxynet
      - mailnet
    links:
      - mariadb:mariadb
    environment:
      DBHOST: mariadb
      DBUSER: <DBUSER>
      DBNAME: <DBNAME>
      DBPASS: <DBPWD>
      VIRTUAL_HOST: postfixadmin.<FQDN>
      VIRTUAL_PORT: 8081
      LETSENCRYPT_EMAIL: <YOURVALIDEMAIL ADDRESS>
      LETSENCRYPT_HOST: "<HOSTLIST THAT YOU NEED FOR SSL"
      HTTPS_METHOD: redirect
      CERT_NAME: mail.<FQDN>
    depends_on:
      - mailserver
      - mariadb
 # Webmail (Optional)
  rainloop:
    image: hardware/rainloop
    container_name: rainloop
    restart: always
    links:
      - mailserver:mailserver
    environment:
      VIRTUAL_HOST: mail.<FQDN>
      VIRTUAL_PORT: 8888
      LETSENCRYPT_EMAIL: <ANOTHER VALID MAIL ADDRESS THAT YOU USE>
      LETSENCRYPT_HOST: "mail.<FQDN>,postfixadmin.<FQDN>"
      HTTPS_METHOD: redirect
      CERT_NAME: mail.<FQDN>
    networks:
      - proxynet
      - mailnet
    volumes:
      - /mnt/docker/rainloop:/rainloop/data
    depends_on:
      - mailserver
      - mariadb
  > # Database
  mariadb:
    image: mariadb:10.1
    container_name: mariadb
    restart: always
    networks:
      - mailnet
    environment:
      - MYSQL_ROOT_PASSWORD=<ROOTPWD>
      - MYSQL_DATABASE=<POSTFIXDB>
      - MYSQL_USER=<POSTFIXUSER>
      - MYSQL_PASSWORD=<POSTFIXUSERPWD>
    volumes:
      - /mnt/docker/mysql/db:/var/lib/mysql
networks:
  proxynet:
    external:
      name: proxy_proxynet
  mailnet:
    driver: bridge

I modified the postfix image with using a different http port than 8888. And here is my nginx-proxy yaml file:

# NGINX server

version: '2'

services:
  nginx-proxy:
    image: jwilder/nginx-proxy:alpine
    container_name: nginx-proxy
    networks:
      - proxynet
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - /usr/local/etc/proxy/certs/:/etc/nginx/certs:ro
      - /etc/nginx/vhost.d
      - /usr/share/nginx/html

  letsencrypt-nginx-proxy:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: letsencrypt-nginx-proxy
    networks:
      - proxynet
    volumes_from:
      - nginx-proxy
    volumes:
      - /usr/local/etc/proxy/certs/:/etc/nginx/certs
      - /var/run/docker.sock:/var/run/docker.sock:ro
    depends_on:
      - nginx-proxy

networks:
  proxynet:
    driver: bridge

I hope this example help others to using nginx-proxy with hardware-mail* solution.

ghost commented 6 years ago

can you possible give a breif detail on why nginx-proxy is maybe better then the one being used for us ?

On Mon, Nov 13, 2017 at 2:37 PM, Istvan Szabo notifications@github.com wrote:

Here is my docker-compose yaml file:

services: mailserver: image: hardware/mailserver:1.1-stable container_name: mailserver domainname: hostname: mail restart: always networks:

  • mailnet links:
  • "mariadb:mariadb" ports:
  • "25:25" # SMTP - Required
  • "143:143" # IMAP STARTTLS - Optional - For webmails/desktop clients
  • "587:587" # Submission STARTTLS - Optional - For webmails/desktop clients
  • "993:993" # IMAPS SSL/TLS - Optional - For webmails/desktop clients
  • "11334:11334" # RSPAMD WEBINTERFACE environment:
  • DBPASS=
  • RSPAMD_PASSWORD=
  • ADD_DOMAINS=<IHAVEOTHERDOMAINSHERE, for example: example.com,newword.com> volumes:
  • /mnt/docker/mail:/var/mail
  • /mnt/docker/mail/opendkim:/etc/opendkim/keys
  • /usr/local/etc/proxy/certs/:/etc/letsencrypt depends_on:
  • mariadb
  • redis

    Redis

    redis: image: redis:3.2-alpine container_name: redis command: redis-server --appendonly yes networks:

  • mailnet volumes:
  • /mnt/docker/redis/db:/data

    Administration interface

    postfixadmin: image: hardware/postfix:latest container_name: postfixadmin domainname: hostname: mail restart: always networks:

  • proxynet
  • mailnet links:
  • mariadb:mariadb environment: DBHOST: mariadb DBUSER: DBNAME: DBPASS: VIRTUAL_HOST: postfixadmin. VIRTUAL_PORT: 8081 LETSENCRYPT_EMAIL: LETSENCRYPT_HOST: "<HOSTLIST THAT YOU NEED FOR SSL" HTTPS_METHOD: redirect CERT_NAME: mail. depends_on:
  • mailserver
  • mariadb

    Webmail (Optional)

    rainloop: image: hardware/rainloop container_name: rainloop restart: always links:

  • mailserver:mailserver environment: VIRTUAL_HOST: mail. VIRTUAL_PORT: 8888 LETSENCRYPT_EMAIL: LETSENCRYPT_HOST: "mail.,postfixadmin." HTTPS_METHOD: redirect CERT_NAME: mail. networks:
  • proxynet
  • mailnet volumes:
  • /mnt/docker/rainloop:/rainloop/data depends_on:
  • mailserver
  • mariadb

    Database

    mariadb: image: mariadb:10.1 container_name: mariadb restart: always networks:

  • mailnet environment:
  • MYSQL_ROOT_PASSWORD=
  • MYSQL_DATABASE=
  • MYSQL_USER=
  • MYSQL_PASSWORD= volumes:
  • /mnt/docker/mysql/db:/var/lib/mysql networks: proxynet: external: name: proxy_proxynet mailnet: driver: bridge

I modified the postfix image with using a different http port than 8888. And here is my nginx-proxy yaml file:

NGINX server

version: '2'

services: nginx-proxy: image: jwilder/nginx-proxy:alpine container_name: nginx-proxy networks:

  • proxynet ports:
  • "80:80"
  • "443:443" volumes:
  • /var/run/docker.sock:/tmp/docker.sock:ro
  • /usr/local/etc/proxy/certs/:/etc/nginx/certs:ro
  • /etc/nginx/vhost.d
  • /usr/share/nginx/html

    letsencrypt-nginx-proxy: image: jrcs/letsencrypt-nginx-proxy-companion container_name: letsencrypt-nginx-proxy networks:

  • proxynet volumes_from:
  • nginx-proxy volumes:
  • /usr/local/etc/proxy/certs/:/etc/nginx/certs
  • /var/run/docker.sock:/var/run/docker.sock:ro depends_on:
  • nginx-proxy

networks: proxynet: driver: bridge

I hope this example help others to using nginx-proxy with hardware-mail* solution.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hardware/mailserver/issues/188#issuecomment-343920961, or mute the thread https://github.com/notifications/unsubscribe-auth/ABXFTnrZiMRIjIv_ulXuH4lh34RZDbIkks5s2EYxgaJpZM4QYkGR .

sknight80 commented 6 years ago

I am using the nginx-proxy on my web hosting service and for me, it was easy to setup/reuse the same container here since the cert support is available in the mail server container. Also the nginx-proxy with the companion handle the renewal process based on the docker-compose file. I hope this answer your question.

For the record: I did not try to use the let's encrypt feature in the mailserver.

AndrewSav commented 6 years ago

@sknight80 is this more convenient to split it over two docker-compose files like this?

sknight80 commented 6 years ago

For me yes, because one yml file handles the proxy-related events and the second one is focusing on the mail-server related events. You can combine this two of course but for start and stop command in docker-compose will be a little bit longer and you might stop the mail server when you don't want too. I hope this answer your question.

ksylvan commented 6 years ago

That's quite neat. Thanks for sharing that.

AndrewSav commented 6 years ago

@sknight80 I noticed that for postifxadmin and rainloop your environment block does not have dashes preceding each environment variable. Is that because you did not copy your actual config here and edited out private information, but instead just typed in something from memory, hoping that it might work? Or is it a different syntax?

ksylvan commented 6 years ago

@AndrewSav the docker compose environment block can be written in two ways:

environment:
  RACK_ENV: development
  SHOW: 'true'
  SESSION_SECRET:

Or:

environment:
  - RACK_ENV=development
  - SHOW=true
  - SESSION_SECRET

See https://docs.docker.com/compose/compose-file/compose-file-v2/#environment

AndrewSav commented 6 years ago

And since we are on a config sharing spree, here is what I ended up with:

version: '2.1'

services:
  nginx-proxy:
    image: jwilder/nginx-proxy:alpine
    container_name: nginx-proxy
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - /mnt/docker/certs:/etc/nginx/certs:ro
      - /mnt/docker/nginx/vhost.d:/etc/nginx/vhost.d
      - /mnt/docker/nginx/html:/usr/share/nginx/html
    depends_on:
      - postfixadmin
      - rainloop
      - mailserver

  letsencrypt-nginx-proxy:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: letsencrypt-nginx-proxy
    restart: always
    environment:
      # Unfortunately every time TOS changes this breaks. There is a PR to fix it here
      # https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion/issues/90
      # Eventually it will make it to the official container
      ACME_TOS_HASH: cc88d8d9517f490191401e7b54e9ffd12a2b9082ec7a1d4cec6101f9f1647e7b
    volumes_from:
      - nginx-proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /mnt/docker/certs:/etc/nginx/certs
    depends_on:
      - nginx-proxy

  mailserver:
    image: hardware/mailserver:1.1-stable
    container_name: mailserver
    domainname: <domain.name>                  # Mail server A/MX/FQDN & reverse PTR = mail.domain.tld.
    hostname: mail
    restart: always
    # extra_hosts:                          - Required for external database (on other server or for local database on host)
    #  - "mariadb:xx.xx.xx.xx"              - Replace with IP address of MariaDB server
    ports:
      - "25:25"       # SMTP                - Required
    # - "110:110"     # POP3       STARTTLS - Optional - For webmails/desktop clients
      - "143:143"     # IMAP       STARTTLS - Optional - For webmails/desktop clients
    # - "465:465"     # SMTPS      SSL/TLS  - Optional - Enabled for compatibility reason, otherwise disabled
      - "587:587"     # Submission STARTTLS - Optional - For webmails/desktop clients
      - "993:993"     # IMAPS      SSL/TLS  - Optional - For webmails/desktop clients
    # - "995:995"     # POP3S      SSL/TLS  - Optional - For webmails/desktop clients
      - "4190:4190"   # SIEVE      STARTTLS - Optional - Recommended for mail filtering
    # - "11334:11334" # HTTP                - Optional - Rspamd WebUI
    environment:
      # MariaDB database password (required)
      - DBPASS=<dbpass>
      # Rspamd WebUI password (required)
      - RSPAMD_PASSWORD=<rspamdpass>
      # Add additional domains separated by commas (needed for dkim keys etc.)
      - ADD_DOMAINS=<additional domaings>
      - VIRTUAL_HOST=<spam.FQDN,mail.FQDN>
      - VIRTUAL_PORT=11334
      - HTTPS_METHOD=redirect
    # - ENABLE_POP3=true                    # Enable POP3 protocol
    # - ENABLE_FETCHMAIL=true               # Enable fetchmail forwarding
    # - DISABLE_CLAMAV=true                 # Disable virus scanning
    # - DISABLE_SIGNING=true                # Disable DKIM/ARC signing
    # - DISABLE_GREYLISTING=true            # Disable greylisting policy
    # - DISABLE_RATELIMITING=true           # Disable ratelimiting policy
    #
    # Full list : https://github.com/hardware/mailserver#environment-variables
    #
    volumes:
      - /mnt/docker/mail:/var/mail
      - /mnt/docker/certs:/etc/letsencrypt/live
    depends_on:
      - mariadb
      - redis

  # Administration interface
  # https://github.com/hardware/postfixadmin
  # http://postfixadmin.sourceforge.net/
  # Configuration : https://github.com/hardware/mailserver/wiki/Postfixadmin-initial-configuration
  postfixadmin:
    image: hardware/postfixadmin
    container_name: postfixadmin
    domainname: <domain.name>
    hostname: postfixadmin
    restart: always
    environment:
      DBPASS: <dbpass>
      VIRTUAL_HOST: postfixadmin.FQDN
      HTTPS_METHOD: redirect
    depends_on:
      - mailserver
      - mariadb

  # Webmail (Optional)
  # https://github.com/hardware/rainloop
  # https://www.rainloop.net/
  # Configuration : https://github.com/hardware/mailserver/wiki/Rainloop-initial-configuration
  rainloop:
    image: hardware/rainloop
    container_name: rainloop
    restart: always
    environment:
      VIRTUAL_HOST: webmail.FQDN
      LETSENCRYPT_EMAIL: <let's encrypt email>
      LETSENCRYPT_HOST: "mail.FQDN,webmail.FQDN,postfixadmin.FQDN,spam.FQDN,other hosts behind this nginx-proxy"
      HTTPS_METHOD: redirect
    volumes:
      - /mnt/docker/rainloop:/rainloop/data
    depends_on:
      - mailserver
      - mariadb

  # Database
  # https://github.com/docker-library/mariadb
  # https://mariadb.org/
  mariadb:
    image: mariadb:10.1
    container_name: mariadb
    restart: always
    # Info : These variables are ignored when the volume already exists (databases created before).
    environment:
      - MYSQL_ROOT_PASSWORD=<mysqlrootpassword>
      - MYSQL_DATABASE=postfix
      - MYSQL_USER=postfix
      - MYSQL_PASSWORD=<mysqlpassword>
    volumes:
      - /mnt/docker/mysql/db:/var/lib/mysql

  # Cache Database
  # https://github.com/docker-library/redis.
  # https://redis.io/
  redis:
    image: redis:3.2-alpine
    container_name: redis
    restart: always
    command: redis-server --appendonly yes
    sysctls:
      - net.core.somaxconn=1024
    volumes:
      - /mnt/docker/redis/db/:/data

# other containers that can be exposed by the same proxy
  <somecontainer>:
    image: <some image>
    container_name: <some name>
    restart: always
    environment:
      VIRTUAL_HOST: <some.FQDN>
      HTTPS_METHOD: redirect
    ...

Notable differences from @sknight80 script:

So as I said before, no one size fits all, but that's what worked for me.

zerpex commented 6 years ago

That's the way I used to do it (with jwilder build and Jrsc's lestencrypt), but it seems Jcrs does not maintain he's container anymore.

And since traefik was developped FOR containers and is efficient and simple, I migrated everything through it.

AndrewSav commented 6 years ago

@zerpex good to know, I'll have a look. So are you saying that traefic can do everything that jwilder / Jrsc can?

sknight80 commented 6 years ago

@AndrewSav sorry copy paste error + markdown. And thank you for updating my yml file. I have to look at what is going on on the nginx-proxy area.

zerpex commented 6 years ago

@AndrewSav nginx is a great proxy/web server ( I am a great fan :) ), but traefik was designed specifically for containers (docker, kubernetes...) and manages LE certificates generation and renewal nativelly.

So you need only one container to get proxy + certs management. One more argument is that traefik is maintained by it's developpers. I appreciate Jwilder and Jrcs for their contribution to the community, but what if they stop maintening their containers (as Jrcs seems to !) ?

From my tests, I can tell that traefik is more efficient and optimized.

nginx and Apache are still good, but more as webservers from inside containers :)

zerpex commented 6 years ago

Hello,

Here is my setup with traefik on one docker-compose.yml file : From my previous post, I added rainloop and access to rspamd webui. This setup is better using @hardware 's architecture with domains and sub-domains.

version: '2.1'

services:
   traefik:
    restart: unless-stopped
    image: traefik
    container_name: proxy_traefik
    hostname: traefik
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.toml:/traefik.toml
      - ./files/acme:/etc/traefik/acme
    networks:
       - traefik_proxy

   mailserver:
    image: hardware/mailserver:1.1-stable
    container_name: mail-server
    hostname: mail
    domainname: domain.tld
    labels:
    - traefik.enable=true
    - traefik.frontend.rule=Host:spam.domain.tld
    - traefik.backend.port=11334
    - traefik.docker.network=traefik_proxy
    depends_on:
      - mariadb
      - redis
    ports:
     - "25:25"     # SMTP                - Required
     - "143:143"   # IMAP       STARTTLS - For webmails/desktop clients
     - "465:465"   # SMTPS      SSL/TLS  - Enabled for compatibility reason, otherwise disabled
     - "587:587"   # Submission STARTTLS - For webmails/desktop clients
     - "993:993"   # IMAPS      SSL/TLS  - For webmails/desktop clients
     - "4190:4190" # SIEVE      STARTTLS - Optional, but recommended for mail filtering
    volumes:
     - ./files/mail/data:/var/mail
     - ./files/mail/opendkim:/etc/opendkim/keys
    environment:
     - VMAILUID=1069
     - VMAILGID=1069
     - OPENDKIM_KEY_LENGTH=4096
     - DBPASS=xxxxxxx
     - RSPAMD_PASSWORD=xxxxxxx 
    networks:
      - mail_back
      - traefik_proxy

   rainloop:
    image: hardware/rainloop
    container_name: rainloop
    # restart: always
    labels:
    - traefik.enable=true
    - traefik.frontend.rule=Host:webmail.domain.tld
    - traefik.backend.port=80
    - traefik.docker.network=traefik_proxy
    volumes:
      - ./files/rainloop:/rainloop/data
    depends_on:
      - mailserver
      - mariadb
    networks:
      - mail_back
      - traefik_proxy

# Databases
   mariadb:
    image: mariadb:latest
    container_name: mail-server_db
    environment:
     - MYSQL_ROOT_PASSWORD=xxxxxxx
     - MYSQL_DATABASE=postfix
     - MYSQL_USER=postfix
     - MYSQL_PASSWORD=xxxxxxx
    volumes:
     - ./files/mail/db:/var/lib/mysql
    networks:
      - mail_back

# Control panel
   postfixadmin:
    image: hardware/postfixadmin
    container_name: mail-postfixadm
    domainname: domain.tld
    hostname: mail
    labels:
    - traefik.enable=true
    - traefik.frontend.rule=Host:mailadm.domain.tld
    - traefik.backend.port=8888
    - traefik.docker.network=traefik_proxy
    depends_on:
      - mailserver
      - mariadb
    environment:
     - UID=1069
     - GID=1069
     - DBPASS=xxxxxxx
    networks:
      - mail_back
      - traefik_proxy

# Cache
   redis:
    image: redis:4.0-alpine
    container_name: mail-cache_redis
    # restart: always
    command: redis-server --appendonly yes
    volumes:
      - ./files/redis/db:/data
    networks:
      - mail_back

networks:
  traefik_proxy:
    external:
      name: traefik_proxy
  mail_back:

And the associated traefik.toml file :

#debug = true

defaultEntryPoints = ["http", "https"]      # defaultEntryPoints must be at the top because it should not be in any table below

[web]
  # Port for the status page
  address = ":8080"
  #[web.auth.basic]
  #  users = ["traefik:generate-with-htpasswd"]

# Entrypoints, http and https
[entryPoints]
  [entryPoints.http]                        # http should be redirected to https
    address = ":80"
    [entryPoints.http.redirect]
      entryPoint = "https"                  # https is the default
  [entryPoints.https]
    address = ":443"
    [entryPoints.https.tls]

# Enable ACME (Let's Encrypt): automatic SSL
[acme]
  # caServer = "https://acme-staging.api.letsencrypt.org/directory"
  email = "me@domain.tld"
  storage = "/etc/traefik/acme/acme.json"   # or "traefik/acme/account" if using KV store
  entryPoint = "https"
  onDemand = false
  OnHostRule = true

[[acme.domains]]
  main = "domain.tld"
  sans = ["mail.domain.tld"]

[docker]
  endpoint = "unix:///var/run/docker.sock"
  domain = "domain.tld"
  watch = true
  exposedbydefault = false                  # tells Træfik to only include containers with the label traefik.enable=true

Actually, this should work only with replacing "domain.tld" by yours in both files (and the RSPAMD_PASSWORD, of course :D).

Regards, z.

sknight80 commented 6 years ago

@zerpex I will try your yaml file. Thanks for sharing it!

AndrewSav commented 6 years ago

@sknight80 @hardware

There seem to be an issue with certificates:

mailserver:
    volumes:
      - /usr/local/etc/proxy/certs/:/etc/letsencrypt

Here https://github.com/hardware/mailserver/blob/master/rootfs/usr/local/bin/run.sh#L89

/live/ is added at the end of certs path, but there is no /live/ in jwilder / Jrsc setup, so the mailserver does not get the right cert.

How do I fix it?

AndrewSav commented 6 years ago

Hm...

changed to - /usr/local/etc/proxy/certs/:/etc/letsencrypt/live

seems to work. Is it alright?

As a side note no one seems to give a rat's ass about validity of this certificate. Rainloop did not catch it. Gmail android client did not catch it. Only when I tried to retrieve my emails from iPad standard email client was I notified that the cert is from France and is untrusted.

I wonder why most of the clients do not care...

AndrewSav commented 6 years ago

@zerpex in your setup above if you need to add more web apps beside mailserver to traefic, where do you add them to? That is what do you need to change?

hardware commented 6 years ago

seems to work. Is it alright?

Yes

I wonder why most of the clients do not care...

Because most of the mail clients are bad and do not want to bother users with messages they do not understand. You're fine with a self-signed certificate if you have a way to check the fingerprint. Thunderbird and K-9 Mail warn the user when the fingerprint change.

Rainloop can verify the validity, look in your settings.

zerpex commented 6 years ago

@AndrewSav 2 things have to be changed on your docker-compose files :

You can also remove exposed ports (except thoses who need public exposure, of course). All traffic will be done in docker virtual network (=increasing security).

cloudtestsoftware commented 6 years ago

@zerpex

I tried your yml and toml by replacing my domain and DB password. I am seeing error while running docker-compose for mail server as below. Can you help me how it can be fixed?

Status: Downloaded newer image for hardware/postfixadmin:latest Creating mail-cache_redis ... Creating proxy_traefik ... Creating mail-server_db ... Creating mail-cache_redis Creating proxy_traefik Creating mail-server_db ... done Creating mail-server ...

ERROR: for mail-server dictionary update sequence element #0 has length 19; 2 is required

ERROR: for mailserver dictionary update sequence element #0 has length 19; 2 is required Traceback (most recent call last): File "bin/docker-compose", line 6, in File "compose/cli/main.py", line 68, in main File "compose/cli/main.py", line 121, in perform_command File "compose/cli/main.py", line 952, in up File "compose/project.py", line 455, in up File "compose/parallel.py", line 70, in parallel_execute ValueError: dictionary update sequence element #0 has length 19; 2 is required Failed to execute script docker-compose

cloudtestsoftware commented 6 years ago

Also tried below config by @AndrewSav but not able to setup the db

*Error: Can't connect to database Please edit the $CONF['database_'] parameters in config.local.php. DEBUG INFORMATION: Connect: php_network_getaddresses: getaddrinfo failed: Name does not resolve**

        version: '2.1'

services: nginx-proxy: image: 'jwilder/nginx-proxy:alpine' container_name: nginx-proxy restart: always ports:

AndrewSav commented 6 years ago

@cloudtestsoftware where you able to get this working following the original instructions of README.MD in the repository? If not, I'd suggest you to start there. Here we mostly building up on what is already done over there, and it would be good to confirm that you got the basics right.

To me it looks like you might have mis-configured mariadb user.

dauriata commented 6 years ago

@cloudtestsoftware got the same error and fixed it with updating docker-compose @zerpex I tried to use your yml file but I would think that imaps and smtp tls would need the let's encrypt certificate. How do they get passed from traefik to mailserver or is it useless ?

hardware commented 6 years ago

All wonderfall's docker images are no longer maintained. I will update hardware/mailserver to Træfik reverse proxy instead of boring-nginx before the end of the year. There should be no breaking changes with current installations.

ksylvan commented 6 years ago

I'm working on integrating use of traefik in my setup. This mostly seems like a confiuration/documentation change, in the base image (differences in the docker-compose.yml mostly).

AndrewSav commented 6 years ago

@zerpex, do you happen to know if the acme.domains section is required at all if you already specified OnHostRule = true? By reading the doco I got the impression that with the latter domains come from the frontend host rules automagically...

JOduMonT commented 6 years ago

a 2cents from far far away for @AndrewSav

@hardware I understand your answer. You are not interested in making improvements here yourself but you will be happy to review a PR if any. I guess this answers the question. Thanks. Feel free to close this if you do not need it.

what I do appreciate in this project over mailcow it's it don't try to do everything
as a fair approach I understood the cloudron.io ones which they use one webservice (apache/nginx) and dbservice(mysql/postgres) out of docker (direct on the host) and point all docker needs to them. it had the advantage of you don't turn 20 docker nginx just to proxy them with a haproxy or traefik

So with hardware/mailserver it's possible to take this approach because it focus on being a mailstack only. You may be able to do the same thing with mailcow or other project goes overs the mailstack but, at least for mailcow, it's a pain in the ass when you custom and upgrade.

anyway a lot of project are out here, I just when to mention @hardware I hope the

Everyone is free to choose and use what is needed beside this docker image depending on the sysadmin needs

will still be true ...

AndrewSav commented 6 years ago

@jodumont sorry, if it was directed at me I did not understand what your point was. Additionally I have no idea what is that "mailstack" you are talking about. Perhaps you confused it with mailserver?

JOduMonT commented 6 years ago

@AndrewSav don't be sorry; for me a mailstack (might now the right definition) is like LAMP (Linux Apache MySql PHP) or LEMP or ... so a bunch a software usually use to offer a Service as a Packaging

i mostly use this words in reference of docker-mailserver

hardware commented 6 years ago

@AndrewSav : hardware/mailserver use the KISS principle (Keep it simple, stupid) to make maintenance easier for me and avoid as far as possible out-of-box project nightmare when upgrading especially when the sysadmin has set up custom things. This docker image focus on the essential, the core of the mailstack, everything else is independent. @ksylvan's ansible playbooks is the perfect example for this. This is the point :)

I just when to mention @hardware I hope the

Everyone is free to choose and use what is needed beside this docker image depending on the sysadmin needs

will still be true ...

Yes.

AndrewSav commented 6 years ago

@hardware yes, you've explained your position well earlier in the thread and I accepted it. I was just struggling to understand what jodumont tried to contribute to the discussion.

I think we already discussed it more that it deserved it, moving on ;)

hardware commented 6 years ago

I switched to Traefik. For the moment, the mailserver does not support Let's Encrypt certificates obtained automatically with Traefik (works right now only with Rainloop, Postfixadmin and Rspamd WebUI), the acme.json storage support should arrive in the next few days. Feel free to give your feedback and make suggestions.

ghost commented 6 years ago

wonder if i can spin this up on kubernetes..... with the traffic proxy

On Sat, Jan 20, 2018 at 12:58 PM, hardware notifications@github.com wrote:

I switched to Traefik. For the moment, the mailserver does not support Let's Encrypt certificates obtained automatically with Traefik, the acme.json storage support should arrive in the next few days. Feel free to give your feedback and make suggestions.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hardware/mailserver/issues/188#issuecomment-359189859, or mute the thread https://github.com/notifications/unsubscribe-auth/ABXFTuQTImSS51Kp5H9XVHiIPQTtHuMZks5tMilLgaJpZM4QYkGR .

denji commented 6 years ago

@outbackdingo why not?