first, and hopefully last commit

This commit is contained in:
lolcat 2025-07-13 18:46:18 -04:00
commit dba715f47f
14 changed files with 508 additions and 0 deletions

174
README.md Normal file
View File

@ -0,0 +1,174 @@
![Bullmail logo](img/logo.png)
BullMail is a no-bullshit solution to setup an E-Mail server on a Debian/Ubuntu shitbox.
## What it does
- Creates an `SMTP` server (`postfix`, to send and receive E-Mails)
- Creates an `IMAP` server (`dovecot`, to check your E-Mails)
- Configures `rspamd` server to filter the spam (yes, the junk emails goes in the Junk folder! **WOW!!!**)
- Signs outgoing E-Mails with `DKIM` (using `rspamd`)
- Strips out your IP address and user agent from the headers of **outgoing** E-Mails (and no, it doesn't fuck with the **INCOMING** headers for fucks sake!)
- Adds `User-Agent: Mozilla Thunderbird` to all E-Mails instead ^^
- `sendmail` just fucking works. No header fuckery, it all just fucking works as described. Programs like Gitea don't fail to send out mail.
- Mailboxes: **Inbox, Drafts, Sent, Junk & Trash** for each user. If you remove & add an E-Mail account in your client, **the mailboxes don't DISAPPEAR FOR NO FUCKING REASON!!**
- Mails are stored in `~/Maildir`, no unecessary database bullshit, just live your life and go outside
- Restores your sanity, it just fucking works
## What it DOESN'T do
- Sets up some gay fucking roundcube piece of shit webmail interface (although you can set that shit up if you're homosexual)
- Fucks with SSL certificate creation: **YOU SUPPLY YOUR OWN FUCKING CERTIFICATE!** (Just make sure to restart `postfix`, `dovecot` & `rspamd` **IN THIS ORDER** when replacing the cert)
- Not suitable for many users, since they need a /home/ directory. Meant for private use. If you want to run a public mail server, here's a pro tip: **FUCKING DON'T DO THAT**
## Port bindings
Certain pieces of shit ISPs like Videotron will block you from **connecting** to your server on ports `25` and `143`, so special alternative ports are configured. If your client is explicitly configured to do so, you can connect **without** SSL with plain auth, so legacy E-Mail clients (like a 4th gen iPod touch) can connect without any bullshit.
- `SMTP`: 25, 2525 (SASL+Plain auth with optional `STARTTLS`)
- `IMAP`: 143, 1143 (SASL+Plain auth, **NO** SSL)
- `IMAPs`: 993, 1993 (SASL+Plain auth, SSL)
# Requirements
- You need a domain, duh. Complete control over it.
- A compute machine. Don't get some scummy host, otherwise your E-Mails are going straight to spam. If you're hosting it under a residential IP, you will get issues with spam lists. **DO YOUR RESEARCH**
- Patience. I know it's your 10th attempt installing an E-Mail server, trust me this script just fucking works okay?
# Installation
## Step 1
Log onto your registrar and set and A and AAAA record onto your domain that points to your server. For the retards in the back:
- `A` record: You give an IPv4 address to your server
- `AAAA` record: Thats where you put the IPv6 address
Now, you want to point these records in a way that `mail.yourdomain.com` resolves to your server's IP.
**Keep your domain's DNS manager open, we're not done with it.**
## Step 2
Go to your host's server configuration panel and set the rDNS (Reverse DNS) to `mail.yourdomain.com` (obviously, replace `yourdomain.com` with your own). This helps some exotic mail servers to perceive your E-Mails as not spam. After setting up the rDNS, restart the server and run this from your local machine:
```
will@fuckedmachine:~$ ping yourdomain.com
PING yourdomain.com (x.x.x.x) 56(84) bytes of data.
64 bytes from mail.yourdomain.com (x.x.x.x): icmp_seq=1 ttl=52 time=13.4 ms
^C
--- yourdomain.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 13.445/13.445/13.445/0.000 ms
```
The 3rd line is the interesting part. It should say `64 bytes from mail.yourdomain.com`.
## Step 3
SSH onto your shitbox and become root
```
sudo su
```
## Step 4
Create your mail user. Note that all users with the `mail` group will be able to send and receive mail.
```
useradd -m yourname -G mail
passwd yourname
```
OR... If you already have an user that you use (say, `will`), do this instead:
```
usermod -aG mail will
```
`-aG` tells your stupid server "hey, `mail` is a secondary group for `will`!" `-G` says it's the primary group.
## Step 5
Run these commands (don't run `bullmail.sh` quite yet you moron)
```
git clone https://git.lolcat.ca/lolcat/bullmail
cd bullmail/script
chmod +x bullmail.sh
```
## Step 6
Edit `bullmail.sh` and change the variables at the top to what matches your configuration. In my case, this is what I used:
```
#
# Input your settings here
#
tls_cert="/etc/apache2/ssl/lolcat.ca.pem"
tls_key="/etc/apache2/ssl/lolcat.ca.key"
hostname="mail.lolcat.ca"
domain="lolcat.ca"
```
Yes, you need to provide your own TLS certificates. Pro tip: ACME.sh rules, you will lose your sanity using certbot.
## Step 7
I know this is not the first time you're trying to install a mail server. Make sure that you cleaned up your previous attempts before running this. This script makes an effort to clean up after your mess, but there are no guarantees.
## Step 8
Run the script. Make sure you're root and inside the `script` folder
```
./bullmail.sh
```
Wait for it to complete. Trust the plan.
## Step 9
The script should output something like this at the end:
```
Done. Please set this TXT record on your yourdomain.com domain.
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvrB..." ) ;
Bye!
```
In your DNS settings of your domain, create a TXT record called `mail._domainkey` with the value `v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvrB...` (truncated)
Make sure to copy-paste what the script outputs, not the bullshit I gave as an example, you moron.
# Check the shit works
Make sure to wait for the DNS to propagate, and then send an email to ping@tools.mxtoolbox.com. You should get an email back with your deliverability score, and it should tell you if your DKIM is setup correctly. You'll also see what headers are being sent when you send out an email. Here are what my headers look on my server:
```
From will@lolcat.ca Fri Jul 11 03:32:35 2025
Return-Path: <will@lolcat.ca>
X-Original-To: ping@tools.mxtoolbox.com
Delivered-To: ping@tools.mxtoolbox.com
Received: from mail.lolcat.ca (mail.lolcat.ca [51.79.70.119])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)
(No client certificate requested)
by tools.mxtoolbox.com (Postfix) with ESMTPS id 84DB9AD33D
for <ping@tools.mxtoolbox.com>; Fri, 11 Jul 2025 03:32:35 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lolcat.ca; s=mail;
t=1752204755;
h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
to:to:cc:mime-version:mime-version:content-type:content-type:
content-transfer-encoding:content-transfer-encoding;
bh=g3zLYH4xKxcPrHOD18z9YfpQcnk/GaJedfustWU5uGs=;
b=RNPVHLULrmpWuCTizr8z8B7aTIUdd19GBeKevo21hacpjS2aFWEDhE8vhea25/XikmJ+8C
ywJLr+TXO+iHb1mG30Unf+760NFuoT3OlAnTHhXbL+D9ozEeKfSbIp4kEtCYwTwRExfrOx
FxiKUvBhUXe1Wy8O8lTKZbhri1N+2pM=
Message-ID: <c1575f29-6793-4971-99ba-eb7ad4b3254f@lolcat.ca>
Date: Thu, 10 Jul 2025 23:32:34 -0400
MIME-Version: 1.0
To: ping@tools.mxtoolbox.com
Content-Language: en-US
User-Agent: Mozilla Thunderbird
From: will <will@lolcat.ca>
Subject: test
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
test
```
# Screenshots
## Inbox
![Inbox screenshot](img/inbox.png)
## Junk
![Junk screenshot](img/junk.png)
## 4th gen iPod mail reader
![4th gen iPod mail reader screenshot](img/mailread_ipod.jpg)
# License
WTFPL

BIN
img/inbox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
img/junk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
img/mailread_ipod.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

3
script/actions.conf Normal file
View File

@ -0,0 +1,3 @@
reject = 100;
add_header = 5;
greylist = 100;

103
script/bullmail.sh Executable file
View File

@ -0,0 +1,103 @@
#!/bin/bash
#
# Input your settings here
#
tls_cert="/etc/apache2/ssl/yourdomain.com.pem"
tls_key="/etc/apache2/ssl/yourdomain.com.key"
hostname="mail.yourdomain.com"
domain="yourdomain.com"
#
# Script
#
echo "Welcome to bullmail"
if [ "$EUID" -ne 0 ]; then
echo "Run this shit as root. Exiting"
exit 1
fi
packages="dovecot-core dovecot-imapd postfix rspamd postfix dovecot-lmtpd dovecot-sieve dovecot-managesieved postfix postfix-pcre"
echo "=== Cleaning up garbage... ==="
rm -rf /etc/rspamd/
rm -rf /etc/dovecot/
apt purge --autoremove $packages -y
echo "=== Installing packages ==="
apt install $packages -y
echo "=== Configuring rspamd ==="
echo "Generating DKIM keys..."
mkdir -p /var/lib/rspamd/dkim
chown _rspamd:_rspamd /var/lib/rspamd/dkim
chmod 750 /var/lib/rspamd/dkim
dkim=$(rspamadm dkim_keygen -d $domain -s mail)
private_key=$(echo "$dkim" | awk 'BEGIN {RS="-----END PRIVATE KEY-----"} NR==1 {print $0 RS}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
dns_record=$(echo "$dkim" | awk 'BEGIN {RS="-----END PRIVATE KEY-----"} NR==2 {print $0}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
touch /var/lib/rspamd/dkim/$domain.mail.key
echo "$private_key" > "/var/lib/rspamd/dkim/$domain.mail.key"
rspamd_dkim=$(< "$PWD/dkim_signing.conf")
rspamd_dkim="${rspamd_dkim//__domain__/$domain}"
echo "$rspamd_dkim" > "/etc/rspamd/local.d/dkim_signing.conf"
chown _rspamd:_rspamd /var/lib/rspamd/dkim/$domain.mail.key
chmod 644 /var/lib/rspamd/dkim/$domain.mail.key
cp $PWD/milter_headers.conf /etc/rspamd/local.d/milter_headers.conf
cp $PWD/actions.conf /etc/rspamd/local.d/actions.conf
touch /var/log/rspamd/rspamd.log
chown _rspamd:_rspamd -R /etc/rspamd/local.d
chown _rspamd:_rspamd /var/log/rspamd/rspamd.log
chmod 744 /etc/rspamd/local.d
chmod 644 /etc/rspamd/local.d/*
chmod 644 /var/log/rspamd/rspamd.log
echo "Restarting rspamd..."
service rspamd restart
echo "=== Configuring Postfix ==="
postfix_conf=$(< "$PWD/main.cf")
postfix_conf="${postfix_conf//KEY.pem/$tls_cert}"
postfix_conf="${postfix_conf//KEY.key/$tls_key}"
postfix_conf="${postfix_conf//__domain__/$domain}"
postfix_conf="${postfix_conf//__hostname__/$hostname}"
echo "$postfix_conf" > "/etc/postfix/main.cf"
escaped_domain="${hostname//./\\.}"
header_checks=$(< "$PWD/header_checks")
header_checks="${header_checks//__hostname__/$escaped_domain}"
touch /etc/postfix/header_checks
echo "$header_checks" > "/etc/postfix/header_checks"
cp $PWD/master.cf /etc/postfix/master.cf
echo "Restarting Postfix..."
service postfix restart
echo "=== Configuring Dovecot ==="
dovecot_conf=$(< "$PWD/dovecot.conf")
dovecot_conf="${dovecot_conf//KEY.pem/$tls_cert}"
dovecot_conf="${dovecot_conf//KEY.key/$tls_key}"
dovecot_conf="${dovecot_conf//__domain__/$domain}"
echo "$dovecot_conf" > "/etc/dovecot/dovecot.conf"
mkdir -p /etc/dovecot/sieve
cp $PWD/spam2junk.sieve /etc/dovecot/sieve
sievec /etc/dovecot/sieve/spam2junk.sieve
chown -R vmail:vmail /etc/dovecot/sieve
echo "Restarting Dovecot..."
service dovecot restart
echo "Done. Please set this TXT record on your $domain domain."
echo $dns_record
echo "Bye!"

11
script/dkim_signing.conf Normal file
View File

@ -0,0 +1,11 @@
enabled = true;
sign_authenticated = true;
allow_hdrfrom_mismatch = false;
allow_username_mismatch = true;
domain {
__domain__ {
selector = "mail";
path = "/var/lib/rspamd/dkim/__domain__.mail.key";
}
}

99
script/dovecot.conf Normal file
View File

@ -0,0 +1,99 @@
#logging
#auth_verbose = yes
#auth_debug = yes
#mail_debug = yes
#log_path = /var/log/dovecot.log
disable_plaintext_auth = no
auth_mechanisms = plain login
auth_username_format = %Ln
ssl = yes
ssl_cert = <KEY.pem
ssl_key = <KEY.key
ssl_client_ca_dir = /etc/ssl/certs
ssl_dh = </usr/share/dovecot/dh.pem
ssl_min_protocol = TLSv1.1
service imap-login {
inet_listener imap {
port = 143
}
inet_listener imap_alt {
port = 1143
}
inet_listener imaps {
port = 993
ssl = yes
}
inet_listener imaps_alt {
port = 1993
ssl = yes
}
}
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}
mail_location = maildir:~/Maildir
mail_privileged_group = mail
managesieve_notify_capability = mailto
managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext
namespace inbox {
inbox = yes
location = maildir:~/Maildir
prefix =
mailbox Drafts {
special_use = \Drafts
auto = subscribe
}
mailbox Junk {
special_use = \Junk
auto = subscribe
}
mailbox Sent {
special_use = \Sent
auto = subscribe
}
mailbox Trash {
special_use = \Trash
auto = subscribe
}
}
passdb {
driver = pam
}
userdb {
driver = passwd
}
plugin {
sieve_global_path = /etc/dovecot/sieve/spam2junk.sieve
#sieve_global_dir = /etc/dovecot/sieve/
#sieve = file:~/sieve;active=~/.dovecot.sieve
}
protocol lmtp {
mail_plugins = $mail_plugins sieve
postmaster_address = postmaster@__domain__
mail_fsync = optimized
}
protocol lda {
mail_plugins = $mail_plugins sieve
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
group = postfix
mode = 0600
user = postfix
}
}
protocols = imap lmtp

8
script/header_checks Normal file
View File

@ -0,0 +1,8 @@
/^\s*Received: from __hostname__/ OK
/^\s*Received:/ IGNORE
/^From:/ PREPEND User-Agent: Mozilla Thunderbird
/^\s*X-Enigmail/ IGNORE
/^\s*X-Mailer/ IGNORE
/^\s*X-Originating-IP/ IGNORE
/^\s*X-Forward/ IGNORE
/^\s*User-Agent/ IGNORE

51
script/main.cf Normal file
View File

@ -0,0 +1,51 @@
# logging
maillog_file = /var/log/mail.log
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no
append_dot_mydomain = no
readme_directory = no
compatibility_level = 3.6
smtpd_tls_cert_file = KEY.pem
smtpd_tls_key_file = KEY.key
smtpd_tls_security_level = may
smtpd_milters = inet:127.0.0.1:11332
non_smtpd_milters = inet:127.0.0.1:11332
milter_default_action = accept
milter_protocol = 6
milter_mail_macros = i {auth_authen} {client_addr}
smtp_header_checks = pcre:/etc/postfix/header_checks
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_tls_auth_only = no
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = __hostname__
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
virtual_transport = dovecot
local_transport = lmtp:unix:private/dovecot-lmtp
dovecot_destination_recipient_limit = 1
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 = __hostname__
mydomain = __domain__
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = __hostname__, __domain__, localhost, localhost.localdomain, localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
home_mailbox = Maildir/

46
script/master.cf Normal file
View File

@ -0,0 +1,46 @@
smtp inet n - y - - smtpd
-o smtpd_milters=inet:127.0.0.1:11332
2525 inet n - y - - smtpd
-o smtpd_milters=inet:127.0.0.1:11332
pickup unix n - y 60 1 pickup
cleanup unix n - y - 0 cleanup
qmgr unix n - n 300 1 qmgr
tlsmgr unix - - y 1000? 1 tlsmgr
rewrite unix - - y - - trivial-rewrite
bounce unix - - y - 0 bounce
defer unix - - y - 0 bounce
trace unix - - y - 0 bounce
verify unix - - y - 1 verify
flush unix n - y 1000? 0 flush
proxymap unix - - n - - proxymap
proxywrite unix - - n - 1 proxymap
smtp unix - - y - - smtp
relay unix - - y - - smtp
-o syslog_name=postfix/$service_name
showq unix n - y - - showq
error unix - - y - - error
retry unix - - y - - error
discard unix - - y - - discard
local unix - n n - - local
virtual unix - n n - - virtual
lmtp unix - - y - - lmtp
anvil unix - - y - 1 anvil
scache unix - - y - 1 scache
postlog unix-dgram n - n - 1 postlogd
maildrop unix - n n - - pipe
flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
uucp unix - n n - - pipe
flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
ifmail unix - n n - - pipe
flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp unix - n n - - pipe
flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix - n n - 2 pipe
flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman unix - n n - - pipe
flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user}
dovecot unix - n n - - pipe
flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop}

View File

@ -0,0 +1,7 @@
enabled = true;
# avoid x-spam headers on sent emails
use_authenticated = false;
# add x-spam headers
extended_spam_headers = true;

6
script/spam2junk.sieve Normal file
View File

@ -0,0 +1,6 @@
require ["fileinto"];
if header :contains "X-Spam" "Yes" {
fileinto "Junk";
stop;
}