Compare commits

...

2 commits

Author SHA1 Message Date
a0a7dfbaf6
Vaultwarden: Add email support. 2024-03-16 13:50:04 +01:00
03cf2817a4
Add Stalwart mailserver. 2024-03-16 13:49:47 +01:00
13 changed files with 221 additions and 9 deletions

View file

@ -3,14 +3,29 @@ This repository contains all the files I use to manage services hosted on [viyur
## Requirements ## Requirements
Ansible: ### Ansible
Install Ansible:
``` ```
sudo apt install -y ansible sudo apt install -y ansible
``` ```
Setup SSL certificates with Certbot beforehand: ### SSL certificates
Install Certbot:
``` ```
sudo apt install -y certbot python3-certbot-dns-ovh sudo apt install -y certbot python3-certbot-dns-ovh python3-certbot-nginx
```
Request certificates:
```
# For the NGINX reverse proxy
sudo certbot certonly --nginx -d viyurz.fr,*.viyurz.fr
# For Coturn
bash <(wget -q -O - https://github.com/zerossl/zerossl-bot/raw/master/get-zerosslbot.sh)
sudo zerossl-bot certonly --nginx -m viyurz@viyurz.fr -d turn.viyurz.fr
# For the mailserver
sudo certbot certonly --nginx -d mail.viyurz.fr
``` ```
@ -30,3 +45,30 @@ After that, you can create a root cronjob to run this playbook without requiring
``` ```
Here we leave `selected_projects` empty to backup all projects. Here we leave `selected_projects` empty to backup all projects.
## Mailserver
When starting the container for the first time, run the initial setup:
```
docker exec -it mailserver /bin/sh /usr/local/bin/configure.sh
```
After that you need to tell Stalwart where the SSL certificate files are in:
```
/opt/stalwart-mail/etc/common/tls.toml
[certificate."default"]
cert = "file:///etc/fullchain.pem"
private-key = "file:///etc/privkey.pem"
```
And configure the user Stalwart will run as:
```
/opt/stalwart-mail/etc/common/server.toml
[server.run-as]
user = "mail"
group = "mail"
```
Then follow the end of the [Official Installation Guide](https://stalw.art/docs/install/docker#take-note-of-the-administrator-password).

10
env.yml
View file

@ -42,6 +42,7 @@ projects:
- etebase - etebase
- hedgedoc - hedgedoc
- homepage - homepage
- mailserver
- reverse-proxy - reverse-proxy
- searxng - searxng
- synapse - synapse
@ -53,6 +54,7 @@ projects:
projects_to_backup: projects_to_backup:
- etebase - etebase
- hedgedoc - hedgedoc
- mailserver
- synapse - synapse
- uptime-kuma - uptime-kuma
- vaultwarden - vaultwarden
@ -79,6 +81,10 @@ ports:
etebase: 3735 etebase: 3735
hedgedoc: 8086 hedgedoc: 8086
homepage: 8082 homepage: 8082
mailserver_smtp: 1025
mailserver_smtps: 1465
mailserver_imaps: 1993
mailserver_jmap: 1443
searxng: 8083 searxng: 8083
synapse: 8008 synapse: 8008
syncthing_discosrv: 8443 syncthing_discosrv: 8443
@ -98,6 +104,7 @@ users:
hedgedoc: 1004 hedgedoc: 1004
hedgedoc_mysql: 1005 hedgedoc_mysql: 1005
homepage: 8686 homepage: 8686
mailserver: 8
searxng: 977 searxng: 977
searxng_redis: 999 searxng_redis: 999
synapse: 991 synapse: 991
@ -115,6 +122,9 @@ volumes:
etebase_datadir: /mnt/etebasedata etebase_datadir: /mnt/etebasedata
hedgedoc_mysql_datadir: /mnt/hedgedoc/mysql-data hedgedoc_mysql_datadir: /mnt/hedgedoc/mysql-data
hedgedoc_configdir: /mnt/hedgedoc/config hedgedoc_configdir: /mnt/hedgedoc/config
mailserver_datadir: /mnt/mailserverdata
mailserver_tls_certificate_file: "/etc/letsencrypt/live/mail.{{ domain }}/fullchain.pem"
mailserver_tls_certificate_key_file: "/etc/letsencrypt/live/mail.{{ domain }}/privkey.pem"
synapse_datadir: /mnt/synapsedata synapse_datadir: /mnt/synapsedata
synapse_postgres_datadir: /mnt/synapsepgdata synapse_postgres_datadir: /mnt/synapsepgdata
syncthing_datadir: "{{ cifs_mounts['syncthing']['path'] }}" syncthing_datadir: "{{ cifs_mounts['syncthing']['path'] }}"

View file

@ -0,0 +1,22 @@
- name:
become: true
block:
- name: Create borg backup
command:
cmd: |
borg create
--compression=lzma
"{{ borg_repodir }}::{{ role_name }}-{now:%Y-%m-%d_%H-%M-%S}"
{{ volumes['mailserver_datadir'] }}
environment:
BORG_PASSCOMMAND: "cat {{ borg_passphrase_file }}"
- name: Prune borg repository
command:
cmd: |
borg prune
--glob-archives='{{ role_name }}-*'
{{ borg_prune_options }}
{{ borg_repodir }}
environment:
BORG_PASSCOMMAND: "cat {{ borg_passphrase_file }}"

View file

@ -0,0 +1,9 @@
- name: Include backup tasks
include_tasks:
file: backup.yml
when: run_backup | default(false) | bool
- name: Include update tasks
include_tasks:
file: update.yml
when: run_update | default(false) | bool

View file

@ -0,0 +1,78 @@
- name: "Create {{ mailserver_project_dir }} project directory"
file:
path: "{{ mailserver_project_dir }}"
state: directory
- name: Template docker-compose.yaml to project directory
template:
src: docker-compose.yaml
dest: "{{ mailserver_project_dir }}/docker-compose.yaml"
owner: "{{ ansible_env['USER'] }}"
group: "{{ ansible_env['USER'] }}"
mode: '640'
- name: "Create directory {{ volumes['mailserver_datadir'] }} with correct permissions"
file:
path: "{{ volumes['mailserver_datadir'] }}"
state: directory
owner: "{{ users['mailserver'] + uid_shift }}"
group: "{{ users['mailserver'] + uid_shift }}"
mode: '770'
become: true
- name: Set limited permissions on certificate directories
file:
path: "/etc/{{ item }}"
state: directory
owner: root
group: root
mode: '751'
become: true
loop:
- letsencrypt
- letsencrypt/live
- letsencrypt/archive
- name: Set limited permissions on certificate directories
file:
path: "/etc/letsencrypt/{{ item }}/mail.{{ domain }}"
state: directory
owner: root
group: "{{ host_uid }}"
mode: '550'
become: true
loop:
- live
- archive
- name: Set limited permissions on certificate key file
file:
path: "/etc/letsencrypt/live/mail.{{ domain }}/privkey.pem"
owner: root
group: "{{ host_uid }}"
mode: '640'
become: true
- name: Pull project services
community.docker.docker_compose:
project_src: "{{ mailserver_project_dir }}"
recreate: never
pull: true
debug: true
when: docker_pull_images | bool
register: mailserver_docker_compose_pull_result
- name: Display pulled image(s) name
set_fact:
mailserver_pulled_images: "{{ mailserver_pulled_images | default([]) + [item.pulled_image.name] }}"
loop: "{{ mailserver_docker_compose_pull_result['actions'] | default([]) | selectattr('pulled_image', 'defined') }}"
- name: Include backup tasks
include_tasks:
file: backup.yml
# Make a backup if we didn't already make one and we pulled a new image
when: not run_backup and mailserver_pulled_images is defined
- name: Create/Restart project services
community.docker.docker_compose:
project_src: "{{ mailserver_project_dir }}"

View file

@ -0,0 +1,14 @@
services:
mailserver:
image: docker.io/stalwartlabs/mail-server:latest
container_name: mailserver
restart: always
ports:
- "{{ ports['mailserver_smtp'] }}:25"
- {{ ports['mailserver_smtps'] }}:465
- {{ ports['mailserver_imaps'] }}:993
- {{ ports['mailserver_jmap'] }}:443
volumes:
- {{ volumes['mailserver_tls_certificate_file'] }}:/etc/fullchain.pem
- {{ volumes['mailserver_tls_certificate_key_file'] }}:/etc/privkey.pem
- {{ volumes['mailserver_datadir' ] }}:/opt/stalwart-mail

View file

@ -0,0 +1 @@
mailserver_project_dir: "{{ docker_projects_dir }}/{{ role_name }}"

View file

@ -2,11 +2,13 @@
flush ruleset flush ruleset
# Forward Syncthing relay traffic from port {{ ports['syncthing_relaysrv'] }} to 22067
table inet nat { table inet nat {
chain prerouting { chain prerouting {
type nat hook prerouting priority dstnat; type nat hook prerouting priority dstnat;
iif eth0 tcp dport {{ ports['syncthing_relaysrv'] }} redirect to :22067 iif eth0 tcp dport {{ ports['syncthing_relaysrv'] }} redirect to :22067
iif eth0 tcp dport 25 redirect to :{{ ports['mailserver_smtp'] }}
iif eth0 tcp dport 465 redirect to :{{ ports['mailserver_smtps'] }}
iif eth0 tcp dport 993 redirect to :{{ ports['mailserver_imaps'] }}
} }
} }
@ -57,7 +59,10 @@ table inet filter {
tcp dport { http, https } accept tcp dport { http, https } accept
# SSH # SSH
tcp dport 995 accept tcp dport ssh accept
# SMTP/IMAP
tcp dport { {{ ports['mailserver_smtp'] }}, {{ ports['mailserver_smtps'] }}, {{ ports['mailserver_imaps'] }} } accept
# Syncthing # Syncthing
tcp dport { {{ ports['syncthing_tcp'] }}, 22067 } accept tcp dport { {{ ports['syncthing_tcp'] }}, 22067 } accept

View file

@ -113,6 +113,24 @@ server {
} }
# JMAP
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name mail.{{ domain }};
location / {
proxy_pass https://127.0.0.1:{{ ports['mailserver_jmap'] }};
# Websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
# SearXNG # SearXNG
server { server {
listen 443 ssl http2; listen 443 ssl http2;

View file

@ -3,13 +3,16 @@
path: "{{ vaultwarden_project_dir }}" path: "{{ vaultwarden_project_dir }}"
state: directory state: directory
- name: Template docker-compose.yaml to project directory - name: Template docker-compose.yaml & .env to project directory
template: template:
src: docker-compose.yaml src: "{{ item }}"
dest: "{{ vaultwarden_project_dir }}/docker-compose.yaml" dest: "{{ vaultwarden_project_dir }}/{{ item }}"
owner: "{{ ansible_env['USER'] }}" owner: "{{ ansible_env['USER'] }}"
group: "{{ ansible_env['USER'] }}" group: "{{ ansible_env['USER'] }}"
mode: '640' mode: '640'
loop:
- docker-compose.yaml
- .env
- name: "Create directory {{ volumes['vaultwarden_datadir'] }} with correct permissions" - name: "Create directory {{ volumes['vaultwarden_datadir'] }} with correct permissions"
file: file:

View file

@ -0,0 +1,2 @@
ADMIN_TOKEN='{{ vaultwarden_secrets["admin_token_hash"] }}'
SMTP_PASSWORD='{{ vaultwarden_secrets["smtp_password"] }}'

View file

@ -7,7 +7,13 @@ services:
environment: environment:
- DOMAIN=https://vw.{{ domain }} - DOMAIN=https://vw.{{ domain }}
- SIGNUPS_ALLOWED=false - SIGNUPS_ALLOWED=false
- ADMIN_TOKEN={{ vaultwarden_secrets['admin_token_hash'] | regex_replace('\$', '$$') }} - ADMIN_TOKEN=${ADMIN_TOKEN}
- SMTP_HOST=mail.{{ domain }}
- SMTP_FROM=vaultwarden@{{ domain }}
- SMTP_PORT={{ ports['mailserver_smtps'] }}
- SMTP_SECURITY=force_tls
- SMTP_USERNAME={{ vaultwarden_secrets['smtp_username'] }}
- SMTP_PASSWORD=${SMTP_PASSWORD}
ports: ports:
- 127.0.0.1:{{ ports['vaultwarden'] }}:80 - 127.0.0.1:{{ ports['vaultwarden'] }}:80
volumes: volumes:

View file

@ -25,3 +25,5 @@ synapse_secrets:
vaultwarden_secrets: vaultwarden_secrets:
# Generate with: docker exec --rm -ti docker.io/vaultwarden/server:alpine /vaultwarden hash # Generate with: docker exec --rm -ti docker.io/vaultwarden/server:alpine /vaultwarden hash
admin_token_hash: admin_token_hash:
smtp_username:
smtp_password: