Giả sử chúng ta có website /ứng dụng web cần gởi email cho user (thí dụ như thông báo mật khẩu). Trong hầu hết trường hợp, user không cần trả lời cho email họ nhận được. Trong trường hợp này, chúng ta có thể cài đặt một SMTP server chỉ gởi mail trên web server bằng cách dùng Postfix, mặc dù cũng có những MTA khác như msmtp, smtp…
Ưu điểm của việc dùng Postfix là email người gởi có dạng user@mydomain.com. Ngoài ra vì Postfix không kiểm tra email người gởi có thuộc domain của mail server hay không, nên có thể gởi email nhân danh bất kỳ ai, thí dụ no-reply@apple.com. Tuy nhiên email mạo danh thường nằm trong thư mục spam của người nhận.
Chúng ta cũng sẽ dùng SMTP relayhost như GMail để đơn giản quá trình cài đặt, bởi vì yêu cầu căn bản nhất là IP của mail server phải không nằm trong danh sách đen của bất cứ tổ chức chống spam nào, điều mà các IP động không có được.
Chuẩn bị
- RPi đã cài đặt OS Debian/Ubuntu
- Internet domain hay subdomain hay tên miền động. Email sẽ có dạng user@domain.com
- User/Password của một tài khoản email thuộc một mail server nào đó làm SMTP relayhost
- DNS quản lý domain phải cho tạo ít nhất 4 record
- Mở port 25 cho RPi (option)
Nếu không có domain, có thể đăng ký free domain ở freenom.com. Tên miền .tk, .ml, .ga, .cf và .gq thuộc tên miền cấp 1 nhưng ít phổ biến.
Vì freenom.com chậm và ít tính năng nên có thể cho openDNS, cloudns.net quản lý tên miền. Cloudflare không nhận quản lý tên miền loại này.
Script cài đặt
Chọn Internet Site khi được hỏi, và điền System mail name, là tên miền đứng sau dấu @. Tất nhiên đây phải là internet domain, có thể dùng subdomain hay tên miền động.
Thay thế các dòng tô đậm trong script bằng các giá trị thực tế, hoặc có thể truyền domain qua tham số dòng lệnh
#!/bin/bash
# pfx-sendonly.sh v.20210420, Ly Anh Tuan <lnt@ly-le.info>
echo "Cài đặt mailserver dùng Postfix chỉ dùng gởi email đi"
# Gởi mail thông qua SMTP bên ngoài
relayhost='[smtp.gmail.com]:587'
# Gởi mail nhờ vào email account user:password
relayhost_account='user@gmail.com:password'
# Thay đổi HOSTNAME, DOMAIN theo thực tế
# Mặc định myhostname = hostname của RPi
HOSTNAME="$(hostname)"
if [ $# -eq 1 ]; then
DOMAIN="$1"
else
# Mặc định mydomain trích từ myhostname
DOMAIN="${HOSTNAME#*.}"
fi
step=0
echo "✅ Bước $step: Chuẩn bị"
((step+=1))
echo '➤➤➤ Cập nhật hệ thống...'
sudo apt update
if [ -z $DOMAIN ]; then
read -p "➤➤➤ Nhập tên miền để gởi email (eg. example.com): " DOMAIN
echo
[ "$DOMAIN" ] || exit
fi
echo "✅ Bước $step: Cài đặt Postfix"
((step+=1))
sudo apt-get install libsasl2-modules postfix -y
echo "✅ Bước $step: Cấu hình Postfix"
((step+=1))
SUFFIX=$(date +'%Y%m%d')
if [ -e /etc/postfix/main.cf.$SUFFIX ]; then
cp -f /etc/postfix/main.cf.$SUFFIX /etc/postfix/main.cf
else
cp -f /etc/postfix/main.cf /etc/postfix/main.cf.$SUFFIX
fi
sudo postconf -e "myhostname = $HOSTNAME"
sudo postconf -e "mydomain = $DOMAIN"
sudo postconf -e 'mydestination = $myhostname, localhost.$mydomain, localhost'
echo "$DOMAIN" | sudo tee /etc/mailname
echo "✅ Bước $step: Cài đặt và cấu hình OpenDkim"
((step+=1))
read -n1 -t5 -p "➤➤➤ Cài đặt OpenDkim cho mailserver $HOSTNAME [C/k]? " ans
echo
ans=${ans:-C}
if [[ "${ans^}" = 'C' ]]; then
sudo apt install opendkim opendkim-tools -y
sudo adduser postfix opendkim
sudo sed -e 's|#\(\bCanonicalization\b\).*|\1 relaxed/simple|
s|#\(\bMode\b\).*|\1 s|
s|#\(\bSubDomains\b\).*|\1 no|' -i /etc/opendkim.conf
grep -Eqs "UserID\s+opendkim" /etc/opendkim.conf || sudo cat << EOT >> /etc/opendkim.conf
# OpenDOMAINKIM user
# Remember to add user postfix to group opendkim
UserID opendkim
# Map domains in From addresses to keys used to sign messages
KeyTable refile:/etc/opendkim/key.table
SigningTable refile:/etc/opendkim/signing.table
# A set of internal hosts whose mail should be signed
InternalHosts /etc/opendkim/trusted.hosts
EOT
[ -d /etc/opendkim ] || sudo mkdir /etc/opendkim
[ -d /etc/opendkim/keys ] || sudo mkdir /etc/opendkim/keys
selection='sendonly'
sudo cat <<EOT > /etc/opendkim/signing.table
*@$DOMAIN $selection._domainkey.$DOMAIN
EOT
sudo cat <<EOT > /etc/opendkim/key.table
$selection._domainkey.$DOMAIN $DOMAIN:$selection:/etc/opendkim/keys/$DOMAIN/sendonly.private
EOT
sudo cat > /etc/opendkim/trusted.hosts <<EOT
127.0.0.1
localhost
*.$DOMAIN
EOT
echo '➤➤➤ Tạo cặp khóa Private/Public...'
[ -d /etc/opendkim/keys/$DOMAIN ] || sudo mkdir /etc/opendkim/keys/$DOMAIN
sudo opendkim-genkey -b 2048 -d $DOMAIN -D /etc/opendkim/keys/$DOMAIN -s $selection -v
sudo chown opendkim:opendkim /etc/opendkim/keys/$DOMAIN/$selection.private
echo "✅ Bước $step: Kết nối Postfix và OpenDkim"
((step+=1))
sudo sed -e 's|\(Socket\s*local:\).*|\1/var/spool/postfix/opendkim/opendkim.sock|' -i /etc/opendkim.conf
[ -d /var/spool/postfix/opendkim ] || sudo mkdir /var/spool/postfix/opendkim
sudo chown opendkim:postfix /var/spool/postfix/opendkim
sudo sed -e 's|\(^SOCKET=\).*|\1"local:/var/spool/postfix/opendkim/opendkim.sock"|' -i /etc/default/opendkim
grep -Eqs 'local:opendkim/opendkim.sock' /etc/postfix/main.cf || sudo cat >> /etc/postfix/main.cf <<EOT
# Milter configuration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:opendkim/opendkim.sock
non_smtpd_milters = \$smtpd_milters
EOT
fi
echo "✅ Bước $step: Tạo A, SPF, PTR, DMARC, DKIM record"
((step+=1))
cIP=$(curl -s http://ifconfig.me/ip)
sudo tee ./DNS.txt <<EOT
Vào DNS manager của $DOMAIN để cấu hình A, SPF, PTR, DMARC, DKIM record
+ A $HOSTNAME $cIP
+ TXT $DOMAIN v=spfv1 mx ip4:$cIP ~all
+ TXT _drmarc v=DMARC1; p=none
+ TXT $selection._domainkey v=DKIM1; h=sha256; k=rsa; p=you-key-here-without-spaces
+ PTR $cIP $HOSTNAME
EOT
echo "✅ Bước $step: Bật TLS Encryption để gởi mail"
((step+=1))
read -n1 -t5 -p "➤➤➤ Cài đặt certbot để tạo chứng chỉ bảo mật (C/k)? " ans
echo
ans=${ans:-C}
if [[ "${ans^}" = 'C' ]]; then
sudo apt install certbot &> /dev/null
SVC=$(sudo ss -tulpn | sed -n '/:80 /{s/.*(("\([^"]*\)".*/\1/;p}')
systemctl stop $SVC
sudo certbot certonly --standalone -d $HOSTNAME
systemctl start $SVC
fi
[ -f "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" ] && CERT="$DOMAIN"
[ -f "/etc/letsencrypt/live/$HOSTNAME/fullchain.pem" ] && CERT="$HOSTNAME"
if [ ! -f "/etc/letsencrypt/live/$CERT/fullchain.pem" ]; then
echo "➤➤➤ ERROR: Không tìm thấy chứng chỉ bảo mật tại /etc/letsencrypt/live/$CERT"
echo
echo '➤➤➤ Tiếp tục cài đặt...'
fi
sudo postconf -e "smtpd_tls_cert_file = /etc/letsencrypt/live/$CERT/fullchain.pem"
sudo postconf -e "smtpd_tls_key_file = /etc/letsencrypt/live/$CERT/privkey.pem"
sudo postconf -e "smtpd_tls_security_level = may"
sudo postconf -e "smtp_tls_security_level = may"
sudo postconf -e 'smtp_tls_loglevel = 1'
echo "✅ Bước $step: Cài đặt SMTP relayhost"
((step+=1))
read -n1 -t5 -p "➤➤➤ Cài đặt SMTP relayhost cho mailserver [C/k]? " ans
echo
ans=${ans:-C}
if [[ "${ans^}" = 'C' ]]; then
echo "➤➤➤ Đang cài đặt..."
sudo postconf -e "relayhost = $relayhost"
sudo grep -Esq 'hash:/etc/postfix/sasl_passwd' /etc/postfix/main.cf ||
sudo cat >> /etc/postfix/main.cf <<EOT
# 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
header_size_limit = 4096000
EOT
sudo cat <<EOT > /etc/postfix/sasl_passwd
$relayhost $relayhost_account
EOT
sudo postmap /etc/postfix/sasl_passwd
sudo chmod 0600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
sudo postconf -e "inet_interfaces = loopback-only"
fi
echo '➤➤➤ Khởi động lại OpenDkim Postfix...'
sudo systemctl restart opendkim postfix
Chú thich
- Script chạy chỉ trong vòng 1 phút, tuy nhiên dừng nhiều lần 5s để hỏi user về việc cài software
- Phải cài đặt các DNS record như ghi chú trong file DNS.txt thì mail server mới có thể hoạt động
- Sau khi cài đặt có thể gởi mail nhờ vào SMTP relay host
- Nếu domain được quản lý bởi cloudflare.com, có thể cấu hình tự động A, SPF, PTR, DMARC, DKIM record
<?php
$sender = 'no-reply@mail.domain';
$recipient = 'you@gmail.com';
$subject = 'Subject here';
$message = 'Testing...';
$headers = 'From:' . $sender;
echo (mail($recipient, $subject, $message, $headers)) ? "Message accepted" : "Failed!"
?>