commit 9e89a950e862d05b8cc190f4a4642b393d7e4f72
parent f08dfd3973a23e9aad063be62c350e48baa49350
Author: Petar Yotsev <petar@yotsev.xyz>
Date: Tue, 14 Dec 2021 10:35:19 +0000
Add multi-domain support & utilities
Diffstat:
9 files changed, 305 insertions(+), 173 deletions(-)
diff --git a/README.md b/README.md
@@ -3,10 +3,8 @@
mmv - minimal mailserver virtual
mmv is a script for setting up a personal email server on OpenBSD using
-virtual users.
-
-As you might have guessed, there also exists a fork -
-[mms](https://git.yotsev.xyz/mms) - that uses system users.
+virtual users. It allows one to host email for multiple domains on the
+same server.
WARNING: The script is still in development and liable to drastic
changes with no backwards compatibility.
@@ -14,24 +12,36 @@ changes with no backwards compatibility.
## Prerequisites
The script automates as much as possible but there is one thing out of
-its reach - dns. You'll need a valid A and/or AAAA record for the domain
-you're going to use and for a subdomain `mail.domain.tld`.
+its reach - DNS. If your email is `user@example.com`, you'll need A/AAAA
+records for `mail.example.com`.
## Installation
git clone git://git.yotsev.xyz/mmv.git
cd mmv
- ./setup.sh domain.tld
+ ./mmv example.com
+
+If you want to add another domain, simply run the script again:
+
+ ./mmv another-domain.tld
## Post-execution
After the script has finished executing successfully, it will have
-written dns record that you have to paste into your name server or your
+written DNS record that you have to paste into your name server or your
registrar's interface.
## Usage
After everything is in place, you can use the newly created script
-`newuser` to add a user account.
+`madduser` to add a user account. You'll be prompted for a password.
+
+ madduser DOMAIN USERNAME
+
+You can similarly delete users with `mdeluser`.
+
+ mdeluser DOMAIN USERNAME
+
+And change a password with `mpasswd`.
- ./newuser
+ mpasswd DOMAIN USERNAME
diff --git a/files/daily.local b/files/daily.local
@@ -1,2 +1 @@
-# renew Let's Encrypt certificate if necessary
-acme-client -v <maildom>
+acme-client -v <maildom> && reboot
diff --git a/files/madduser b/files/madduser
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+[ $# != 2 ] &&
+echo "Error: Invalid number of arguments" &&
+echo "Usage: madduser DOMAIN USERNAME"
+
+domain=$1
+user=$2
+
+grep "$domain" /etc/mail/domains > /dev/null ||
+echo "Error: Invalid domain" &&
+echo "Info: It should be one of the following:" &&
+cat /etc/mail/domains &&
+return 1
+
+grep "^$user@$domain" /etc/mail/credentials > /dev/null ||
+echo "Error: User already exists in /etc/mail/credentials" && return 1
+
+grep "^$user@$domain" /etc/mail/virtuals > /dev/null ||
+echo "Error: User already exists in /etc/mail/virtuals" && return 1
+
+echo -n "Password: "
+stty -echo
+read password
+stty echo
+
+record="$(smtpctl encrypt "$password")"
+record="$user@$domain:$record:vmail:2000:2000:/var/vmail/$domain/$user::userdb_mail=maildir:/var/vmail/$domain/$user"
+echo "$record" >> /etc/mail/credentials
+
+echo "$user@$domain: vmail" >> /etc/mail/virtuals
diff --git a/files/mdeluser b/files/mdeluser
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+[ $# != 2 ] &&
+echo "Error: Invalid number of arguments" &&
+echo "Usage: mdeluser DOMAIN USERNAME"
+
+domain=$1
+user=$2
+
+grep "$domain" /etc/mail/domains > /dev/null ||
+echo "Error: Invalid domain" &&
+echo "Info: It should be one of the following:" &&
+cat /etc/mail/domains &&
+return 1
+
+grep "^$user@" /etc/mail/credentials > /dev/null ||
+echo "Error: User already exists" && return 1
+
+grep "^$user@" /etc/mail/virtuals > /dev/null ||
+echo "Error: User already exists in /etc/mail/virtuals" && return 1
+
+echo -n "Delete records for user \"$user\"? (yes/no): " &&
+read responce &&
+[ $responce = "yes" ] &&
+sed "/$user@$domain/d" /etc/mail/credentials > temp-cred-file &&
+mv temp-cred-file /etc/mail/credentials &&
+sed "/$user@$domain/d" /etc/mail/virtuals > temp-virt-file &&
+mv temp-virt-file /etc/mail/virtuals &&
+
+echo -n "Delete all mail associated with user \"$user\"? (yes/no): " &&
+read responce &&
+[ $responce = "yes" ] &&
+rm -rf /var/vmail/$domain/$user ||
+return 0
diff --git a/files/mpasswd b/files/mpasswd
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+[ $# != 2 ] &&
+echo "Error: Invalid number of arguments" &&
+echo "Usage: mpasswd DOMAIN USERNAME"
+
+domain=$1
+user=$2
+
+grep "$domain" /etc/mail/domains > /dev/null ||
+echo "Error: Invalid domain" &&
+echo "Info: It should be one of the following:" &&
+cat /etc/mail/domains &&
+return 1
+
+grep "^$user@$domain" /etc/mail/credentials > /dev/null &&
+grep "^$user@$domain" /etc/mail/virtuals > /dev/null ||
+echo "Error: Invalid username" &&
+return 1
+
+echo -n "Password: "
+stty -echo
+read password
+stty echo
+
+record="$(smtpctl encrypt "$password")"
+record="$user@$domain:$record:vmail:2000:2000:/var/vmail/$domain/$user::userdb_mail=maildir:/var/vmail/$domain/$user"
+
+sed "s#^$user@$domain.*#$record#g"
+
+echo "$record" >> /etc/mail/credentials
diff --git a/files/newuser b/files/newuser
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-echo -n "Username: "
-read -r user
-echo -n "Password: "
-read -r password
-
-record="$(smtpctl encrypt "$password")"
-record="$user@<domain>:$record:vmail:2000:2000:/var/vmail/<domain>/$user::userdb_mail=maildir:/var/vmail/<domain>/$user"
-echo "$record" >> /etc/mail/credentials
-
-echo "$user@<domain>: vmail" >> /etc/mail/virtuals
diff --git a/files/smtpd.conf b/files/smtpd.conf
@@ -8,6 +8,7 @@ pki "mail" key "/etc/ssl/private/<maildom>.key"
table aliases file:/etc/mail/aliases
table credentials passwd:/etc/mail/credentials
table virtuals file:/etc/mail/virtuals
+table domains file:/etc/mail/domains
filter "rspamd" proc-exec "/usr/local/libexec/smtpd/filter-rspamd"
@@ -17,11 +18,12 @@ listen on egress port 587 tls-require pki "mail" \
hostname "<maildom>" auth <credentials> filter "rspamd"
action "local" mbox alias <aliases>
-action "inbound" maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}" virtual <virtuals>
+action "inbound" \
+ maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}/Inbox" virtual <virtuals>
action "outbound" relay helo "<domain>"
# accept external mail
-match from any for domain "<domain>" action "inbound"
+match from any for domain <domains> action "inbound"
match from local for local action "local"
match from local for any action "outbound"
match from auth for any action "outbound"
diff --git a/mmv b/mmv
@@ -0,0 +1,184 @@
+#!/bin/sh
+
+domain=$1
+maildom="mail.$domain"
+firstuse="true"
+
+replace() { \
+sed "s/<domain>/$domain/g;s/<maildom>/$maildom/g" $1
+}
+
+success() { \
+ echo "\033[1;32m========================================================================"
+ echo "$@"
+ echo "========================================================================\033[0m"
+}
+
+failure() { \
+ echo "\033[1;31m========================================================================"
+ echo "$@"
+ echo "========================================================================\033[0m"
+}
+
+#
+# register domain
+#
+
+[ test -f /etc/mail/maindom ] || firstuse="false"
+
+[ $firstuse = "true" ] &&
+echo "$domain" >> /etc/mail/maindom
+
+grep "$domain" /etc/mail/domains > /dev/null ||
+echo "$domain" >> /etc/mail/domains
+
+#
+# install required software
+#
+
+pkg_add -I opensmtpd-extras opensmtpd-filter-rspamd dovecot \
+ dovecot-pigeonhole rspamd-3.0 redis sieve tor &&
+
+success "Installed required software"
+
+#
+# certs
+#
+
+[ $firstuse = "true" ] &&
+
+replace files/acme-client.conf >> /etc/acme-client.conf &&
+
+replace files/httpd.conf >> /etc/httpd.conf &&
+
+rcctl enable httpd &&
+rcctl restart httpd &&
+
+acme-client -v $maildom &&
+
+replace files/daily.local >> /etc/daily.local &&
+
+success "Created and signed tls certificates (letencrypt)"
+
+#
+# vmail user & authentication
+#
+
+[ $firstuse = "true" ] &&
+
+touch /etc/mail/credentials &&
+chmod 0440 /etc/mail/credentials &&
+chown _smtpd:_dovecot /etc/mail/credentials &&
+useradd -c "Virtual Mail Account" -d /var/vmail -s /sbin/nologin \
+ -u 2000 -g =uid -L staff vmail &&
+mkdir -p /var/vmail &&
+chown vmail:vmail /var/vmail &&
+
+replace files/virtuals >> /etc/mail/virtuals &&
+
+success "Created vmail user & authentication file"
+
+#
+# smtpd
+#
+
+[ $firstuse = "true" ] &&
+
+replace files/smtpd.conf > /etc/mail/smtpd.conf &&
+
+cp files/madduser /usr/local/bin/ &&
+cp files/mdeluser /usr/local/bin/ &&
+
+success "Configured OpenSMTPD"
+
+#
+# dovecot
+#
+
+[ $firstuse = "true" ] &&
+
+echo "dovecot:\\
+ :openfiles-cur=1024:\\
+ :openfiles-max=2048:\\
+ :tc=daemon:
+" >> /etc/login.conf &&
+
+replace files/local.conf > /etc/dovecot/local.conf &&
+
+sed "s/^ssl_cert/#ssl_cert/;s/^ssl_key/#ssl_key/" \
+ /etc/dovecot/conf.d/10-ssl.conf > tempfile &&
+mv tempfile /etc/dovecot/conf.d/10-ssl.conf &&
+
+# setup training rspamd from email moving in and out of the Junk folder
+
+mkdir -p /usr/local/lib/dovecot/sieve &&
+cp files/report-ham.sieve /usr/local/lib/dovecot/sieve &&
+cp files/report-spam.sieve /usr/local/lib/dovecot/sieve &&
+sievec /usr/local/lib/dovecot/sieve/report-ham.sieve &&
+sievec /usr/local/lib/dovecot/sieve/report-spam.sieve &&
+
+cp files/sa-learn-ham.sh /usr/local/lib/dovecot/sieve/ &&
+cp files/sa-learn-spam.sh /usr/local/lib/dovecot/sieve/ &&
+chmod 0755 /usr/local/lib/dovecot/sieve/sa-learn-ham.sh &&
+chmod 0755 /usr/local/lib/dovecot/sieve/sa-learn-spam.sh &&
+
+rcctl enable dovecot &&
+rcctl start dovecot &&
+
+success "Configured Dovecot"
+
+#
+# rspamd
+#
+
+[ $firstuse = "true" ] &&
+
+mkdir -p /etc/mail/dkim &&
+openssl genrsa -out /etc/mail/dkim/$domain.key 1024 &&
+openssl rsa -in /etc/mail/dkim/$domain.key \
+ -pubout -out /etc/mail/dkim/public.key &&
+chmod 0440 /etc/mail/dkim/$domain.key &&
+chown root:_rspamd /etc/mail/dkim/$domain.key &&
+
+replace files/dkim_signing.conf > /etc/rspamd/local.d/dkim_signing.conf &&
+
+rcctl enable redis rspamd &&
+rcctl start redis rspamd &&
+rcctl restart smtpd &&
+
+success "Configured rspamd"
+
+#
+# dns
+#
+
+[ $firstuse = "true" ] &&
+
+pub_key=$(grep -v -e "---" /etc/mail/dkim/public.key | tr -d '\n' ) &&
+mkdir -p dns &&
+echo "mail._domainkey.$domain. IN TXT \"v=DKIM1;k=rsa;p=$pub_key\"" > /etc/mail/dns/$domain/dkim &&
+echo "$domain. IN TXT \"v=spf1 mx -all\"" > /etc/mail/dns/$domain/spf &&
+echo "_dmarc.$domain. IN TXT
+\"v=DMARC1;p=quarantine;pct=100;rua=mailto:postmaster@$domain\"" > /etc/mail/dns/$domain/dmarc &&
+echo "$domain. IN MX 0 $maildom." > /etc/mail/dns/$domain/mx &&
+success "Wrote relevant dns records in /etc/mail/dns/$domain/"
+
+[ $firstuse = "false" ] &&
+
+echo "$domain. IN MX 0 $maildom." > /etc/mail/dns/$domain/mx &&
+success "Wrote relevant dns records in /etc/mail/dns/$domain/"
+
+# TODO: does .forward work with virtual users?
+
+[ $firstuse = "true" ] &&
+
+success \
+"The creation of an admin account is required for this setup! Email to
+it can be forwarded to an email address written in:
+/var/vmail/$domain/admin/.forward
+Please choose a password for the \"admin\" user." &&
+
+madduser $domain admin &&
+rcctl restart smtpd
+
+success "Done!"
diff --git a/setup.sh b/setup.sh
@@ -1,147 +0,0 @@
-#!/bin/sh
-
-domain=$1
-maildom="mail.$domain"
-
-replace() { \
-sed "s/<domain>/$domain/g;s/<maildom>/$maildom/g" $1
-}
-
-success() { \
- echo "\033[1;32m==================================================="
- echo "$@"
- echo "===================================================\033[0m"
-}
-
-failure() { \
- echo "\033[1;31m==================================================="
- echo "$@"
- echo "\033[0m==================================================="
-}
-
-#
-# install required software
-#
-
-pkg_add opensmtpd-extras opensmtpd-filter-rspamd dovecot dovecot-pigeonhole rspamd redis sieve &&
-
-success "Installed required software" &&
-
-#
-# certs
-#
-
-replace files/acme-client.conf >> /etc/acme-client.conf &&
-
-replace files/httpd.conf >> /etc/httpd.conf &&
-
-rcctl enable httpd &&
-rcctl start httpd &&
-
-acme-client -v $maildom &&
-
-replace files/daily.local >> /etc/daily.local &&
-
-success "Created and signed tls certificates (letencrypt)" &&
-
-#
-# vmail user & authentication
-#
-
-touch /etc/mail/credentials &&
-chmod 0440 /etc/mail/credentials &&
-chown _smtpd:_dovecot /etc/mail/credentials &&
-useradd -c "Virtual Mail Account" -d /var/vmail -s /sbin/nologin \
- -u 2000 -g =uid -L staff vmail &&
-mkdir -p /var/vmail &&
-chown vmail:vmail /var/vmail &&
-
-replace files/virtuals >> /etc/mail/virtuals &&
-replace files/newuser > ./newuser &&
-chmod +x ./newuser &&
-
-success "Created vmail user & authentication file" &&
-
-#
-# smtpd
-#
-
-replace files/smtpd.conf > /etc/mail/smtpd.conf &&
-
-success "Configured OpenSMTPD" &&
-
-#
-# dovecot
-#
-
-echo "dovecot:\\
- :openfiles-cur=1024:\\
- :openfiles-max=2048:\\
- :tc=daemon:
-" >> /etc/login.conf &&
-
-replace files/local.conf > /etc/dovecot/local.conf &&
-
-sed "s/^ssl_cert/#ssl_cert/;s/^ssl_key/#ssl_key/" \
- /etc/dovecot/conf.d/10-ssl.conf > tempfile &&
-mv tempfile /etc/dovecot/conf.d/10-ssl.conf &&
-
-# setup training rspamd from email moving in and out of the Junk folder
-
-mkdir -p /usr/local/lib/dovecot/sieve &&
-cp files/report-ham.sieve /usr/local/lib/dovecot/sieve &&
-cp files/report-spam.sieve /usr/local/lib/dovecot/sieve &&
-sievec /usr/local/lib/dovecot/sieve/report-ham.sieve &&
-sievec /usr/local/lib/dovecot/sieve/report-spam.sieve &&
-
-cp files/sa-learn-ham.sh /usr/local/lib/dovecot/sieve/ &&
-cp files/sa-learn-spam.sh /usr/local/lib/dovecot/sieve/ &&
-chmod 0755 /usr/local/lib/dovecot/sieve/sa-learn-ham.sh &&
-chmod 0755 /usr/local/lib/dovecot/sieve/sa-learn-spam.sh &&
-
-rcctl enable dovecot &&
-rcctl start dovecot &&
-
-success "Configured Dovecot" &&
-
-#
-# rspamd
-#
-
-mkdir -p /etc/mail/dkim &&
-openssl genrsa -out /etc/mail/dkim/$domain.key 1024 &&
-openssl rsa -in /etc/mail/dkim/$domain.key \
- -pubout -out /etc/mail/dkim/public.key &&
-chmod 0440 /etc/mail/dkim/$domain.key &&
-chown root:_rspamd /etc/mail/dkim/$domain.key &&
-
-replace files/dkim_signing.conf > /etc/rspamd/local.d/dkim_signing.conf &&
-
-rcctl enable redis rspamd &&
-rcctl start redis rspamd &&
-rcctl restart smtpd &&
-
-success "Configured rspamd" &&
-
-#
-# dns
-#
-
-pub_key=$(grep -v -e "---" /etc/mail/dkim/public.key | tr -d '\n' ) &&
-
-mkdir -p dns &&
-echo "mail._domainkey.$domain. IN TXT \"v=DKIM1;k=rsa;p=$pub_key\"" > ./dns/dkim-record &&
-echo "$domain. IN TXT \"v=spf1 mx -all\"" > ./dns/spf-record &&
-echo "_dmarc.$domain. IN TXT \"v=DMARC1;p=none;pct=100;rua=mailto:postmaster@$domain\"" > ./dns/dmarc-record &&
-
-success "Wrote relevant dns records in ./dns/" &&
-# TODO: does .forward work with virtual users?
-success \
-"The creation of an admin account is required for this setup! Email to
-it can be forwarded to an email address written in:
-/var/vmail/$domain/admin/.forward
-New users can be similarly added by running ./newuser. Please use the
-username \"admin\" and a password of your choosing" &&
-
-./newuser &&
-rcctl restart smtpd