52

I have a docker container running PHP and Apache. The host is in an AWS instance which has the docker instance running. I am unable to send an email from the docker terminal. Is there any way to send an email from docker instance using sendmail which uses the docker's host's configuration?

The following command sends an email from host but doesn't send an email from docker instance. No error is given either.

echo "Subject: Testing Email" | cat - text | /usr/lib/sendmail -F abc.pqr@example.com -t abc.pqr@example.com
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
tarun mittal
  • 551
  • 1
  • 5
  • 7
  • 1
    Which MTA provides you with sendmail program? [sendmail/postfix/exim] – AnFi Oct 06 '14 at 13:18
  • sendmail-8.12+ is installed by default as set GROUP id. According to comment in http://stackoverflow.com/a/23157832/2139766 docker by default disallows running set group id programs. – AnFi Oct 06 '14 at 13:22
  • 1
    I hope this comment will help somehow : https://github.com/docker-library/php/issues/135#issuecomment-277199026 – Charaf Feb 03 '17 at 09:15

13 Answers13

30

What I do is to configure the host MTA to listen on docker0 and install ssmtp in the container to bridge sendmail in the container with the host MTA. The reason to run an MTA on the host is that system (critical) errors can be sent to the admin's mailbox. The reason to not run MTA in the container is that it is a duplicated process as the host system already runs an MTA.

On the host, I used postfix. All we need to do is to configure postfix to listen on docker0 and accept outgoing mails from Docker containers. Edit the file /etc/postfix/main.cf and add the docker0 IP address to inet_interfaces so it accepts connections from Docker containers. Also, add the network addresses of Docker containers to mynetworks so that Docker containers are legal to send mails via the postfix server on the host. (reference and more details)

To use sendmail in containers, install ssmtp and set FromLineOverride to be permitted and and mailhub to the IP address of the host in /etc/ssmtp/ssmtp.conf. You can set mailhub to be a symbol such as smtp-server and then run the container with --add-host option, as shown in this Dockerfile (run it with --add-host smtp-server:your-docker0-address). This would configure a usable sendmail in containers which would actually use the host MTA to send mails.

ki9
  • 5,183
  • 5
  • 37
  • 48
xuhdev
  • 8,018
  • 2
  • 41
  • 69
  • 5
    This works and gets a usable sendmail command line executable. But what exactly is the problem with the "real" sendmail executable. Unless I am getting something wrong, it should be possible to make it work without setting up your own smtp server - given dns mx lookups work and recipient MTAs are accessible. – Andreas Steffan May 05 '15 at 08:43
  • In addition to this I had to accept incoming connections on port 25 on the host from docker containers. With UFW: `sudo ufw allow from 172.17.0.0/16 to any port 25` – pawamoy May 14 '18 at 16:32
6

building on previous answers,
create config/sendmail_config.sh with:

#!/bin/sh
# set host in hosts
line=$(head -n 1 /etc/hosts)
line2=$(echo $line | awk '{print $2}')
echo "$line $line2.localdomain" >> /etc/hosts

yum install -y sendmail sendmail-cf m4 \
    && hostname >> /etc/mail/relay-domains \
    && m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf

#remove localhost limit
sed -i -e "s/Port=smtp,Addr=127.0.0.1, Name=MTA/Port=smtp, Name=MTA/g" \
    /etc/mail/sendmail.mc
sendmail -bd

change yum for apt-get on debian based containers

then in Dockerfile add:

RUN sed -i -e "s#;sendmail_path =#sendmail_path = /usr/sbin/sendmail -t -i#g"  \
    /your_path_to/php.ini
COPY ./config/sendmail_config.sh .

I want sendmail with my php util so I can stick it anywhere with out having to link to another MTA container or host to complete the task.
I run sh sendmail_config.sh, and then run my php util.

stuartz
  • 121
  • 4
  • 9
  • 1
    For me this worked after I replaced `echo "$line $line2.localdomain" >> /etc/hosts` with `echo "$line $line2.localdomain $(hostname)" >> /etc/hosts` – Erik Verheij Mar 28 '17 at 12:12
  • 1
    Doesn't works with docker-compose. /ect/hosts file is replace by docker at the starting of the container. Instead of use extra_hosts: - "localhost.localdomain:127.0.0.1" in docker-compose.yml – ratm Nov 25 '17 at 10:38
  • 1
    If controlled by supervisor, use `exec sendmail -bD` as start command – weynhamz Mar 30 '18 at 12:54
5

Nowhere in your Dockerfile is sendmail (or any other mail agent) installed. The host, however, apparently does have sendmail available. The "best" or most Docker-like solution is to spin up another container that runs an MTA (like postfix or exim), and configure your application to use that.

naneau
  • 489
  • 2
  • 4
  • 3
    Yep this can be done by defining '[Links](https://docs.docker.com/v1.8/compose/yml/#links)' in docker compose file. But I already got a nice working Postfix server running on the host, how can I connect from a Docker container to the hosts Postfix server (which also runs the docker container). – Melroy van den Berg Jun 16 '16 at 11:17
3

I figured out a way myself, although not the most elegant solution. I configured the sendmail inside my docker so as to Relay the request via host's ip. Add the following line to the file "/etc/mail/access

Connect:<host_ip_here>          RELAY

Also, in the host as well as docker, comment out the following line in the file "/etc/mail/sendmail.mc" by prefixing it with "dnl #" and suffixing with "dnl".

DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')

I passed the host ip as an environment variable to the docker container, so that it is configurable. Now the docker's sendmail will relay it's sendmail's smtp request via host machine.

tarun mittal
  • 551
  • 1
  • 5
  • 7
3

Adding a fully qualified domain name to the Docker host name in /etc/hosts does the trick for me:

{YourDockerIP} {YouDockerHostName}.localdomain {YouDockerHostName}

To me it looks like this:

172.17.0.25 77f5a7ae8606.localdomain 77f5a7ae8606

You can also use this bash script to automatically update this line:

#!/bin/bash
line=$(head -n 1 /etc/hosts | awk '{printf "%s %s.localdomain %s", $1, $2, $2}')
sed -e "1 s/^.*$/${line}/g" /etc/hosts > hosts
# with sed -i, it actually performs a rename of /etc/hosts, but docker does not
# allow that, so we have to use a temp file and copy it to overwrite /etc/hosts
cp hosts /etc/hosts
rm hosts

Reference: http://hjk41.azurewebsites.net/2015/09/25/using-sendmail-inside-docker/

hjk41
  • 239
  • 2
  • 10
  • 1
    As a comment on the source of your source implies, this does not work all the time: http://www.tothenew.com/blog/setting-up-sendmail-inside-your-docker-container/ Unfortunately, I do not know, why it for some users seem to work and for others not. – David Georg Reichelt Aug 18 '16 at 13:34
  • Easier way: `RUN echo -e "$(hostname -i)\t$(hostname) $(hostname).localhost" >> /etc/hosts` – Oscar Fanelli Oct 08 '16 at 21:05
  • I am one of the unfortunate ones where this does not work, running a Windows host. I can use the php mail function to get a response of 1 and I see the cached messages in `/var/spool/mqueue`, but they never reach the addresses they are sent to. – Spencer Williams Nov 20 '16 at 20:29
  • 1
    `echo -e "$(hostname -i)\t$(hostname) $(hostname).localhost" >> /etc/hosts` followed by a service restart `service sendmail restart` worked for me – jaciefan Jul 19 '18 at 15:08
3

According to the Sendmail manual we may use sendmail -bd command to run a service. But it exists with zero status. You need to put a container into a sleep mode (CentOS sleep infinity, or you may use supervisord instead).

Here is an example for CentOS. Set this in your Dockerfile:

# In your Dockerfile
RUN yum install -y sendmail
# Run mail listener
RUN sendmail -bd

CMD sleep infinity
# or run your command

I use this config for sendmail:

[program:sendmail]
command=/usr/sbin/sendmail -bd
autostart=true
autorestart=false
numprocs=1
startretries=0

And test it:

$ echo test123 | sendmail your@example.com

Don't forget to check Spam folder.

Kirby
  • 2,847
  • 2
  • 32
  • 42
3

Assuming there is a mail server installed and configured on host!

Alpine based docker images should have sendmail executable.

The simple solution is to run the container on host' network:

docker run --rm --net="host" php:fpm-alpine sh -c 'echo "Subject: test" | sendmail -v your@mail.example'

To run the container with default network bridge, configure mail server to listen on docker interface 172.17.0.1, and allow to relay emails from docker subnet 172.17.0.0/16.

Exim options affected in: /etc/exim4/update-exim4.conf.conf

dc_local_interfaces='127.0.0.1 ; ::1 ; 172.17.0.1'
dc_relay_nets='172.17.0.0/16'

Restart mail server and run the container verbose:)

docker run --rm --hostname="your-domain.example" php:fpm-alpine sh -c 'echo "Subject: test" | sendmail -v your@mail.example -S 172.17.0.1'
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Igor Sukharev
  • 2,467
  • 24
  • 21
  • This doesn't answer the question, you use this docker container as a glorified shell. What you have is the same as: `sh -c 'echo "Subject: test" | sendmail -v your@mail.example -S 172.17.0.1` – Berend de Boer Dec 16 '22 at 20:45
1

EDIT: Please see xuhdev answer for more info and how to set up mail forwarder. My answer can be used to setup sendmail instead of postfix on host.

EDIT #2: Add firewall rule to allow smtp traffic from docker

I have made a similiar setup as the tarun mittal as following:

  • in docker app I have set it up to use the smtp server with the ip of docker0 interface (172.17.42.1)
  • in docker host, modified the /etc/mail/sendmail.mc to include listening on the docker0 interface (in contrast to all interfaces in tarun's answer - bold is added line)

    DAEMON_OPTIONS(`Family=inet, Name=MTA-v4, Port=smtp, Addr=127.0.0.1')dnl

    DAEMON_OPTIONS(`Family=inet, Name=MTA-v4, Port=smtp, Addr=172.17.42.1')dnl

  • Allow the access from the docker containers to docker interface in firewall iptables -I INPUT -s 172.17.0.0/24 -d 172.17.42.1 -dport 25 -j ACCEPT

  • in /etc/mail/access I have added at the end to allow all docker instances to send emails and makemap hash /etc/mail/access < /etc/mail/access to compile database

    /// EDITED - USE BELOW AS DOCKER WILL GO TO 172.17.1.X after many rebuilds

    /// OLD - Connect:172.17.0 RELAY

    Connect:172.17 RELAY

  • Finally restart sendmail - service sendmail restart

zveljkovic
  • 444
  • 4
  • 16
1

I struggled with this issue as well. From my python code running inside a docker container, I needed to temporarily send emails using postfix (SMTP server) running on the host machine. After trying whole bunch of things, what turned out to be a simple solution was docker run command with --net="host". Caution: This may not be a good solution in all cases as such a container will share the network stack with the docker host and from the container point of view, localhost(or 127.0.0.1) will refer to the docker host. Here's what I did on an Ubuntu host: docker run -it --net="host" Ubuntu /bin/bash This gave me the container shell. I then installed python in this container: apt-get update && apt-get -y install supervisor python-pip

Then I launched the python interpreter and fired the following lines of code:

import smtplib
from email.mime.text import MIMEText

fromaddr = 'testemail.example.com'
toaddr = 'youremail.example.com'

msg = MIMEText('Sample email from python/docker container')
msg['from'] = fromaddr
msg['subject'] = 'Subject of python email'
msg['to'] = toaddr

server = smtplib.SMTP('localhost')
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddr, msg.as_string())
server.quit

I also found [this][1] a useful read.

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Dean Sha
  • 837
  • 1
  • 10
  • 15
1

I use an image that has postfix installed. I slightly need to tweek the configuration, because the default configuration (at least in Centos 8) does not work, because inside the container, "localhost" is unkown (a priori). So I just comment out one configline line in the Dockerfile after installing postfix:

RUN dnf install -y postfix \
    && sed -i "s/inet_interfaces = localhost/#inet_interfaces = localhost/" /etc/postfix/main.cf

Then I start the container using:

       --mount type=bind,source=/var/spool/postfix,target=/var/spool/postfix

Now, whenever I use sendmail within the container, it writes the email to var/spool/post-fix. Then the mail gets picked up by the host, who sends it off.

That way, I do not need to start (or configure) any further processes in my container (except for the one small fix of the postfix config).

jarauh
  • 1,836
  • 22
  • 30
0

You have to point inet_interfaces to docker bridge (docker0) in post fix config located at set /etc/postfix/main.cf

inet_interfaces =

More internal working detail at sending email from docker via postfix installed on host

Note: use ifconfig command to get the docker bridge address

Satish Gandham
  • 401
  • 4
  • 12
0

For me, just adding the --network=host flag on my docker run command worked. It should use the host to send the email.

0

I know this question has already been answered a few different ways, but I will include mine to expand upon. One way would be to create an SSH Tunnel to the host computer, this is if you are having issues getting the mail client to connect directly just using ssmtp.

This is more of a hack around if everything you have tried keeps failing.

You can create a script that connects to the SSH tunnel running on port 25 by using sockets in whatever language you are coding in, this is if ssmtp is not working or there are certificate issues. But make sure your container and your machine running on port 25 are not exposed to remote connections. Because you are going to be using your local machine as a relay server. Meaning your script does not have to verify that it is a user on your host computer. The only remote connections you should allow is the docker0 connection and that is added to /etc/postfix/main.cf (see inet_interfaces solution above).

The script can be written fairly easily. Just lookup how to send mail using telnet to get an idea of the handshake protocol for smtp.

If port 25 is closed off to the public (it is recommended to keep this port close), use a ssh tunnel "ssh user@yourdomain.club -L 25:localhost:25" this is the same ssh tunnel you will be using to access port 25 inside your container. You will have to add open-ssh to your Dockerfile because most light weight versions of linux will not include ssh. You might be able to use net cat as well instead of open-ssh (ssh command), that may come some distros by default. Also, be sure to add \r\n to each line in your script when you are connecting with sockets inside of your program. the \r stands for return and the \n stands for new line.

You are going to 1 create the tunnel 2 connect to the tunnel inside your script 3 send the mail commands from your script 4 disconnect from the tunnel

the tunnel will have to automatically open and reopen if it closes accidentally. Also, create an SSH key or use an expect -c bash script to enter in the password when creating the SSH tunnel. It is better in my opinion to have an SSH key added to the Docker container because expect -c scripts tend to have a slight delay waiting for the password prompt.