Docker: how to use postfix from host

Hello,

I’ve installed Nextcloud using the official docker image.
Now I want to use the postfix from the host to relay the mails from the docker container. I’ve used this guide to change configuration to expose postfix to the Docker network, but so far I haven’t been able to send mails from Nextcloud (fyi: It is working on the host).

This is what I’ve done:

/etc/postfix/main.cfg

mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 172.17.0.0/16 192.168.32.0/20
mailbox_command = procmail -a "$EXTENSION"
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = 172.17.0.1
inet_protocols = ipv4


# specify SMTP relay host
relayhost = smtp.mail.yahoo.com:587
# enable SASL authentication
smtp_sasl_auth_enable = yes
# disallow methods that allow anonymous authentication.
smtp_sasl_security_options = noanonymous
# where to find sasl_passwd
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
# Enable STARTTLS encryption
smtp_use_tls = yes
# where to find CA certificates
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
# change sender mail address
sender_canonical_maps = regexp:/etc/postfix/sender_canonical

inet_interfaces = the IP address of docker0 interface using command ifconfig
mynetworks = 192.168.32.0/20 : the subnet used by my Nextcloud docker container

Here are the mail settings in config.php from Nextcloud:

  'mail_domain' => 'yahoo.com',
  'mail_from_address' => 'my.cloud',
  'mail_smtpmode' => 'smtp',
  'mail_sendmailmode' => 'smtp',
  'mail_smtpport' => '25',
  'mail_smtphost' => '127.0.0.1',

I’m having 2 questions:

  1. which IP should I use for mail_smtphost ? I’ve tried with localhost, private IP of nextcloud container, private IP of the host…

  2. Should I expose port 25 from the host to the container (e.g. docker run -p 25:25 )?

Any help is appreciated

thanks

FYI:I’ve also opened port 25 on the host in iptables

it doesn’t work this way -p 25:25 maps port 25 of the container to the port 25 of the host - which obviously doesn’t work as it is used already (most likely your container doesn’t start with such setting)

localhost and private IP/fqdn of the container are the same (settings are applied to the the container, so all IPs and hostnames are from the container point of view). the right setting would be IP/fqdn of the host. According to the tutorial you referenced:

Instead of providing the known SMTP server’s IP and host, one should use the IP of docker0, as explained above. In the case of many nodes in Kubernetes cluster with different docker0 IP, the Docker container of Jenkins master should reside only on one host and docker0’s IP on that host should be used.

you need to your 172.x.x.x address of the docker0 interface (previously configure postfix to listen on this interface as well) - see "Modified “/etc/postfix/main.cf” in your tutorial

inet_interfaces = localhost, 172.22.91.1

I remember there are restrictions in place when docker container talks to the host but don’t have any reference now… try searching for "docker container access host ip"or similar…

Indeed, I totally overlooked this. Oddly, I tried it before and my container did start without any warning that the port was already in use.

After some testing using nmap in the container I figured out I could use the IP address of docker0 to send mail (Note: you can also use the private IP of the host but for this you first need to add this IP to the inet_interfaces in main.cf - but it doesn’t make any difference)

Anyway I can now send test mails from my Nextcloud instance.
However: when sharing a file or adding a note I got the following error in mail.log:

Apr  8 15:47:28  postfix/smtpd[3227107]: connect from unknown[172.24.0.3]
Apr  8 15:47:28  postfix/smtpd[3227107]: 24BACFD9B4: client=unknown[172.24.0.3]
Apr  8 15:47:28  postfix/cleanup[3227111]: 24BACFD9B4: message-id=<32ced750fc384e5a246175adf17184dc@my.cloud>
Apr  8 15:47:28  postfix/qmgr[3227061]: 24BACFD9B4: from=<my.cloud@yahoo.com>, size=20673, nrcpt=1 (queue active)
Apr  8 15:47:28  postfix/smtpd[3227107]: disconnect from unknown[172.24.0.3] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
Apr  8 15:47:30  postfix/smtp[3227112]: 24BACFD9B4: to=<recipient@gmail.com>, relay=smtp.mail.yahoo.com[74.6.228.44]:587, delay=2.2, delays=0.01/0.02/1.9/0.32, dsn=5.0.0, status=bounced (host smtp.mail.yahoo.com[74.6.228.44] said: 550 Request failed; Mailbox unavailable (in reply to end of DATA command))
Apr  8 15:47:30  postfix/cleanup[3227111]: 5DFB9FD9B6: message-id=<20220408134730.5DFB9FD9B6@.mynet.oraclevcn.com>
Apr  8 15:47:30  postfix/bounce[3227113]: 24BACFD9B4: sender non-delivery notification: 5DFB9FD9B6
Apr  8 15:47:30  postfix/qmgr[3227061]: 5DFB9FD9B6: from=<my.cloud@yahoo.com>, size=22894, nrcpt=1 (queue active)
Apr  8 15:47:30  postfix/qmgr[3227061]: 24BACFD9B4: removed

(FYI: 172.24.0.3 is the IP of the nextcloud container)

This is very similar to the error I got last year using the same mail provider (yahoo) but using SMTP directly to yahoo.

It does seems strange to my as an 550 error means that the mailbox of the recipient does not exist, but this is not the case here obviously. How can I solve this, or should I create a separate post for this?

I have no idea about postfix but the following log

looks for me like you postfix accepted the message and forwarded it to yahoo, which in turn rejected the message (most likely to avoid open relay). I assume your postfix tried to send the message to yahoo without authentication…

According to this post - yahoo doesn’t like certain Reply To headers.

I’ve compared my mail headers, see below:

Test mail (=OK)

Received: from mycloud.com (unknown [172.24.0.3])
	by myserver.oraclevcn.com (Postfix) with ESMTP id 008BAFD996
	for <nextcloudadmin@gmail.com>; Fri,  8 Apr 2022 16:19:29 +0200 (CEST)
Message-ID: <46a2ffd86aa4b51d6466488bc3e7f1d6@mycloud.com>
Subject: Mail test
From: My Cloud <my.cloud@yahoo.com>
To: admin <nextcloudadmin@gmail.com>
MIME-Version: 1.0
Content-Type: multipart/alternative;
 boundary="_=_swift_1649427569_1118be7e5e0306759d8028d78f8e50b2_=_"
X-SMTP-MAILFROM: my.cloud@yahoo.com
X-SMTP-RCPTTO: nextcloudadmin@gmail.com
References: <46a2ffd86aa4b51d6466488bc3e7f1d6.ref@mycloud.com>
X-Mailer: WebService/1.1.20001 mail.backend.jedi.jws.acl:role.jedi.acl.token.atz.jws.hermes.yahoo
Content-Length: 15170

Share by mail (bounced with 550 error)

Return-Path: <my.cloud@yahoo.com>
Received: from mycloud.com (unknown [172.24.0.3])
	by myserver.oraclevcn.com (Postfix) with ESMTP id 24BACFD9B4
	for <user@gmail.com>; Fri,  8 Apr 2022 15:47:28 +0200 (CEST)
Message-ID: <32ced750fc384e5a246175adf17184dc@mycloud.com>
Date: Fri, 08 Apr 2022 13:47:28 +0000
Subject: "admin" voegde een notitie toe aan een bestand dat met jou is
 gedeeld
From: admin via My Cloud <my.cloud@yahoo.com>
Reply-To: admin <nextcloudadmin@gmail.com>
To: admin@gmail.com
MIME-Version: 1.0
Content-Type: multipart/alternative;
 boundary="_=_swift_1649425648_db2e47324bf516359e593dd241515197_=_"

In the failed mail I do see a Reply-To mailadres which is different than the mail that the mail is send from. Maybe this causes the 550 mailbox unavailable error?
If so, this could be possibly solved by adding a fixed Reply-To header with the nextcloud mailadres (in my example my.cloud@yahoo.com)?
(I don’t know whether this is possible but I assume it is)

Edit:
I’ve checked with my other Nextcloud instance which is installed natively and also uses postfix.
When sending a mail by share by mail I notice that the Reply-To is indeed the mailadres used by Nextcloud to send the mails (instead of the mailadres of the user sharing the file. Thus guess the solution is indeed to somehow change the Reply-To headers?

Date: Fri, 08 Apr 2022 18:26:57 +0000
Subject: share by mail
From: user via Other-Cloud <other.cloud@yahoo.com>
Reply-To: user <other.cloud@yahoo.com>
To: otheruser@hotmail.com
MIME-Version: 1.0
Content-Type: multipart/alternative;
 boundary="_=_swift_1649442417_0990c404215067f6fb0cde319f5552bb_=_"
References: <0de5c8a424b825165d7ef13378e1ba40.ref@wouter.servebeer.com>
X-Mailer: WebService/1.1.20001 mail.backend.jedi.jws.acl:role.jedi.acl.token.atz.jws.hermes.yahoo
Content-Length: 19904

Edit 2:
Seems that I was right. I used this to replace my Reply-To headers in my mail with the Nextcloud-mail address & now I don’t receive any 550 error anymore but 250 OK :slight_smile:
Only downside is that the mails are delivered to the spam box from the recipient, but guess this is due to the nature of the automatically send mail.

I’ll send another post with the full solution of my 2 problems, guess it can help others who are using free mail services.

kr

OK so here is what I’ve done to make postfix listen to my docker container:
add/change the following lines to /etc/postfix/main.cfg:

  • mynetworks = add the IP address range of your nextcloud docker container e.g. 172.24.0.0/16

Tip: add the folowing to your docker-compose.yml to add a static IP address subnet to your docker container:

networks:
  nextcloud_network:
    ipam:
      config:
       - subnet: 172.24.0.0/16
  • inet_interfaces = add the IP address of your docker0 interface e.g. 172.17.0.1
    (user ip addr show of ifconfig and note the IP address corresponding to docker0, you don’t need to enter the mask e.g. /16)

Then restart your postfix service (e.g. sudo service postfix restart). If you have a firewall like ufw or iptables active, make sure you add rules to accept connections from your Nextcloud container IP to port 25.

Now you can add add the following to your Nextcloud config.php:

  'mail_domain' => 'mail.com',
  'mail_from_address' => 'my.cloud',
  'mail_smtpmode' => 'smtp',
  'mail_sendmailmode' => 'smtp',
  'mail_smtpport' => '25',
  'mail_smtphost' => '172.17.0.1', //the IP address of docker0

For server address use the IP address of your docker0 interface and all should work

Sorry for bumping, but where exactly did you add the

networks:
  nextcloud_network:
    ipam:
      config:
       - subnet: 172.24.0.0/16

in your docker compose? I added it like this:

services:
    all-in-one:
        init: true
        container_name: nextcloud-aio-mastercontainer
        restart: always
        ports:
            - 8080:8080
# Here:
        networks:
            - nextcloud_network
        volumes:
            - nextcloud_aio_mastercontainer:/mnt/docker-aio-config
            - /var/run/docker.sock:/var/run/docker.sock:ro
        environment:
            - NEXTCLOUD_DATADIR=/srv/ncdata
            - APACHE_PORT=11000
            - APACHE_IP_BINDING=127.0.0.1
        image: nextcloud/all-in-one:latest
# And here:
networks:
    nextcloud_network:
        ipam:
          config:
            - subnet: 172.25.0.0/16
volumes:
    nextcloud_aio_mastercontainer:
        external: true
        name: nextcloud_aio_mastercontainer

But running:

sudo docker exec -it nextcloud-aio-nextcloud bash
ip a

Still gives me 172.18.0.1/16…

At first glanse, this looks fine to me. FYI below is my docker.compose.yml

services:

  db:
    image: linuxserver/mariadb:10.6.13
    container_name: nextcloud-db
    networks:
      - network
    volumes:
      - ./mount/db/:/config
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Brussels
      - MYSQL_ROOT_PASSWORD=
      - MYSQL_PASSWORD=
      - MYSQL_DATABASE=
      - MYSQL_USER=
      - log_bin_trust_function_creators=true
    restart: unless-stopped

  redis:
    image: redis
    container_name: nextcloud-redis
    restart: unless-stopped
    networks:
      - network
    command: redis-server --save 20 1 --loglevel warning --requirepass <password>

  nextcloud:
    build:
      context: .
      dockerfile: dockerfile
    container_name: nextcloud-app
    networks:
      - network
    ports:
      - 8080:80
    depends_on:
      - db
      - redis
    volumes:
      - nextcloud_restore:/var/www/html
      - ./mount/nc/config:/var/www/html/config
      - ./mount/nc/custom_apps:/var/www/html/custom_apps
      - ./nc-data:/Nextcloud-data
      - ./mount/nc/themes:/var/www/html/themes
      - /etc/localtime:/etc/localtime:ro
      - type: tmpfs
        target: /tmp
    tmpfs:
      - /tmp:exec
    environment:
      - REDIS_HOST=nextcloud-redis
      - REDIS_HOST_PASSWORD=
      - MYSQL_PASSWORD=
      - MYSQL_DATABASE=
      - MYSQL_USER=
      - MYSQL_HOST=db
      - NEXTCLOUD_ADMIN_USER=
      - NEXTCLOUD_ADMIN_PASSWORD=
      - NEXTCLOUD_DATA_DIR=/Nextcloud-data
      - PHP_MEMORY_LIMIT=2048M
    restart: unless-stopped

volumes:
  nextcloud_restore:

networks:
  network:
    ipam:
      config:
       - subnet: 172.24.0.0/24

So the only differences I can spot:

  • I’m using a /24 subnet, not sure whether this will make a difference (/24 is big enough anyway)
  • The networks segment is at the bottom of my compose file. Again I don’t know if this will make a difference

If above doesn’t solve, maybe you could also investigate your nextcloud network ($ docker network inspect nextcloud_nextcloud_network I suppose)

I actually got an answer here: Explicitly define nextcloud-aio docker network subnet · nextcloud/all-in-one · Discussion #5698 · GitHub
according to that the network must be called “nextcloud-aio”.

I was confused as my docker compose just creates another network, the previous still being used. One might need to recreate the network as shown here. (I was lazy and just kept what it was set to before, seems to work)

Hi,

More intel regarding my problem.

Sending an e-mail with openssl from the container does work.

I entered a shell with:

docker exec -it nextcloud-aio-nextcloud sh

And then

/var/www/html # openssl s_client -starttls smtp -connect 172.17.0.1:25
...
read R BLOCK
MAIL FROM: nextcloud@entrepriseduweb.com
250 2.1.0 Ok
...

Message sent and received.

Hi there,

First of all thank you for these intels which help moving one step forward.

But after days of searching and trying I still can’t send the test e-mail from Nextcloud.

Here’s what I’ve done in postfix main.cfg:

inet_interfaces = localhost, 172.17.0.1 # docker0
mynetworks = 127.0.0.0/8, 172.18.0.0/16 # nextcloud-aio network, nextcloud-aio-nextcloud being 172.18.08

In Nextcloud admin I’ve set:

  • SMTP
  • none/STARTTLS
  • 172.17.0.1:25

I’ve also installed and configured a Let’s Encrypt certificate with Cerbot for the host.

I even tried added 172.18.0.8 nextcloud.domain.tld in the host’s hosts (huhu!).

Here’s the maillog:

postfix/smtpd[1502121]: initializing the server-side TLS engine
postfix/smtpd[1502121]: connect from nextcloud.domain.tld[172.18.0.8]
postfix/smtpd[1502121]: setting up TLS connection from nextcloud.domain.tld[172.18.0.8]
postfix/smtpd[1502121]: nextcloud.domain.tld[172.18.0.8]: TLS cipher list "aNULL:-aNULL:HIGH:MEDIUM:+RC4:@STRENGTH"
postfix/smtpd[1502121]: SSL_accept:before SSL initialization
postfix/smtpd[1502121]: SSL_accept:before SSL initialization
postfix/smtpd[1502121]: SSL_accept:SSLv3/TLS read client hello
postfix/smtpd[1502121]: SSL_accept:SSLv3/TLS write server hello
postfix/smtpd[1502121]: SSL_accept:SSLv3/TLS write change cipher spec
postfix/smtpd[1502121]: SSL_accept:TLSv1.3 write encrypted extensions
postfix/smtpd[1502121]: SSL_accept:SSLv3/TLS write certificate
postfix/smtpd[1502121]: SSL_accept:TLSv1.3 write server certificate verify
postfix/smtpd[1502121]: SSL_accept:SSLv3/TLS write finished
postfix/smtpd[1502121]: SSL_accept:TLSv1.3 early data
postfix/smtpd[1502121]: SSL_accept:TLSv1.3 early data
postfix/smtpd[1502121]: SSL_accept:SSLv3/TLS read finished
postfix/smtpd[1502121]: nextcloud.domain.tld[172.18.0.8]: Issuing session ticket, key expiration: 1739959039
postfix/smtpd[1502121]: SSL_accept:SSLv3/TLS write session ticket
postfix/smtpd[1502121]: Anonymous TLS connection established from nextcloud.domain.tld[172.18.0.8]: TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256
postfix/smtpd[1502121]: lost connection after STARTTLS from nextcloud.domain.tld[172.18.0.8]
postfix/smtpd[1502121]: disconnect from nextcloud.domain.tld[172.18.0.8] ehlo=1 starttls=1 commands=2

Any hint would be greatly appreciated.

King regards,

Hello,

So it works if I disable TLS in Postfix with

smtpd_tls_security_level = none

But doesn’t if it’s set to « may » (Opportunistic) or « encrypt » (Mandatory).

Why not? That is the question.