Further testing and refinement completed.

This commit is contained in:
Brian Lee 2023-08-11 12:22:29 -07:00
parent c4bdc0164b
commit 0a286ccc1c
10 changed files with 99 additions and 23 deletions

View File

@ -55,6 +55,14 @@ Postfix `master.cf` should configure smtpd behavior to require encrypted client
See [docs/CLIENTS.md](docs/CLIENTS.md) for notes on mail clients. See [docs/CLIENTS.md](docs/CLIENTS.md) for notes on mail clients.
## Backups
See the provided [example](docs/examples/backup.sh) script. Keep in mind that when restoring the `imap.passwd` file for Dovecot, that a new system will have different user ids for maildir. There is a helper to rewrite all the uid/gids to the maildir user when restoring from a backup on a new system:
```bash
ansible-playbook -e 'force_dovecot_passwd_file_maildir_ids=yes' playbooks/mail.yml
```
## Misc ## Misc
There are some interesting mta implementations that may replace or compliment parts of this stack in the future: There are some interesting mta implementations that may replace or compliment parts of this stack in the future:

View File

@ -8,8 +8,6 @@ dkim_key_path: /etc/dkimkeys
#postfix_hostname: "{{ dkim_selector }}.{{ postfix_domain }}" #postfix_hostname: "{{ dkim_selector }}.{{ postfix_domain }}"
imap_bind_address: "{{ ansible_default_ipv4.address|default(ansible_all_ipv4_addresses[0]) }}" imap_bind_address: "{{ ansible_default_ipv4.address|default(ansible_all_ipv4_addresses[0]) }}"
#postfix_maildir_user: maildir #postfix_maildir_user: maildir
postfix_virtual_uid: "{{ ansible_facts.getent_passwd.maildir[1] }}"
postfix_virtual_gid: "{{ ansible_facts.getent_group.maildir[1] }}"
#postfix_inet_interfaces: all #postfix_inet_interfaces: all
#postfix_mynetworks: #postfix_mynetworks:
# - 127.0.0.0/8 # - 127.0.0.0/8
@ -42,9 +40,6 @@ postfix_smtpd_client_message_rate_limit: 3
postfix_smtpd_client_new_tls_session_rate_limit: 3 postfix_smtpd_client_new_tls_session_rate_limit: 3
postfix_smtpd_client_auth_rate_limit: 3 postfix_smtpd_client_auth_rate_limit: 3
# robertdebock.dovecot
dovecot_mailbox_location: "maildir:{{ postfix_virtual_mailbox_base }}/{{ postfix_domain }}/%n"
postfix_install: postfix_install:
- postfix - postfix
# - mailutils # - mailutils
@ -118,4 +113,9 @@ postfix_smtpd_sasl_path: private/auth
postfix_virtual_mailbox_maps: /etc/postfix/vmailbox postfix_virtual_mailbox_maps: /etc/postfix/vmailbox
#postfix_virtual_uid: 1000 #postfix_virtual_uid: 1000
#postfix_virtual_gid: 1000 #postfix_virtual_gid: 1000
postfix_maildir_user: maildir postfix_maildir_user: maildir
force_dovecot_passwd_file_maildir_ids: false # useful when recovering from backup
# robertdebock.dovecot
dovecot_mailbox_location: "maildir:{{ postfix_virtual_mailbox_base }}/{{ postfix_domain }}/%n"

View File

@ -1,6 +1,15 @@
# Mail Server: Deployment # Mail Server: Deployment
1. Create MX and TXT records 1. Create MX and TXT records. For example, here are example records defined in dnscontrol:
```Javascript
D('example.com', REG_NAMECHEAP, DnsProvider(DSP_NAMECHEAP),
A('mail', '10.87.129.99'),
MX('@', 10, 'mail.example.com.'),
TXT('_dmarc', 'v=DMARC1; p=none'),
TXT('@', 'v=spf1 mx ~all')
);
```
The `A` and `MX` records are required, while the `TXT` records are optional but recommended.
2. Set a password for the "main" virtual inbox: 2. Set a password for the "main" virtual inbox:
@ -8,11 +17,17 @@
echo main:$(doveadm pw -s BLF-CRYPT) >> files/$TARGET/imap.passwd echo main:$(doveadm pw -s BLF-CRYPT) >> files/$TARGET/imap.passwd
``` ```
Also, if you use `doas` rather than `sudo`, you need to permit your ansible_user to become opendkim in your `/etc/doas.conf`:
```
permit nopass blee as opendkim
```
3. Copy a vars/targets file, update the values, and run this playbook 3. Copy a vars/targets file, update the values, and run this playbook
Sanity check opendkim (may need restart): Troubleshooting: Sanity check opendkim (may need restart, although I think I fixed that):
```shell ```shell
l /var/spool/postfix/opendkim/opendkim.sock ls -AlF /var/spool/postfix/opendkim/opendkim.sock
``` ```
4. look at the maildir uid/gid in main.cf and use those in the imap.passwd file (switching to the dovecot role will fix that later) 4. look at the maildir uid/gid in main.cf and use those in the imap.passwd file (switching to the dovecot role will fix that later)
@ -29,9 +44,20 @@
7. (optional) Create another TXT record for DKIM using the contents of /etc/dkimkeys/mail.txt 7. (optional) Create another TXT record for DKIM using the contents of /etc/dkimkeys/mail.txt
* See [scripts/print-rdata.py](../scripts/print-rdata.py) for an example of how to parse mail.txt Here's an example line in dnscontrol:
* See [octodns](https://github.com/octodns/octodns-easydns) and [dnscontrol](https://dnscontrol.org/)
```Javascript
TXT('mail._domainkey', 'v=DKIM1; h=sha256; k=rsa; s=email; p=MIIBIjANB...QIDAQAB')
```
8. (optional) After records propogate, verify outbound mail using: https://www.mail-tester.com/ * See [print-rdata.py](examples/print-rdata.py) for a (kind of bad) example of how to automatically parse mail.txt
* See [dnscontrol](https://dnscontrol.org/) as well as [octodns](https://github.com/octodns/octodns-easydns)
If you're really feeling adventurous, you could even set up a proper dmarc address to replace the original placeholder TXT record.
```Javascript
TXT('_dmarc', 'v=DMARC1; p=reject; rua=mailto:dmarc@satstack.cloud; fo=1')
```
After records propogate, verify outbound mail using [mail-tester](https://www.mail-tester.com/).

10
docs/examples/backup.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
set -x
TARGET=mail.example.com
mkdir -p $HOME/archive/${TARGET}/{dovecot,postfix}
rsync -tav root@${TARGET}:/etc/dovecot/imap.passwd $HOME/archive/${TARGET}/
rsync -tav root@${TARGET}:/etc/postfix/virtual $HOME/archive/${TARGET}/postfix
rsync -tav root@${TARGET}:/etc/dkimkeys $HOME/archive/${TARGET}/
rsync -tav root@${TARGET}:/var/vmail $HOME/archive/${TARGET}/

13
docs/examples/print-rdata.py Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
import zonefile_parser
with open("mail.txt","r") as stream:
content = stream.read()
records = zonefile_parser.parse(content)
for record in records:
print()
print(f"{record.name}:")
print(record.rdata['value'])
print()

View File

@ -7,7 +7,7 @@
- name: Register Dovecot account database - name: Register Dovecot account database
ansible.builtin.file: ansible.builtin.file:
path: /etc/dovecot/imap.passwd path: "{{ dovecot_passwd_file }}"
state: touch state: touch
owner: root owner: root
group: dovecot group: dovecot
@ -32,3 +32,11 @@
src: dovecot.conf.j2 src: dovecot.conf.j2
dest: /etc/dovecot/local.conf dest: /etc/dovecot/local.conf
notify: restart dovecot notify: restart dovecot
- name: Update UID and GID in imap.passwd
ansible.builtin.replace:
path: "{{ dovecot_passwd_file }}"
regexp: '(.*):(\d+):(\d+)$'
replace: '\1:{{ maildir_uid }}:{{ maildir_gid }}'
when: force_dovecot_passwd_file_maildir_ids
notify: restart dovecot

View File

@ -32,12 +32,20 @@
system: true system: true
append: true append: true
- name: Get maildir user's id - name: Add maildir user's id and group id to ansible_facts
ansible.builtin.getent: ansible.builtin.getent:
database: passwd database: passwd
key: "{{ postfix_maildir_user }}" key: "{{ postfix_maildir_user }}"
- name: Get maildir user's group id #- name: Add maildir user's id and group id to ansible_facts
ansible.builtin.getent: # ansible.builtin.getent:
database: group # database: "{{ item }}"
key: "{{ postfix_maildir_user }}" # key: "{{ postfix_maildir_user }}"
# loop:
# - passwd
# - group
- name: Set maildir UID and GID
set_fact:
maildir_uid: "{{ ansible_facts.getent_passwd[postfix_maildir_user][1] }}"
maildir_gid: "{{ ansible_facts.getent_passwd[postfix_maildir_user][2] }}"

View File

@ -21,7 +21,7 @@ service imap-login {
# doveadm pw -s BLF-CRYPT # doveadm pw -s BLF-CRYPT
passdb { passdb {
driver = passwd-file driver = passwd-file
args = username_format=%n scheme=blf-crypt /etc/dovecot/imap.passwd args = username_format=%n scheme=blf-crypt {{ dovecot_passwd_file }}
auth_verbose=yes auth_verbose=yes
} }
@ -30,7 +30,7 @@ passdb {
userdb { userdb {
driver = passwd-file driver = passwd-file
args = username_format=%n /etc/dovecot/imap.passwd args = username_format=%n {{ dovecot_passwd_file }}
default_fields = uid={{ postfix_maildir_user }} gid={{ postfix_maildir_user }} default_fields = uid={{ postfix_maildir_user }} gid={{ postfix_maildir_user }}
# override_fields = # override_fields =

View File

@ -17,8 +17,8 @@ virtual_mailbox_base = {{ postfix_virtual_mailbox_base }}/{{ postfix_domain }}
virtual_mailbox_maps = {{ postfix_default_database_type }}:{{ postfix_virtual_mailbox_maps }} virtual_mailbox_maps = {{ postfix_default_database_type }}:{{ postfix_virtual_mailbox_maps }}
virtual_mailbox_limit = 0 virtual_mailbox_limit = 0
# User: {{ postfix_maildir_user }} # User: {{ postfix_maildir_user }}
virtual_uid_maps = static:{{ postfix_virtual_uid }} virtual_uid_maps = static:{{ maildir_uid }}
virtual_gid_maps = static:{{ postfix_virtual_gid }} virtual_gid_maps = static:{{ maildir_gid }}
virtual_alias_maps = {{ postfix_default_database_type }}:{{ postfix_virtual_aliases_file }} virtual_alias_maps = {{ postfix_default_database_type }}:{{ postfix_virtual_aliases_file }}
{% endif %} {% endif %}

View File

@ -21,4 +21,7 @@ postfix_virtual_mailbox_file: /etc/postfix/vmailbox
postfix_smtpd_tls_dh1024_param_file: /etc/ssh/dhaparams.pem postfix_smtpd_tls_dh1024_param_file: /etc/ssh/dhaparams.pem
# https://github.com/vdukhovni/postfix/blob/master/postfix/INSTALL # https://github.com/vdukhovni/postfix/blob/master/postfix/INSTALL
postfix_compatibility_level: 3.5 # Debian 11 postfix_compatibility_level: 3.5 # Debian 11
# dovecot
dovecot_passwd_file: /etc/dovecot/imap.passwd