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

mail() does not work out of the box #135

Closed
coconitro opened this issue Sep 16, 2015 · 29 comments
Closed

mail() does not work out of the box #135

coconitro opened this issue Sep 16, 2015 · 29 comments

Comments

@coconitro
Copy link

It seems that the sendmail_from config setting isn't right? If additional configuration is required for this to work should this be noted in the documentation?

When running php -i | grep sendmail_path I get this

php -i | grep sendmail_path
sendmail_path =>  -t -i  =>  -t -i 

When running this script

<?php 
mail('[email protected]', 'Subject', 'Body');
?>

The results are this:

sh: 1: -t: not found
@md5
Copy link
Contributor

md5 commented Sep 17, 2015

The docs say this:

Where the sendmail program can be found, usually /usr/sbin/sendmail or /usr/lib/sendmail. configure does an honest attempt of locating this one for you and set a default, but if it fails, you can set it here.

Looks like configure is failing to set a value for sendmail_path since there is no sendmail binary installed when PHP is built.

@coconitro
Copy link
Author

I ended up trying to add sendmail or postfix to a container extending from php:5.6-apache and pointing the php.ini sendmail_path to them correctly but ran into the problem of multiple services running inside the container.

Ended up settling on using ssmtp to solve this. Here is a Dockerfile that should work with mail() out of the box.

FROM php:5.6-apache

RUN apt-get update && \
  apt-get install -y ssmtp && \
  apt-get clean && \
  echo "FromLineOverride=YES" >> /etc/ssmtp/ssmtp.conf && \
  echo 'sendmail_path = "/usr/sbin/ssmtp -t"' > /usr/local/etc/php/conf.d/mail.ini

mail() should work out of the box with this container. I'm not sure if this is something that should be modified and changed in the base php container or documented somewhere. Thoughts?

@md5
Copy link
Contributor

md5 commented Sep 17, 2015

I don't think it's possible to have a reasonable default configuration for mail(). In the case of ssmtp, you need to point it at an SMTP server (likely providing credentials). Trying to use an actual MTA like Postfix or Sendmail means that multiple services will be running in the container, as you mention.

Given that, I think that documenting the issue and possible ways to fix it is the best that can be done.

I previously opened an issue for the wordpress image where users have mentioned other configurations they've used to get mail() working: docker-library/wordpress#30

@coconitro
Copy link
Author

Good points. Seems the most Docker-like way to do this would be to have two containers, the php one and a MTA one, have ssmtp setup in the php one linked to the MTA one. Not the easiest thing in the world to document in a general manner. I'll close this for now since it doesn't seem like there are any actions that can be taken. Thanks for the insight.

@md5
Copy link
Contributor

md5 commented Sep 17, 2015

This PHP feature request is relevant (enable direct SMTP support on non-Windows systems), though it doesn't seem like they'll ever implement it: https://bugs.php.net/bug.php?id=29629

@stuartz-VernonCo
Copy link

stuartz-VernonCo commented Jan 29, 2016

It took me way too much time to research and empliment. Hope this helps someone else.

for simple solution that may be filtered by spam filters:
for more details, information used from: [http://www.tothenew.com/blog/setting-up-sendmail-inside-your-docker-container/]

Dockerfile bare-bones example:
FROM php:apache
'#' debian:jessie, apache on port 80, php:latest
'#' retrieve needed system programs
RUN apt-get update && apt-get upgrade -y sendmail && rm -rf /var/lib/apt/lists/*
COPY ./app/ /var/www/html/
'#' to be ran with docker exec later
COPY ./config_files/mail_config.sh /var/www'
'#' php:apache recommends using your own php.ini
COPY ./config_files/php.ini /usr/local/etc/php/php.ini

.config_files/mail_config.sh example:
line=$(head -n 1 /etc/hosts)
line2=$(echo $line | awk '{print $2}')
echo "$line $line2.localdomain" >> /etc/hosts
/etc/init.d/sendmail start
sleep 1
service apache2 graceful

add to your ./config_files/php.ini:
sendmail_path = /usr/sbin/sendmail -t -i

Then run the following to initiate it after starting container
or put as a CMD in Dockerfile if yours doesn't already have a CMD.

docker exec container_name bash /var/www/mail_config.sh

EDIT May 16, 2019

added ability to send as desired domain (see replace MASQUERADE_AS)

previous EDIT Feb 10,2017

I had another program that was editing the /etc/hosts file at start up, and so I changed my mail_config.sh The change also made it possible to not have to edit /etc/mail/sendmail.mc to remove localhost access thanks to a hint from @charafsalmi.

** /mail_config.sh

#!/bin/sh
#add host to /etc/hosts
host=$(hostname)
line=$(cat /etc/hosts |grep [1]27.0.0.1)
#placed at end to prevent being changed by weave
echo "$line [email protected] $host" >> /etc/hosts

#or if file is not being changed by other applications
#sed -i -e "s/$line/$line [email protected] $host/g" /etc/hosts

#finally
echo "$host" >> /etc/mail/relay-domains
replace MASQUERADE_AS with your domain to have it send mail out as the desired domain
line=$(cat /etc/mail/sendmail.mc | grep [M]ASQUERADE_AS)
's/^"$line".*/MASQUERADE_AS(`YOURDOMAIN.COM')dnl/' /etc/mail/sendmail.mc
m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf
#start service
service sendmail restart # or sendmail -bd

@charafsalmi
Copy link

@stuartz-VernonCo do you know wether there is anything more to do to get it working with docker php-fpm or not ?

Everytime I send a mail I get a 504 error.

@stuartz-VernonCo
Copy link

@charafsalmi Time out waiting for a response (504) could be an issue with firewall not allowing the connection back in, or on AWS not allowing emails to be sent out on port 25 to prevent spam. Could be a number of things. On AWS, I used a queue on a database for applications sending out email, and smtp the emails through our office365.
Have you tried the container inside your LAN to send to a local email? Remove the network obstacles and if it works, focus on the network rules that are keeping it from working else where.

@charafsalmi
Copy link

charafsalmi commented Feb 3, 2017

@stuartz-VernonCo thank you !

it took me wayyyy so much time to find out why it wasn't working… it was not about the firewall, it was about what sendmail is expecting us to put in /etc/hosts

The last line of /etc/hosts has to contain this structure : [IP] "your.domain.com" [HOSTNAME]

So, once I changed my /etc/hosts to 127.0.0.1 noreply.domain.com e0dd810b0efd and restarted docker, it worked.

It's sad that sendmail doesn't give any clue about this.

# echo "127.0.0.1 noreply.domain.com $(hostname)" >> /etc/hosts

@zerowebcorp
Copy link

@charafsalmi You are a lifesaver.

@partounian
Copy link

Just to confirm @charafsalmi all we need to do is run that last line on our docker container?

@charafsalmi
Copy link

@partounian Yes sir.

@rburkovsky
Copy link

It wasn't enough for me. I had to add

RUN sed -i 's@local@internet@' /etc/exim4/update-exim4.conf.conf \
  && update-exim4.conf

to be able to send emails to external addresses (like gmail).

But even after that, I am still getting errors in /var/log/exim4/mainlog

SMTP error from remote mail server after end of data: The IP you're using to send mail is not authorized to\n550-5.7.1 send email directly to our servers. 
Please use the SMTP relay at your\n550-5.7.1 service provider instead. 
Learn more at\n550 5.7.1  https://support.google.com/mail/?p=NotAuthorizedError

It's probably not in the scope of this issue, since mail function is working, it's more about sendmail configuration.
But that's something to be aware of.

@adriansecretsource
Copy link

@m0onspell You just saved my day!

I had problems trying to configure exim4 on a Rails Docker container to be able to send emails, but your code just did the trick.

Now its working with this commands.

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs exim4
RUN sed -i 's@local@internet@' /etc/exim4/update-exim4.conf.conf \
  && update-exim4.conf
...

@drewwestphal
Copy link

drewwestphal commented Jan 26, 2018

I solved this problem with MSMTP, sendgrid (works for us, you could pick another service, obv) and some environment variables, which I believe is pretty lightweight. I stumbled across this thread and the answers here helped me, so here's what I did for the next person:

DOCKERFILE

RUN apt-get update && apt-get install -y msmtp && apt-get clean && rm -r /var/lib/apt/lists/*
# configure sendmail
RUN { \
    	echo "php_admin_value[sendmail_path] = $(which msmtp) -ti "; \
    } >> /usr/local/etc/php-fpm.d/www.conf

# configure entrypoint
COPY fpm-entrypoint.sh /usr/local/bin
ENTRYPOINT ["fpm-entrypoint.sh"]

ENTRYPOINT

#!/bin/bash
set -euo pipefail

cat >> /etc/msmtprc <<-EOCFG
account         default
host         smtp.sendgrid.net
port         587
timeout         30
auth         on
tls          on
tls_starttls    on
tls_trust_file     /etc/ssl/certs/ca-certificates.crt
syslog          on

auto_from       off
user         ${MTA_SENDGRID_USER}
password        ${MTA_SENDGRID_PASS}
from         ${MTA_SENDGRID_FROM}
maildomain      ${MTA_SENDGRID_MAILDOMAIN}
EOCFG


exec "$@"

@LaQuay
Copy link

LaQuay commented Feb 5, 2018

@stuartz-VernonCo Thanks!

Also, if you are having problems if the docker-container is always restarting after doing what he says, just make sure to call your php server again

Example, this inside the Dockerfile:
CMD /var/www/mail_config.sh && php-fpm

@michelalbers
Copy link

michelalbers commented Feb 9, 2018

Well here is my simple solution:

Dockerfile (for your custom php image)

FROM php:fpm
RUN apt-get update && apt-get install -y sendmail
ADD ./php.sh /opt/php.sh
ADD sendmail.ini /usr/local/etc/php/conf.d/
RUN chmod u+x /opt/php.sh
WORKDIR /opt
CMD ["php.sh"]

sendmail.ini

# Enable sendmail
sendmail_path = "/usr/sbin/sendmail -t -i"

php.sh

#! /bin/sh
line=$(head -n 1 /etc/hosts)
line2=$(echo $line | awk '{print $2}')
echo "$line $line2.localdomain" >> /etc/hosts
/etc/init.d/sendmail start
php-fpm

Be sure to specify a hostname inside your docker-compose.yml or when running the container, e.g.:

docker run -it --hostname foobar acme/php

or

docker-compose.yml

version: '2'
services:
  phpfpm:
    image: acme/php
    container_name: "my_php_fpm"
    hostname: "foobar"

Thats it - php mail() will work now. It's propably possible with alpine, too - but I did not manage to get that working yet. Feel free to enhance my solution ;)

@unoexperto
Copy link

I dunno guys. ssmtp didn't work for me out of the box. I get ssmtp: Cannot open mailhub:25 errors in the log. It seems like it ignores connection settings set by php code (MantisBT in my case).

@okainov
Copy link

okainov commented Aug 19, 2018

Is there any update on it? Issue is still here...

@ukpasupport
Copy link

I am trying to search for a solution for this as the issue is still present

@WilliamDenniss
Copy link

WilliamDenniss commented Sep 29, 2018

Here's a simple way to enable PHP to send mail via a SMTP provider. Unless you want to manage your own sendmail service, and worry about the mail sending reputation of your host, etc, this is the way to go. You can replace the Sendgrid details in the example config with the SMTP provider of your choice (if you do, be sure to also update the port number, and note that some cloud providers block outbound port 25).

One caveat is that the script will block on the mail() call until the email has been accepted by the SMTP provider, it's not async out of the box.

Dockerfile

FROM php:5.6-apache

RUN apt-get update && \
   apt-get install -y ssmtp && \
   apt-get clean

# SSMTP settings
COPY ssmtp.conf /etc/ssmtp/ssmtp.conf
# PHP mail settings
RUN echo 'sendmail_path = "/usr/sbin/ssmtp -t -i"' > /usr/local/etc/php/conf.d/mail.ini

ssmtp.conf

# Sendgrid setup
# https://sendgrid.com/docs/for-developers/sending-email/ssmtp/
mailhub=smtp.sendgrid.net:587
AuthUser=<username>
AuthPass=<password>
UseSTARTTLS=YES
# Allow the "From" email header.
FromLineOverride=YES

@rmartinsjr
Copy link

Hi @WilliamDenniss! Thanks for sharing this solution. Tried myself after following your recipe and for me it's the neatest way to replicate sendmail function in containerized php applications.

@ooaklee
Copy link

ooaklee commented Dec 10, 2018

Hey @WilliamDenniss, I used your solution with the official WordPress docker image, and it worked flawlessly. Thanks!

@Nimrod-666
Copy link

Nimrod-666 commented Jan 7, 2019

Hi there. I tried the ssmtp solution as you guys recommended and i got the issue that a testmail is sendable from my host system, but not inside the php:7.3-apache-stretch container.

Here are my configs (i replaced my domain with xxx):

#/etc/ssmtp/ssmtp.conf
root=[email protected]
mailhub=smtp.goneo.de:587
rewriteDomain=xxx.de
hostname=xxx.de
FromLineOverride=YES
AuthUser=[email protected]
AuthPass=xxx
UseTLS=YES

#/etc/ssmtp/revaliases
root:[email protected]:smtp.goneo.de:587
www-run:[email protected]:smtp.goneo.de:587

Now i try to send a testmail:

root@webserver:/var/www/html# echo "Subject: Test" | /usr/sbin/ssmtp -f [email protected] -vvv [email protected]
[<-] 220 smtp3.goneo.de ESMTP
[->] EHLO xxx.de
[<-] 250 DSN
[->] AUTH LOGIN
[<-] 334 VXNlcm5hbWU6
[->] bm9yZXBseUBibGFreWxsZS5kZQ0=
[<-] 535 5.7.8 Error: authentication failed: VXNlcm5hbWU6
ssmtp: Server didn't accept AUTH LOGIN (535 5.7.8 Error: authentication failed: VXNlcm5hbWU6)

As i already mentioned, it works flawless on the hostsystem with the same configfiles:

root@webdev:~# echo "Subject: Test" | /usr/sbin/ssmtp -f [email protected] -vvv [email protected]
[<-] 220 smtp1.goneo.de ESMTP
[->] EHLO xxx.de
[<-] 250 DSN
[->] STARTTLS
[<-] 220 2.0.0 Ready to start TLS
[->] EHLO xxx.de
[<-] 250 DSN
[->] AUTH LOGIN
[<-] 334 VXNlcm5hbWU6
[->] bm9yZXBseUBibGFreWxsZS5kZQ==
[<-] 334 UGFzc3dvcmQ6
[<-] 235 2.7.0 Authentication successful
[->] MAIL FROM:[email protected]
[<-] 250 2.1.0 Ok
[->] RCPT TO:[email protected]
[<-] 250 2.1.5 Ok
[->] DATA
[<-] 354 End data with .
[->] Received: by xxx.de (sSMTP sendmail emulation); Mon, 07 Jan 2019 18:56:22 +0100
[->] From: "root" [email protected]
[->] Date: Mon, 07 Jan 2019 18:56:22 +0100
[->] Subject: Test
[->]
[->] .
[<-] 250 2.0.0 Ok: queued as C202323F197
[->] QUIT
[<-] 221 2.0.0 Bye

The version of both ssmtps is 2.64. What's strange is, that it seems that the hostsystem begins with a STARTTLS handshake, and the container don't.

Has anybody a hint for me?

@WilliamDenniss
Copy link

@Nimrod-666 just a guess, but is the port correct in your config? My example used port 587 for sendgrid to avoid the issue of port 25 being blocked on GCP, but you may need to update that with the port number of the SMTP server you are using.

Note that some cloud providers block or throttle port 25, so you may need to use a different port if this affects you (as this can result in the container being able to send mail successfully locally, but then not when you deploy it to the cloud).

I've updated my original comment with that information.

@angeloreale
Copy link

Well here is my simple solution:

Dockerfile (for your custom php image)

FROM php:fpm
RUN apt-get update && apt-get install -y sendmail
ADD ./php.sh /opt/php.sh
ADD sendmail.ini /usr/local/etc/php/conf.d/
RUN chmod u+x /opt/php.sh
WORKDIR /opt
CMD ["php.sh"]

sendmail.ini

# Enable sendmail
sendmail_path = "/usr/sbin/sendmail -t -i"

php.sh

#! /bin/sh
line=$(head -n 1 /etc/hosts)
line2=$(echo $line | awk '{print $2}')
echo "$line $line2.localdomain" >> /etc/hosts
/etc/init.d/sendmail start
php-fpm

Be sure to specify a hostname inside your docker-compose.yml or when running the container, e.g.:

docker run -it --hostname foobar acme/php

or

docker-compose.yml

version: '2'
services:
  phpfpm:
    image: acme/php
    container_name: "my_php_fpm"
    hostname: "foobar"

Thats it - php mail() will work now. It's propably possible with alpine, too - but I did not manage to get that working yet. Feel free to enhance my solution ;)

I had to update this solution in order to make it work around here.

Added these lines to php.sh:

echo "$(hostname -i)\t$(hostname) $(hostname).localhost" >> /etc/hosts
echo "O HostsFile=/etc/hosts" >> /etc/mail/sendmail.cf

As well as defining an ENTRYPOINT in Dockerfile for php.sh:
ENTRYPOINT ["/opt/php.sh"]

@GrrrDog
Copy link

GrrrDog commented Mar 8, 2019

The previous configs didn't work for me due various reasons.
Came to the next solution:
Dockerfile

FROM php:apache
RUN apt-get update && \
  apt-get install -y exim4 &&\
  echo 'sendmail_path = "/usr/sbin/exim4 -t"' >> /usr/local/etc/php/conf.d/mail.ini && \
  echo 'SMTP = localhost' >> /usr/local/etc/php/conf.d/mail.ini && \
  echo 'smtp_port = 25' >> /usr/local/etc/php/conf.d/mail.ini 
COPY ./config/exim4.conf /etc/exim4/exim4.conf
RUN chmod 644 /etc/exim4/exim4.conf

exim4.conf

primary_hostname = anynameyouwant.io
hostlist legit_sending_hosts = 127.0.0.1 

local_interfaces = <; ::0 ; 0.0.0.0
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d
split_spool_directory = true
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data

#                       ACL CONFIGURATION                            #
begin acl
acl_check_rcpt:
  accept  hosts = :
          control = dkim_disable_verify
  deny    message       = Restricted characters in address
          local_parts   = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
  accept  hosts         = +legit_sending_hosts
          control       = submission
          control       = dkim_disable_verify
  accept  authenticated = *
          control       = submission
          control       = dkim_disable_verify
  deny    message       = "You are not allowed to send email"
acl_check_data:
  accept

#                      ROUTERS CONFIGURATION                         #
begin routers
dnslookup:
  driver = dnslookup
  transport = remote_smtp
  ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
  no_more

#                      TRANSPORTS CONFIGURATION                      #
begin transports
remote_smtp:
  driver = smtp

#                      RETRY CONFIGURATION                           #
begin retry
*                      *           F,2h,15m; G,16h,1h,1.5; F,4d,6h
#                   AUTHENTICATION CONFIGURATION                     #
begin authenticators

It's based on rickw/debian-exim-send
Hope it will help someone

@jonhattan
Copy link

jonhattan commented Mar 18, 2020

In our setup we've a host with smtp configured to relay to mandrill. We want the containers to leverage this config. The simplest setup we've found is:

In the host

iptables rule

-A INPUT -s 172.17.0.0/16 -p tcp -m multiport --dports 25 -m comment --comment "120 Postfix connections from docker" -j ACCEPT

postfix:

main.cf:

-mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
+mynetworks = 127.0.0.0/8 172.17.0.0/16 [::ffff:127.0.0.0]/104 [::1]/128
+local_header_rewrite_clients = permit_inet_interfaces permit_mynetworks

master.cf:

-127.0.0.1:smtp      inet  n       -       n       -       -       smtpd
+smtp      inet  n       -       n       -       -       smtpd

In the Dockerfile

apt-get install msmtp

php.ini

sendmail_path="/usr/bin/msmtp --host 172.17.0.1 --port 25 --from=docker-`id -u` -ti" 

We add the user id in the --from for traceability. An alternative is to replace --from with --auto-from=on.


hope this helps someone.

@tashrifbillah
Copy link

Hi @charafsalmi ,

So, once I changed my /etc/hosts to 127.0.0.1 noreply.domain.com e0dd810b0efd and restarted docker, it worked.

I tried this but didn't help. Also, the instruction isn't clear. If I am modifying /etc/hosts within the docker container, how would I be able to restart that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests