Отправка почты из Docker контейнера через ISPConfig3 хоста Postfix MTA
Используйте IP-адрес моста докер0 для подключения к сети Postfix
В бесконечном количестве проблем, с которыми вы сталкиваетесь и решаете, когда начинаете использовать новую технологию, я сталкиваюсь с новой: как отправлять электронную почту из моего приложения с помощью ISPConfig хоста (Mail Transfer Agent). Я нашел два способа сделать это:
- Отправка почты из нашего контейнера на адрес хоста, на котором она прослушивается
- Запишите почтовый файл в каталог на хосте и используйте скрипт для переноса почтовых файлов на сервер. MTA
Может быть, позже я расследую метод почтовых файлов, см. также ссылки ниже.
Отправка почты из нашего контейнера на адрес хоста, на котором она прослушивается
Для этого мы должны использовать Docker мост под названием docker0. Из Docker документов:
По умолчанию Docker сервер создает и настраивает сетевой интерфейс docker0 хост-системы под названием docker0, который представляет собой устройство моста Ethernet. Если при запуске контейнера не указать другую сеть, контейнер подключается к мосту, и весь поток, идущий от него и идущий через мост к контейнеру, направляется к контейнеру, Docker daemonкоторый обрабатывает маршрутизацию от имени контейнера.
Docker конфигурирует docker0 с IP-адресом netmaskи диапазоном назначения IP-адресов. Контейнерам, подключенным к мосту по умолчанию, присваиваются IP-адреса в этом диапазоне".
Это означает, что при использовании контейнера для проверки возможности подключения к тестовому письму MTA и отправки тестовых сообщений мы не можем этого сделать:
telnet localhost 25
telnet 127.0.0.1 25
но вместо этого должен использовать IP-адрес моста доккера:
telnet <docker0 IP address> 25
IP-адрес докера0 можно узнать, например, запустив его:
ifconfig docker0
Тестирование на локальной машине (Ubuntu 18.04 desktop)
Я решил сначала протестировать это на своей локальной машине, а не валять дурака на производстве. Если предположить, что вы Docker установили программу и нет, для выполнения этих тестов вам понадобятся следующие действия:
- Проверьте порты прослушивания и приложения на предмет . Запустите одну из них:
sudo lsof -i -P -n | grep LISTEN | grep 25 sudo netstat -tulpn | grep LISTEN | grep 25
- Рабочий контейнер. Alpine Подходит для нашей цели. Введите его, запустив:
docker run -it alpine /bin/sh
- Telnet в Docker контейнере. Находясь в контейнере, выполните установку, выполнив пробежку:
apk add busybox-extras
- socket server Прослушивание port 25. Я использовал следующее:
# description: python 3 socket server printing and echoing received data import socket import sys # host: all available interfaces host = '' # port: set to port 25 for our test port = 25 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print('socket created') try: s.bind((host, port)) except socket.error as msg: print('bind failed, msg = {}'.format(msg)) sys.exit() print('bind done') s.listen(1) print('start listening') conn, addr = s.accept() print('Connection from ', addr) while True: data = conn.recv(1024) print('data = {}'.format(data)) if not data: break conn.sendall(data) conn.close()
На моей локальной машине IP-адрес моста докер0 будет следующим: 172.17.0.1.
Во-первых, мы проверяем, port 25 есть ли такая возможность (она должна быть):
sudo lsof -i -P -n | grep LISTEN | grep 25
Откройте другое окно терминала, запустите эхо-сигнал :
sudo python3 listen_port.py
Это печатает какой-то текст:
socket created
bind done
start listening
Теперь должны использоваться (нашими ):
python3 5857 root 3u IPv4 2925123970 0t0 TCP *:25 (LISTEN)
Откройте другое окно терминала, запустите и введите Docker контейнер:
docker run -it alpine /bin/sh
Запустите 'apk add busybox-extras' для установки .
В Docker контейнерном типе:
telnet 127.0.0.1 25
Вы получите сообщение:
telnet: can't connect to remote host (127.0.0.1): Connection refused
Теперь используйте вместо этого IP-адрес docker0:
telnet 172.17.0.1 25
В окнах терминала socket server вы должны увидеть сообщение:
Connection from ('172.17.0.2', 38928)
В Docker контейнере введите несколько слов, нажмите Enter и т.д. Они должны быть отражены в Docker контейнере и вы должны увидеть их в окне socket server терминала.
Имейте в виду, что могут возникнуть проблемы с последовательностью операций. порядком запуска Docker контейнера socket server и его содержимого. Также при прерывании работы socket server и повторном запуске может появиться ошибка:
OSError: [Errno 98] Address already in use
В этом случае подождите некоторое время, прежде чем начинать заново.
Пока все в порядке. Мы можем общаться через докер0 мост. Давайте перейдем к производственному серверу.
Внедрение на производстве ("Debian Растягивание + ISPConfig3")
На моем производственном сервере IP-адрес моста docker0 равен: 172.17.0.1
Для ISPConfigтроих MTA это... Конфигурация находится в файле:
/etc/postfix/main.cf
В этом файле есть две строки, которые могут потребовать изменений:
inet_interfaces = all
...
mynetworks = 127.0.0.0/8 [::1]/128
Строка с символом ''inet_interfacesвыглядит хорошо, Postfix уже слушает все интерфейсы, нет необходимости менять эту строку. Строка с " 'mynetworksдолжна быть изменена, она должна включать в себя:
- IP-адрес моста докер0 и..,
- IP-адреса Docker изображений (которые будут отправлять почту)
У нас уже есть IP-адрес моста docker0, 172.17.0.1. Чтобы найти IP-адреса наших контейнеров, мы можем сначала проверить их:
docker ps
чтобы получить идентификатор контейнера, а затем использовать инспекцию, например
docker inspect c2c44e9bea28
Это даст много информации и где-то в конце концов, что-то вроде:
"IPAddress": "172.20.0.2"
Чтобы включить все Docker адреса, я использую подсеть 172.16.0.0/12, это дает диапазон IP-адресов: 172.16.0.1 - 172.31.255.254. Тогда mynetworks линия становится:
mynetworks = 127.0.0.0/8 [::1]/128 172.16.0.0/12
После изменения этой строки в файле /etc/postfix/main.cf и сохранения файла мы должны перезапустить :
service postfix restart
Чтобы проверить, работают ли наши изменения, мы снова используем . По типу хоста:
telnet 127.0.0.1 25
Он напечатает что-то вроде:
220 server.example.com ESMTP Postfix (Debian/GNU)
Запомни эту строчку! Введите quit, чтобы остановиться. Теперь мы начинаем маленький Docker контейнер, как на локальной машине:
docker run -it alpine /bin/sh
и снова добавить, запустив 'apk add busybox-extras'. Конечно:
telnet 127.0.0.1 25
потерпит неудачу:
telnet: can't connect to remote host (127.0.0.1): Connection refused
Но
telnet 172.17.0.1 25
теперь должен дать вам сообщение того же самого сообщения MTA, что и на хосте:
220 server.example.com ESMTP Postfix (Debian/GNU)
В качестве заключительного теста, мы можем отправить письмо себе из контейнера, используя . Получить sendmail варианты:
sendmail --help
Где-то здесь есть черта:
-S HOST[:PORT] Server (default $SMTPHOST or 127.0.0.1)
Это означает, что мы можем изменить адрес по умолчанию на адрес докер0. Чтобы отправить почту, напечатайте:
sendmail -S 172.17.0.1 yourname@yourdomain.yourextension
Начинай печатать:
To: someone@example.com
Subject: My docker test mail
Hello this is a mail from my docker container.
Thank you.
Введите Ctrl-D, чтобы отправить письмо. Вы можете просмотреть /var/log/mail.log для вашего почтового сообщения. Да, и, к сожалению, возникла ошибка, сообщение в mail.log:
You cannot send mail from 4449d8888ddd since that domain cannot receive mail
Далее проверка показала, что домен from не является полностью квалифицированным доменом. Вы можете проверить это, выполнив команду Verbose (-v). Оно показало:
sendmail: send:'MAIL FROM:<root@1f12e2cef814>'
Решение заключается в добавлении опции MAIL FROM SENDER (-f) в командную строку:
sendmail -v -S 172.17.0.1 -f info@example.com yourname@yourdomain.yourextension
Начни снова печатать:
To: yourname@yourdomain.yourextension
From: someone@example.com
Subject: My docker test mail
Hello this is a mail from my docker container.
Thank you.
Введите Ctrl-D, чтобы отправить почту. Теперь почта отправлена и должна появиться в вашем почтовом ящике (или спаме). Миссия выполнена!
Использование локального сервера для отладки
Существует Python один лайнер, который действует как SMPT сервер и может быть полезен для отладки, флаг -d добавляет отладочную информацию:
sudo python -m smtpd -d -n -c DebuggingServer 172.17.0.1:1025
Заключительные замечания
Возникает проблема с описанным методом, и это, в том числе, ссылки:
Postfix должен быть запущен после того, как будет поднят интерфейс docker0".
Потому что Docker сетевой мост может быть еще не готов к постфиксу загрузки системы, но может не запуститься, потому что он не может быть привязан к этому адресу.
Я еще не искал решения, но это страшно. Если сервер перезагружен, я не должен забывать проверить, можно ли отправлять почту. По этой причине метод 'mail files' может быть лучшим решением, так как нет зависимости от docker0 bridge.
Ссылки / кредиты
Configure sendmail inside a docker container
https://stackoverflow.com/questions/26215021/configure-sendmail-inside-a-docker-container
Send an email from a Docker container through an external MTA with ssmtp
https://www.michelebologna.net/2019/send-an-email-from-a-docker-container/
Sending email from docker through Postfix installed on the host
http://satishgandham.com/2016/12/sending-email-from-docker-through-postfix-installed-on-the-host/
Sending email inside a docker container to hosts smtp running postfix
https://serverfault.com/questions/817353/sending-email-inside-a-docker-container-to-hosts-smtp-running-postfix
Using system postfix as mail relay for docker containers
https://markusbenning.de/blog/2017/08/16/using-system-postfix-as-mail-relay-for-docker-containers.html
Недавний
- Скрытие первичных ключей базы данных UUID вашего веб-приложения
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- Использование PyInstaller и Cython для создания исполняемого файла Python
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу