Build a mail server with Postfix and run your own program on the server side every time a mail is received. The self-made program uses the received mail as input data and performs some processing. This can be implemented in any language, but in this article we'll use Python to implement posting to Slack. This time, the mail server is built so that it works at least in the local environment, and mails are sent and received only in the local environment. We do not make detailed settings such as security.
Here are the steps for both CentOS 8 and Ubuntu 20.04. Install BIND9, Postfix, and Dovecot from Linux packages. The environment of the server that builds the mail server is as follows.
--Domain name: localdomain --Hostname: localhost --General Linux username and password: usrname, secret
Python version of CentOS
$ python3 -V
Python 3.6.8
Python version of Ubuntu
$ python3 -V
Python 3.8.2
CentOS ifconfig result
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.79.128 netmask 255.255.255.0 broadcast 192.168.79.255
inet6 fe80::59ac:7015:10c9:543c prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:93:f6:a7 txqueuelen 1000 (Ethernet)
RX packets 914 bytes 156651 (152.9 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 284 bytes 25365 (24.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
virbr0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255
ether 52:54:00:be:03:9b txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Ubuntu ifconfig result
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:cd:65:85:ad txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.79.130 netmask 255.255.255.0 broadcast 192.168.79.255
inet6 fe80::f8e9:90fd:cfc4:280e prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:cc:76:18 txqueuelen 1000 (Ethernet)
RX packets 159457 bytes 233626546 (233.6 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 10206 bytes 690515 (690.5 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local loop back)
RX packets 514 bytes 48478 (48.4 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 514 bytes 48478 (48.4 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Build a Postfix mail server while looking at the following two books. Since these books contain construction procedures and explanations, this article only shows the procedures required to build a local environment. Both books use CentOS 5.4, but this article rewrites the commands for CentOS 8 and Ubuntu 20.04.
--Title: DNS server construction by BIND9 (revised new edition) --Author: Hiromichi Ito / work Tatsuto Kawahara / work Shin Nozu / work --Publisher: Technical Review Company --Publishing date: 2010-07
--Book title: Introduction to Postfix practice --Author: Masato Shimizu / work --Publisher: Technical Review Company --Publishing date: 2010-10
Install BIND9 as the root user. CentOS uses chroot according to the book. Ubuntu doesn't use chroot this time.
CentOS
yum install bind bind-chroot
cd /var/named/chroot/etc
mv /etc/named.conf .
ln -s /var/named/chroot/etc/named.conf /etc/
Ubuntu
apt install bind9 bind9utils
As the root user, edit /etc/resolv.conf.
editor /etc/resolv.conf
CentOS
/etc/resolv.conf
# Generated by NetworkManager
search localdomain
-nameserver 192.168.79.2
+nameserver 127.0.0.1
Ubuntu
/etc/resolv.conf
-nameserver 127.0.0.53
+nameserver 127.0.0.1
options edns0
search localdomain
As the root user, edit named.conf.
CentOS
editor /etc/named.conf
/etc/named.conf
(Omitted)
options {
- listen-on port 53 { 127.0.0.1; };
+ //listen-on port 53 { 127.0.0.1; };
- listen-on-v6 port 53 { ::1; };
+ //listen-on-v6 port 53 { ::1; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
secroots-file "/var/named/data/named.secroots";
recursing-file "/var/named/data/named.recursing";
- allow-query { localhost; };
+ //allow-query { localhost; };
(Omitted)
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
+
+zone "localdomain" IN {
+ type master;
+ file "localdomain.zone";
+ allow-update { none; };
+};
Ubuntu
editor /etc/bind/named.conf.local
/etc/bind/named.conf.local
//
// Do any local configuration here
//
// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";
+zone "localdomain" IN {
+ type master;
+ file "localdomain.zone";
+ allow-update { none; };
+};
As the root user, check the syntax of named.conf.
CentOS
/usr/sbin/named-checkconf /etc/named.conf
Ubuntu
/usr/sbin/named-checkconf /etc/bind/named.conf
As the root user, create a new localdomain.zone.
CentOS
editor /var/named/chroot/var/named/localdomain.zone
/var/named/chroot/var/named/localdomain.zone
$TTL 10800
localdomain. 1D IN SOA ns.localdomain. root.localdomain. (
2020052601 ; serial
43200 ; refresh
5400 ; retry
3600000 ; expiry
3600 ) ; minimum
localdomain. 1D IN NS ns.localdomain.
localdomain. 1D IN MX 10 mail01.localdomain.
ns.localdomain. 1D IN A 192.168.79.128
mail01.localdomain. 1D IN A 192.168.79.128
Ubuntu
editor /var/cache/bind/localdomain.zone
/var/cache/bind/localdomain.zone
$TTL 10800
localdomain. 1D IN SOA ns.localdomain. root.localdomain. (
2020052601 ; serial
43200 ; refresh
5400 ; retry
3600000 ; expiry
3600 ) ; minimum
localdomain. 1D IN NS ns.localdomain.
localdomain. 1D IN MX 10 mail01.localdomain.
ns.localdomain. 1D IN A 192.168.79.130
mail01.localdomain. 1D IN A 192.168.79.130
As the root user, set the permissions of the zone file.
CentOS
chmod 640 /var/named/chroot/var/named/localdomain.zone
chown root:named /var/named/chroot/var/named/localdomain.zone
ln -s /var/named/chroot/var/named/localdomain.zone /var/named/
Ubuntu
chmod 640 /var/cache/bind/localdomain.zone
chown root:bind /var/cache/bind/localdomain.zone
As the root user, check the syntax of the zone file.
CentOS
/usr/sbin/named-checkzone localdomain /var/named/chroot/var/named/localdomain.zone
Ubuntu
/usr/sbin/named-checkzone localdomain /var/cache/bind/localdomain.zone
Start BIND as the root user.
Common to CentOS and Ubuntu
#When you want to check the current status
systemctl status named.service
#When you want to start after stopping
systemctl stop named.service
systemctl start named.service
#When you want to start automatically when the OS boots
systemctl enable named.service
Test the zone transfer as the root user.
CentOS
dig @192.168.79.128 localdomain AXFR
CentOS results
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> @192.168.79.128 localdomain AXFR
; (1 server found)
;; global options: +cmd
localdomain. 86400 IN SOA ns.localdomain. root.localdomain. 2020052601 43200 5400 3600000 3600
localdomain. 86400 IN NS ns.localdomain.
localdomain. 86400 IN MX 10 mail01.localdomain.
mail01.localdomain. 86400 IN A 192.168.79.128
ns.localdomain. 86400 IN A 192.168.79.128
localdomain. 86400 IN SOA ns.localdomain. root.localdomain. 2020052601 43200 5400 3600000 3600
;; Query time: 1 msec
;; SERVER: 192.168.79.128#53(192.168.79.128)
;; WHEN:Thu June 04 15:35:22 JST 2020
;; XFR size: 6 records (messages 1, bytes 217)
Ubuntu
dig @192.168.79.130 localdomain AXFR
Ubuntu results
; <<>> DiG 9.16.1-Ubuntu <<>> @192.168.79.130 localdomain AXFR
; (1 server found)
;; global options: +cmd
localdomain. 86400 IN SOA ns.localdomain. root.localdomain. 2020052601 43200 5400 3600000 3600
localdomain. 86400 IN NS ns.localdomain.
localdomain. 86400 IN MX 10 mail01.localdomain.
mail01.localdomain. 86400 IN A 192.168.79.130
ns.localdomain. 86400 IN A 192.168.79.130
localdomain. 86400 IN SOA ns.localdomain. root.localdomain. 2020052601 43200 5400 3600000 3600
;; Query time: 3 msec
;; SERVER: 192.168.79.130#53(192.168.79.130)
;; WHEN:Thu June 04 15:36:11 JST 2020
;; XFR size: 6 records (messages 1, bytes 217)
As the root user, stop if sendmail is running.
Common to CentOS and Ubuntu
ps ax | grep sendmail
Sendmail wasn't working in the environment in this article, so I didn't do anything.
Install Postfix as the root user.
CentOS
yum install postfix
Ubuntu
apt install postfix
As the root user, select Postfix with the alternatives command.
CentOS
alternatives --config mta
In the environment of this article, Postfix was the only option, so I didn't do anything.
As the root user, edit main.cf.
Common to CentOS and Ubuntu
cd /etc/postfix
cp main.cf main.cf.org
editor main.cf
CentOS
/etc/postfix/main.cf
(Omitted)
#myhostname = host.domain.tld
#myhostname = virtual.domain.tld
+myhostname = mail01.localdomain
(Omitted)
#mydomain = domain.tld
+mydomain = localdomain
(Omitted)
-#myorigin = $mydomain
+myorigin = $mydomain
(Omitted)
-mydestination = $myhostname, localhost.$mydomain, localhost
-#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
+#mydestination = $myhostname, localhost.$mydomain, localhost
+mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
(Add the following to the end of the file)
+# Allowed to run :include: method in /etc/aliases
+alias_maps = hash:/etc/aliases
+alias_database = hash:/etc/aliases
+allow_mail_to_commands = alias,forward,include
+allow_mail_to_files = alias,forward,include
Ubuntu
/etc/postfix/main.cf
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
# Debian specific: Specifying a file name will cause the first
# line of that file to be used as the name. The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname
smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no
# appending .domain is the MUA's job.
append_dot_mydomain = no
# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h
readme_directory = no
# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on
# fresh installs.
compatibility_level = 2
# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_tls_security_level=may
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_security_level=may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
-#myhostname = sample.localdomain
+myhostname = mail01.localdomain
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
-mydestination = mail01.localhost.localdomain, $myhostname, sample, localhost.localdomain, localhost
+mydestination = mail01.localhost.localdomain, $myhostname, mail01, localhost.localdomain, localhost localdomain
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = loopback-only
default_transport = error
relay_transport = error
inet_protocols = all
+allow_mail_to_commands = alias,forward,include
+allow_mail_to_files = alias,forward,include
Start Postfix as the root user.
Common to CentOS and Ubuntu
#When you want to check the settings
postfix check
#When you want to check the current status
postfix status
#When you want to start after stopping
postfix stop
postfix start
#When you want to reload
postfix reload
As a general user, connect to SMTP with telnet and check the operation of Postfix.
Common to CentOS and Ubuntu
telnet localhost 25
EHLO localdomain
MAIL FROM:<usrname@localdomain>
RCPT TO:<usrname@localdomain>
DATA
This is test1
This is test2
This is test3
.
NOOP
QUIT
If you get the following error on the way,
RCPT TO:<usrname@localdomain>
451 4.3.0 <usrname@localdomain>: Temporary lookup failure
As the root user, do the following (book "Introduction to Postfix Practice" p.142), go back to the general user and re-execute from telnet.
postalias /etc/aliases
postfix reload
The screen image when executed is as follows.
CentOS
[usrname@localhost ~]$ telnet localhost 25
Trying ::1...
Connected to localhost.
Escape character is '^]'.
220 mail01.localdomain ESMTP Postfix
EHLO localdomain
250-mail01.localdomain
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 SMTPUTF8
MAIL FROM:<usrname@localdomain>
250 2.1.0 Ok
RCPT TO:<usrname@localdomain>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
This is test1
This is test2
This is test3
.
250 2.0.0 Ok: queued as 39B219AF86
NOOP
250 2.0.0 Ok
QUIT
221 2.0.0 Bye
Connection closed by foreign host.
Ubuntu
usrname@localhost:~$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mail01.localdomain ESMTP Postfix (Ubuntu)
EHLO localdomain
250-mail01.localdomain
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250 CHUNKING
MAIL FROM:<usrname@localdomain>
250 2.1.0 Ok
RCPT TO:<usrname@localdomain>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
This is test1
This is test2
This is test3
.
250 2.0.0 Ok: queued as 49F8B2F816F8
NOOP
250 2.0.0 Ok
QUIT
221 2.0.0 Bye
Connection closed by foreign host.
As the root user, set the alias file. In addition to the procedure of the book, specify the command to be activated when receiving an email here. In this article, we will start "/ home / usrname / email_hook / hook" every time we receive an email. You will create a file named this hook later.
Common to CentOS and Ubuntu
editor /etc/aliases
CentOS
(End of file)
# Person who should get root's mail
#root: marc
+root: usrname
+
+# hook
+usrname: usrname, :include:/home/usrname/email_hook/hook
Ubuntu
# See man 5 aliases for format
postmaster: root
# Person who should get root's mail
root: usrname
+
+# hook
+usrname: usrname, :include:/home/usrname/email_hook/hook
As the root user, update aliases.db and reload Postfix.
Common to CentOS and Ubuntu
postalias /etc/aliases
postfix reload
Install Dovecot as the root user.
CentOS
yum install dovecot
Ubuntu
apt install dovecot-core dovecot-imapd dovecot-pop3d
As the root user, edit dovecot-openssl.cnf for your situation.
CentOS
editor /etc/pki/dovecot/dovecot-openssl.cnf
CentOS is as follows in my case.
/etc/pki/dovecot/dovecot-openssl.cnf
[ req ]
default_bits = 3072
encrypt_key = yes
distinguished_name = req_dn
x509_extensions = cert_type
prompt = no
[ req_dn ]
# country (2 letter code)
#C=FI
+C=JP
# State or Province Name (full name)
#ST=
+ST=TOKYO
# Locality Name (eg. city)
#L=Helsinki
+L=Chofu
# Organization (eg. company)
#O=Dovecot
+O=Kanedaq Office
# Organizational Unit Name (eg. section)
OU=IMAP server
# Common Name (*.example.com is also possible)
-CN=imap.example.com
+CN=mail01.localdomain
# E-mail contact
[email protected]
+emailAddress=postmaster@localdomain
[ cert_type ]
nsCertType = server
Ubuntu
editor /usr/share/dovecot/dovecot-openssl.cnf
Ubuntu didn't change the content this time.
/usr/share/dovecot/dovecot-openssl.cnf
#
# SSLeay configuration file for Dovecot.
#
RANDFILE = /dev/urandom
[ req ]
default_bits = 2048
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
prompt = no
policy = policy_anything
req_extensions = v3_req
x509_extensions = v3_req
[ req_distinguished_name ]
organizationName = Dovecot mail server
organizationalUnitName = @commonName@
commonName = @commonName@
emailAddress = @emailAddress@
[ v3_req ]
basicConstraints = CA:FALSE
Recreate the certificate as root user.
CentOS
sh /usr/share/doc/dovecot/mkcert.sh
Ubuntu has the following command to run, but I didn't run it this time.
sh /usr/share/dovecot/mkcert.sh
As the root user, edit dovecot.conf.
Common to CentOS and Ubuntu
editor /etc/dovecot/dovecot.conf
CentOS
/etc/dovecot/dovecot.conf
(Omitted)
# Protocols we want to be serving.
#protocols = imap pop3 lmtp
+protocols = pop3
(Add the following to the end of the file)
+log_path = /var/log/dovecot.log
+disable_plaintext_auth = no #Allow plaintext passwords for the time being (insecure)
Ubuntu
/etc/dovecot/dovecot.conf
(Omitted)
# Enable installed protocols
-!include_try /usr/share/dovecot/protocols.d/*.protocol
+!include_try /usr/share/dovecot/protocols.d/pop3d.protocol
(Add the following to the end of the file)
+log_path = /var/log/dovecot.log
+disable_plaintext_auth = no #Allow plaintext passwords for the time being (insecure)
As the root user, edit the mailbox location.
Common to CentOS and Ubuntu
editor /etc/dovecot/conf.d/10-mail.conf
CentOS
/etc/dovecot/conf.d/10-mail.conf
(Omitted)
#mail_location =
+mail_location = mbox:~/mail:INBOX=/var/mail/%u
(Omitted)
Ubuntu was already set up as intended, as shown below, so I didn't change it.
/etc/dovecot/conf.d/10-mail.conf
(Omitted)
mail_location = mbox:~/mail:INBOX=/var/mail/%u
(Omitted)
Start Dovecot as the root user.
Common to CentOS and Ubuntu
#When you want to check the current status
systemctl status dovecot.service
#When you want to start after stopping
systemctl stop dovecot.service
systemctl start dovecot.service
#When you want to start automatically when the OS boots
systemctl enable dovecot.service
As the root user, check your Dovecot launch settings.
Common to CentOS and Ubuntu
systemctl list-unit-files -t service | grep dovecot
As the root user, check for open ports.
Common to CentOS and Ubuntu
netstat -ln | grep tcp
CentOS results
tcp 0 0 0.0.0.0:5355 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:110 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN
tcp 0 0 192.168.79.128:53 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN
tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:953 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:995 0.0.0.0:* LISTEN
tcp6 0 0 :::5355 :::* LISTEN
tcp6 0 0 :::110 :::* LISTEN
tcp6 0 0 :::111 :::* LISTEN
tcp6 0 0 :::53 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:631 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
tcp6 0 0 ::1:953 :::* LISTEN
tcp6 0 0 :::995 :::* LISTEN
Ubuntu results
tcp 0 0 172.17.0.1:53 0.0.0.0:* LISTEN
tcp 0 0 192.168.79.130:53 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:953 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:995 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:110 0.0.0.0:* LISTEN
tcp6 0 0 fe80::f8e9:90fd:cfc4:53 :::* LISTEN
tcp6 0 0 ::1:53 :::* LISTEN
tcp6 0 0 ::1:631 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
tcp6 0 0 ::1:953 :::* LISTEN
tcp6 0 0 :::995 :::* LISTEN
tcp6 0 0 :::110 :::* LISTEN
As a general user, check the operation of the POP server with telnet. Before that, CentOS required the following as the root user:
CentOS
chmod 0600 /var/mail/*
chmod 0600 /var/spool/mail/*
Now start telnet.
Common to CentOS and Ubuntu
telnet localhost 110
USER usrname
PASS secret
LIST
RETR 1
QUIT
The screen image when executed is as follows.
CentOS
[usrname@localhost ~]$ telnet localhost 110
Trying ::1...
Connected to localhost.
Escape character is '^]'.
+OK Dovecot ready.
USER usrname
+OK
PASS secret
+OK Logged in.
LIST
+OK 1 messages:
1 463
.
RETR 1
+OK 463 octets
Return-Path: <usrname@localdomain>
X-Original-To: usrname@localdomain
Delivered-To: usrname@localdomain
Received: from localdomain (localhost [IPv6:::1])
by mail01.localdomain (Postfix) with ESMTP id 39B219AF86
for <usrname@localdomain>; Thu, 4 Jun 2020 17:52:38 +0900 (JST)
Message-Id: <[email protected]>
Date: Thu, 4 Jun 2020 17:52:38 +0900 (JST)
From: usrname@localdomain
This is test1
This is test2
This is test3
.
QUIT
+OK Logging out.
Connection closed by foreign host.
Ubuntu
usrname@localhost:~$ telnet localhost 110
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
+OK Dovecot (Ubuntu) ready.
USER usrname
+OK
PASS secret
+OK Logged in.
LIST
+OK 1 messages:
1 468
.
RETR 1
+OK 468 octets
Return-Path: <usrname@localdomain>
X-Original-To: usrname@localdomain
Delivered-To: usrname@localdomain
Received: from localdomain (localhost [127.0.0.1])
by mail01.localdomain (Postfix) with ESMTP id 49F8B2F816F8
for <usrname@localdomain>; Thu, 4 Jun 2020 17:08:33 +0900 (JST)
Message-Id: <[email protected]>
Date: Thu, 4 Jun 2020 17:08:33 +0900 (JST)
From: usrname@localdomain
This is test1
This is test2
This is test3
.
QUIT
+OK Logging out.
Connection closed by foreign host.
This completes the construction of the Postfix mail server.
Subsequent work will be done by general users. There is no difference in working between CentOS and Ubuntu.
I will create a subdirectory (email_hook) under my home directory (/ home / usrname) and put all my files here.
cd
mkdir email_hook
cd email_hook
Subsequent work is done under / home / usrname / email_hook.
Create a new file with the name hook (whatever the name is) and write the command there.
editor hook
The content of the command passes the mail received by Postfix through a pipeline to Python code (which we will implement later) called hook_slack.py.
"|LC_CTYPE='C.UTF-8' /usr/bin/python3 /home/usrname/email_hook/hook_slack.py || true"
Create a new file with the file name hook_slack.py (any name you like).
editor hook_slack.py
The contents are as stated below. Forgive me that the code isn't very well-behaved. Make sure you set the appropriate URL for Slack Incoming Webhooks.
/home/usrname/email_hook/hook_slack.py
import logging
import os
import sys
import time
import datetime
import json
import email.parser
import urllib.request
import urllib.parse
from pathlib import Path
def make_logfile_path(file):
directory = Path("/home/usrname/email_hook/log")
#Create if no directory
directory.mkdir(parents=True, exist_ok=True)
#Returns the full path log file name
return directory / os.path.basename(os.path.splitext(file)[0] + datetime.datetime.today().strftime("_%Y%m%d_%H%M%S.log"))
def get_logger(name, filepath):
LOG_LEVEL_FILE = logging.DEBUG
LOG_LEVEL_CONSOLE = logging.INFO
_detail_formatting = "\n%(asctime)s %(levelname)-8s [%(module)s#%(funcName)s %(lineno)d]\n%(message)s"
logging.basicConfig(
level=LOG_LEVEL_FILE,
format=_detail_formatting,
filename=filepath
)
#Create a handler console that sends logs to the console
console = logging.StreamHandler()
console.setLevel(LOG_LEVEL_CONSOLE)
console_formatter = logging.Formatter(_detail_formatting)
console.setFormatter(console_formatter)
#Get the logger and add a console handler
logger = logging.getLogger(name)
logger.addHandler(console)
return console, logger
#Logger
console, logger = get_logger(__name__, make_logfile_path(__file__))
def main():
start = time.time()
logger.info(f"hook started : {time.strftime('%d %b %X', time.localtime(start))}")
#Receive mail received by Postfix from standard input
mime_str = sys.stdin.read()
logger.debug(f"mime_str={mime_str}")
message = email.parser.Parser().parsestr(mime_str)
logger.debug(f"message={message}")
##URL for Slack Incoming Webhooks
url = "secret"
payload = {}
logger.debug(f'message.get("Subject")={message.get("Subject")}')
logger.debug(f"message.get_payload()={message.get_payload()}")
payload["text"] = message.get("Subject") + "\n" + message.get_payload(0).get_payload()
logger.debug(f"payload={payload}")
data = json.dumps(payload).encode("utf-8")
logger.debug(f"data={data}")
request = urllib.request.Request(url, data)
urllib.request.urlopen(request)
stop = time.time()
delta = stop - start
logger.info(f"hook started : {time.strftime('%d %b %X', time.localtime(start))}")
logger.info(f"hook finished : {time.strftime('%d %b %X', time.localtime(stop))}")
logger.info("hook duration : {:0.3} seconds".format(delta))
if __name__ == "__main__":
try:
main()
except Exception as ee:
logger.exception(ee)
We have prepared the data for Slack post test of hook_slack.py.py.
editor hook_test_stdin.txt
/home/usrname/email_hook/hook_test_stdin.txt
From usrname@localdomain Wed Jun 3 21:41:32 2020
Return-Path: <usrname@localdomain>
X-Original-To: usrname@localdomain
Delivered-To: usrname@localdomain
Received: from localhost.localdomain (localhost [127.0.0.1])
by mail01.localdomain (Postfix) with ESMTPS id 9630E40D18C9
for <usrname@localdomain>; Wed, 3 Jun 2020 21:41:32 +0900 (JST)
Content-Type: multipart/mixed; boundary="===============8887878637416477407=="
MIME-Version: 1.0
Subject:Slack post test subject
From: usrname@localdomain
To: usrname@localdomain
Date: Wed, 03 Jun 2020 12:41:32 -0000
Message-Id: <[email protected]>
--===============8887878637416477407==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Slack post test body
--===============8887878637416477407==--
Let's run hook_slack.py and test if it is posted to Slack.
cat hook_test_stdin.txt | LC_CTYPE='C.UTF-8' /usr/bin/python3 ./hook_slack.py || true
A log file was output under the log subdirectory and posted to Slack as follows:
Create a new file with the file name send_testmail.py (any name you like).
editor send_testmail.py
The contents are as stated below.
/home/usrname/email_hook/send_testmail.py
import smtplib
from email.mime.text import MIMEText
from email.utils import formatdate
from email.mime.multipart import MIMEMultipart
def create_message(from_addr, to_addr, subject, body):
#header
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = from_addr
msg['To'] = to_addr
msg['Date'] = formatdate()
#Text
msg.attach(MIMEText(body))
return msg
def send_mail(from_addr, to_addr, body_msg):
smtpobj = smtplib.SMTP("localhost", 25)
smtpobj.ehlo()
smtpobj.starttls()
smtpobj.ehlo()
smtpobj.sendmail(from_addr, to_addr, body_msg.as_string())
smtpobj.close()
MAIL_ADDRESS = "usrname@localdomain"
from_addr = MAIL_ADDRESS
to_addr = MAIL_ADDRESS
subject = "test mail"
body = "We'll send you a test email."
msg = create_message(from_addr, to_addr, subject, body)
send_mail(from_addr, to_addr, msg)
Run this program to send an email and test if the hook launches.
python3 ./send_testmail.py
It was posted on Slack as follows.
--Push mail using IMAP IDLE --Set up a mail server on your VPS and set up security etc. --It seems that many of MetaTrader's indicators and EA have an email sending function, so I would like to have the EA send an email to the local server and do some processing with the hook program.
that's all.
Recommended Posts