mirror of
https://github.com/rubenhensen/k8scd.git
synced 2026-09-17 02:12:55 +02:00
mailserver: add broken sogo
This commit is contained in:
@@ -89,7 +89,7 @@ From the web UI you can manage domains, accounts, DKIM keys, and other mail sett
|
||||
|
||||
## TLS certificates
|
||||
|
||||
Handled automatically by Stalwart's built-in ACME support (Let's Encrypt, `tls-alpn-01` challenge on port 443). No certbot needed. Certificates auto-renew.
|
||||
Managed by certbot (Let's Encrypt). On first run, certbot uses standalone mode (temporarily stops Stalwart to bind port 80/443). Renewals happen automatically via `certbot.timer` and a deploy hook restarts Stalwart. Certs are at `/etc/letsencrypt/live/mail.rubenhensen.nl/`.
|
||||
|
||||
## Ports
|
||||
|
||||
@@ -101,7 +101,7 @@ Handled automatically by Stalwart's built-in ACME support (Let's Encrypt, `tls-a
|
||||
| 587 | SMTP submission (STARTTLS) |
|
||||
| 993 | IMAP (implicit TLS) |
|
||||
| 443 | HTTPS (web admin + JMAP + ACME) |
|
||||
| 8080 | HTTP |
|
||||
| 80 | HTTP (ACME + redirect) |
|
||||
|
||||
## If something breaks
|
||||
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
---
|
||||
stalwart_fallback_admin_password: "{{ vault_stalwart_fallback_admin_password }}"
|
||||
stalwart_db_password: "{{ vault_stalwart_db_password }}"
|
||||
sogo_db_password: "{{ vault_sogo_db_password }}"
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
31346664666161363261666662653164623562376161313865323065636664643331303234633263
|
||||
6164323366303938346231636134323762323634343731340a313966306130333832363764323633
|
||||
61326261613839303765303239653439343563333031643030363764653735663337356631613331
|
||||
3162643836623463370a323834393931373434373636366266376639366561336333653165343166
|
||||
66623162343032663034613663373133663563343463613935306366363461616636646630663961
|
||||
37376132666531323230383530633430353762346237343035393065656230306430376334333937
|
||||
64383632366333316337323036303532343838343035653631396165313939303465653730303865
|
||||
35393935636361633135343438666263633165386435656361613136313039303235303134333031
|
||||
6433
|
||||
37373639633565366339643336366137666465303365363532393364626333306232636661373331
|
||||
6239343934326134386663396366333232613337323266320a303836623065633534653034666661
|
||||
33643731356161656563313330626166613562356434313961313539643636373665373635613635
|
||||
3631636639393563330a666530353733623166313063373439306234613938353366303331336234
|
||||
37616266363434336639373535303038666231656461373061376666636464393233323963366234
|
||||
65383062666162343561353961373230323763323432646364663437313361633331313433613465
|
||||
62663034623330333936623035353235393461333363303435613364643838373164313431636562
|
||||
30323736326632663032616136643430306330366333353565646432343466386564303432616339
|
||||
32303231363835666537386136376532346632343961396334663130376366373566313965383163
|
||||
34396566363830666437303266623437383064396462643132336332643239326364313632386164
|
||||
38363561643038363065313630636437366636623134666564613937613633643835656236623331
|
||||
32393366643433613230646361316539633236353539316162336237373737316136373466663863
|
||||
36646162323266336436343631383837363338336234653063626565653931633862616662666265
|
||||
34396430313132333536363736333434666634333432336639633963393665616163653237343235
|
||||
36626138356230313963383033643432313839333232386265633330666664666262343433396362
|
||||
66646666356563303136636337323333306466373036313333643135643436393165656539396139
|
||||
3861
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
become: true
|
||||
|
||||
roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- fail2ban
|
||||
- stalwart
|
||||
- { role: base, tags: [base] }
|
||||
- { role: ssh, tags: [ssh] }
|
||||
- { role: ufw, tags: [ufw] }
|
||||
- { role: fail2ban, tags: [fail2ban] }
|
||||
- { role: certbot, tags: [certbot] }
|
||||
- { role: stalwart, tags: [stalwart] }
|
||||
- { role: sogo, tags: [sogo] }
|
||||
- { role: nginx, tags: [nginx] }
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
certbot_email: "admin@rubenhensen.nl"
|
||||
certbot_domains:
|
||||
- "mail.rubenhensen.nl"
|
||||
- "stalwart.rubenhensen.nl"
|
||||
certbot_webroot: "/var/www/acme"
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
- name: Install certbot
|
||||
ansible.builtin.apt:
|
||||
name: certbot
|
||||
state: present
|
||||
|
||||
- name: Check if certificate already exists
|
||||
ansible.builtin.stat:
|
||||
path: "/etc/letsencrypt/live/{{ certbot_domains[0] }}/fullchain.pem"
|
||||
register: certbot_cert
|
||||
|
||||
- name: Stop Stalwart for initial cert issuance
|
||||
ansible.builtin.service:
|
||||
name: stalwart
|
||||
state: stopped
|
||||
when: not certbot_cert.stat.exists
|
||||
|
||||
- name: Obtain certificate (standalone)
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
certbot certonly --standalone
|
||||
--non-interactive
|
||||
--agree-tos
|
||||
--email {{ certbot_email }}
|
||||
{% for domain in certbot_domains %}-d {{ domain }} {% endfor %}
|
||||
when: not certbot_cert.stat.exists
|
||||
changed_when: true
|
||||
|
||||
- name: Start Stalwart after cert issuance
|
||||
ansible.builtin.service:
|
||||
name: stalwart
|
||||
state: started
|
||||
when: not certbot_cert.stat.exists
|
||||
|
||||
- name: Grant stalwart read access to certs
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop:
|
||||
- /etc/letsencrypt/live
|
||||
- /etc/letsencrypt/archive
|
||||
|
||||
- name: Deploy Stalwart admin password for renewal hook
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/letsencrypt/.stalwart-admin-pass
|
||||
content: "{{ stalwart_fallback_admin_password }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
|
||||
- name: Deploy certbot renewal hook
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/letsencrypt/renewal-hooks/deploy/stalwart.sh
|
||||
content: |
|
||||
#!/bin/bash
|
||||
DOMAIN="{{ certbot_domains[0] }}"
|
||||
PASS=$(cat /etc/letsencrypt/.stalwart-admin-pass)
|
||||
|
||||
python3 -c "
|
||||
import json, urllib.request
|
||||
|
||||
cert = open('/etc/letsencrypt/live/$DOMAIN/fullchain.pem').read()
|
||||
key = open('/etc/letsencrypt/live/$DOMAIN/privkey.pem').read()
|
||||
body = json.dumps([{'type':'insert','assert_empty':False,'values':[
|
||||
['certificate.default.cert', cert],
|
||||
['certificate.default.private-key', key],
|
||||
]}]).encode()
|
||||
|
||||
import base64
|
||||
auth = base64.b64encode(('admin:' + open('/etc/letsencrypt/.stalwart-admin-pass').read().strip()).encode()).decode()
|
||||
|
||||
req = urllib.request.Request('http://127.0.0.1:8080/api/settings', data=body,
|
||||
headers={'Content-Type':'application/json','Authorization':'Basic ' + auth}, method='POST')
|
||||
urllib.request.urlopen(req)
|
||||
|
||||
req = urllib.request.Request('http://127.0.0.1:8080/api/reload',
|
||||
headers={'Authorization':'Basic ' + auth})
|
||||
urllib.request.urlopen(req)
|
||||
"
|
||||
mode: "0755"
|
||||
|
||||
- name: Ensure certbot renewal timer is enabled
|
||||
ansible.builtin.systemd:
|
||||
name: certbot.timer
|
||||
enabled: true
|
||||
state: started
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
nginx_server_name: "mail.rubenhensen.nl"
|
||||
nginx_stalwart_server_name: "stalwart.rubenhensen.nl"
|
||||
nginx_tls_cert: "/etc/letsencrypt/live/mail.rubenhensen.nl/fullchain.pem"
|
||||
nginx_tls_key: "/etc/letsencrypt/live/mail.rubenhensen.nl/privkey.pem"
|
||||
nginx_stalwart_backend: "127.0.0.1:8443"
|
||||
nginx_sogo_backend: "127.0.0.1:20000"
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: reload nginx
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
- name: Install Nginx
|
||||
ansible.builtin.apt:
|
||||
name: nginx
|
||||
state: present
|
||||
|
||||
- name: Remove default site
|
||||
ansible.builtin.file:
|
||||
path: /etc/nginx/sites-enabled/default
|
||||
state: absent
|
||||
notify: reload nginx
|
||||
|
||||
- name: Deploy mail site config
|
||||
ansible.builtin.template:
|
||||
src: mail.conf.j2
|
||||
dest: /etc/nginx/sites-available/mail.conf
|
||||
mode: "0644"
|
||||
notify: reload nginx
|
||||
|
||||
- name: Enable mail site
|
||||
ansible.builtin.file:
|
||||
src: /etc/nginx/sites-available/mail.conf
|
||||
dest: /etc/nginx/sites-enabled/mail.conf
|
||||
state: link
|
||||
notify: reload nginx
|
||||
|
||||
- name: Enable and start Nginx
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
enabled: true
|
||||
state: started
|
||||
@@ -0,0 +1,113 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name {{ nginx_server_name }};
|
||||
|
||||
# ACME challenge
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/acme;
|
||||
}
|
||||
|
||||
# Redirect everything else to HTTPS
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name {{ nginx_server_name }};
|
||||
|
||||
ssl_certificate {{ nginx_tls_cert }};
|
||||
ssl_certificate_key {{ nginx_tls_key }};
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
# SOGo
|
||||
location ^~ /SOGo {
|
||||
proxy_pass http://{{ nginx_sogo_backend }};
|
||||
proxy_redirect http://{{ nginx_sogo_backend }} /;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
|
||||
proxy_set_header x-webobjects-remote-host $remote_addr;
|
||||
proxy_set_header x-webobjects-server-name $server_name;
|
||||
proxy_set_header x-webobjects-server-url $scheme://$host;
|
||||
proxy_set_header x-webobjects-server-port $server_port;
|
||||
proxy_buffer_size 128k;
|
||||
proxy_buffers 64 512k;
|
||||
proxy_busy_buffers_size 512k;
|
||||
client_max_body_size 100m;
|
||||
}
|
||||
|
||||
# SOGo static resources
|
||||
location ^~ /SOGo.woa/WebServerResources/ {
|
||||
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
}
|
||||
|
||||
location /SOGo/WebServerResources/ {
|
||||
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
}
|
||||
|
||||
# Microsoft AutoDiscover / ActiveSync
|
||||
location ^~ /Microsoft-Server-ActiveSync {
|
||||
proxy_pass http://{{ nginx_sogo_backend }}/SOGo/Microsoft-Server-ActiveSync;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_connect_timeout 360;
|
||||
proxy_send_timeout 360;
|
||||
proxy_read_timeout 360;
|
||||
}
|
||||
|
||||
# CalDAV/CardDAV well-known
|
||||
location /.well-known/caldav {
|
||||
return 301 https://$host/SOGo/dav;
|
||||
}
|
||||
location /.well-known/carddav {
|
||||
return 301 https://$host/SOGo/dav;
|
||||
}
|
||||
|
||||
# Default: SOGo
|
||||
location / {
|
||||
return 301 https://$host/SOGo;
|
||||
}
|
||||
}
|
||||
|
||||
# Stalwart web admin
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name {{ nginx_stalwart_server_name }};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/acme;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name {{ nginx_stalwart_server_name }};
|
||||
|
||||
ssl_certificate {{ nginx_tls_cert }};
|
||||
ssl_certificate_key {{ nginx_tls_key }};
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
location / {
|
||||
proxy_pass https://{{ nginx_stalwart_backend }};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
sogo_db_name: "sogo"
|
||||
sogo_db_user: "sogo"
|
||||
sogo_db_password: "{{ vault_sogo_db_password }}"
|
||||
|
||||
sogo_imap_server: "127.0.0.1"
|
||||
sogo_smtp_server: "127.0.0.1"
|
||||
sogo_mail_domain: "rubenhensen.nl"
|
||||
|
||||
sogo_time_zone: "Europe/Amsterdam"
|
||||
sogo_page_title: "Mail - rubenhensen.nl"
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: restart sogo
|
||||
ansible.builtin.systemd:
|
||||
name: sogo
|
||||
state: restarted
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
- name: Install PostgreSQL
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- postgresql
|
||||
- postgresql-client
|
||||
- python3-psycopg2
|
||||
state: present
|
||||
|
||||
- name: Ensure PostgreSQL is running
|
||||
ansible.builtin.systemd:
|
||||
name: postgresql
|
||||
enabled: true
|
||||
state: started
|
||||
|
||||
- name: Create SOGo database user
|
||||
become: true
|
||||
become_user: postgres
|
||||
community.postgresql.postgresql_user:
|
||||
name: "{{ sogo_db_user }}"
|
||||
password: "{{ sogo_db_password }}"
|
||||
|
||||
- name: Create SOGo database
|
||||
become: true
|
||||
become_user: postgres
|
||||
community.postgresql.postgresql_db:
|
||||
name: "{{ sogo_db_name }}"
|
||||
owner: "{{ sogo_db_user }}"
|
||||
|
||||
- name: Grant SOGo user read access to Stalwart accounts
|
||||
become: true
|
||||
become_user: postgres
|
||||
community.postgresql.postgresql_privs:
|
||||
database: "stalwart"
|
||||
roles: "{{ sogo_db_user }}"
|
||||
type: table
|
||||
objs: accounts,emails
|
||||
privs: SELECT
|
||||
|
||||
- name: Download SOGo repository key
|
||||
ansible.builtin.shell:
|
||||
cmd: >
|
||||
wget -qO- "https://keys.openpgp.org/vks/v1/by-fingerprint/74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9"
|
||||
| gpg --dearmor > /etc/apt/trusted.gpg.d/sogo.gpg
|
||||
creates: /etc/apt/trusted.gpg.d/sogo.gpg
|
||||
|
||||
- name: Add SOGo nightly repository
|
||||
ansible.builtin.apt_repository:
|
||||
repo: "deb https://packages.sogo.nu/nightly/5/ubuntu/ {{ ansible_facts['distribution_release'] }} {{ ansible_facts['distribution_release'] }}"
|
||||
filename: sogo
|
||||
state: present
|
||||
|
||||
- name: Install SOGo and dependencies
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- sogo
|
||||
- sogo-activesync
|
||||
- sope4.9-gdl1-postgresql
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Disable Apache (installed as SOGo dependency, Nginx is used instead)
|
||||
ansible.builtin.systemd:
|
||||
name: apache2
|
||||
enabled: false
|
||||
state: stopped
|
||||
failed_when: false
|
||||
|
||||
- name: Deploy SOGo configuration
|
||||
ansible.builtin.template:
|
||||
src: sogo.conf.j2
|
||||
dest: /etc/sogo/sogo.conf
|
||||
owner: sogo
|
||||
group: sogo
|
||||
mode: "0640"
|
||||
notify: restart sogo
|
||||
|
||||
- name: Enable and start SOGo
|
||||
ansible.builtin.systemd:
|
||||
name: sogo
|
||||
enabled: true
|
||||
state: started
|
||||
|
||||
- name: Enable and start memcached
|
||||
ansible.builtin.systemd:
|
||||
name: memcached
|
||||
enabled: true
|
||||
state: started
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
SOGoTimeZone = "{{ sogo_time_zone }}";
|
||||
SOGoPageTitle = "{{ sogo_page_title }}";
|
||||
SOGoLanguage = "English";
|
||||
|
||||
SOGoMailDomain = "{{ sogo_mail_domain }}";
|
||||
SOGoIMAPServer = "imaps://{{ sogo_imap_server }}:993/?tls=YES&tlsVerifyMode=none";
|
||||
SOGoSMTPServer = "smtp://{{ sogo_smtp_server }}:587/?tls=YES&tlsVerifyMode=none";
|
||||
SOGoSMTPAuthenticationType = "PLAIN";
|
||||
SOGoIMAPAclConformsToIMAPExt = YES;
|
||||
SOGoForceExternalLoginWithEmail = YES;
|
||||
|
||||
SOGoUserSources = (
|
||||
{
|
||||
type = sql;
|
||||
id = directory;
|
||||
viewURL = "postgresql://{{ sogo_db_user }}:{{ sogo_db_password }}@127.0.0.1:5432/stalwart/sogo_users";
|
||||
canAuthenticate = YES;
|
||||
isAddressBook = NO;
|
||||
LoginFieldNames = ("mail");
|
||||
userPasswordAlgorithm = "sha512-crypt";
|
||||
}
|
||||
);
|
||||
|
||||
SOGoProfileURL = "postgresql://{{ sogo_db_user }}:{{ sogo_db_password }}@127.0.0.1:5432/{{ sogo_db_name }}/sogo_user_profile";
|
||||
OCSFolderInfoURL = "postgresql://{{ sogo_db_user }}:{{ sogo_db_password }}@127.0.0.1:5432/{{ sogo_db_name }}/sogo_folder_info";
|
||||
OCSSessionsFolderURL = "postgresql://{{ sogo_db_user }}:{{ sogo_db_password }}@127.0.0.1:5432/{{ sogo_db_name }}/sogo_sessions_folder";
|
||||
OCSEMailAlarmsFolderURL = "postgresql://{{ sogo_db_user }}:{{ sogo_db_password }}@127.0.0.1:5432/{{ sogo_db_name }}/sogo_alarms_folder";
|
||||
OCSStoreURL = "postgresql://{{ sogo_db_user }}:{{ sogo_db_password }}@127.0.0.1:5432/{{ sogo_db_name }}/sogo_store";
|
||||
OCSAclURL = "postgresql://{{ sogo_db_user }}:{{ sogo_db_password }}@127.0.0.1:5432/{{ sogo_db_name }}/sogo_acl";
|
||||
OCSCacheFolderURL = "postgresql://{{ sogo_db_user }}:{{ sogo_db_password }}@127.0.0.1:5432/{{ sogo_db_name }}/sogo_cache_folder";
|
||||
|
||||
SOGoAppointmentSendEMailNotifications = YES;
|
||||
SOGoEnableEMailAlarms = YES;
|
||||
|
||||
SOGoMemcachedHost = "127.0.0.1";
|
||||
|
||||
SOGoCalendarDefaultRoles = (
|
||||
PublicViewer,
|
||||
ConfidentialDAccessor
|
||||
);
|
||||
|
||||
SOGoSuperUsernames = ("admin");
|
||||
SOGoFirstDayOfWeek = 1;
|
||||
}
|
||||
@@ -39,12 +39,12 @@ stalwart_server_listeners:
|
||||
options:
|
||||
tls.implicit: true
|
||||
- name: "https"
|
||||
bind: "[::]:443"
|
||||
bind: "127.0.0.1:8443"
|
||||
protocol: "http"
|
||||
options:
|
||||
tls.implicit: true
|
||||
- name: "http"
|
||||
bind: "[::]:8080"
|
||||
bind: "127.0.0.1:8080"
|
||||
protocol: "http"
|
||||
|
||||
# Stores
|
||||
@@ -54,11 +54,24 @@ stalwart_stores:
|
||||
options:
|
||||
path: "{{ stalwart_data_path }}"
|
||||
compression: "lz4"
|
||||
- name: "postgresql"
|
||||
type: "postgresql"
|
||||
options:
|
||||
host: "127.0.0.1"
|
||||
port: 5432
|
||||
database: "{{ stalwart_db_name }}"
|
||||
user: "{{ stalwart_db_user }}"
|
||||
password: "{{ stalwart_db_password }}"
|
||||
|
||||
# Directory
|
||||
stalwart_directory_type: "internal"
|
||||
stalwart_directory_type: "sql"
|
||||
stalwart_directory_options:
|
||||
store: "rocksdb"
|
||||
store: "postgresql"
|
||||
|
||||
# Database
|
||||
stalwart_db_name: "stalwart"
|
||||
stalwart_db_user: "stalwart"
|
||||
stalwart_db_password: "{{ vault_stalwart_db_password }}"
|
||||
|
||||
# Tracers
|
||||
stalwart_tracers:
|
||||
@@ -76,12 +89,9 @@ stalwart_tracers:
|
||||
ansi: false
|
||||
enable: true
|
||||
|
||||
# ACME / Let's Encrypt
|
||||
stalwart_acme_enabled: true
|
||||
stalwart_acme_directory: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
stalwart_acme_contact: "mailto:admin@rubenhensen.nl"
|
||||
stalwart_acme_domains:
|
||||
- "mail.rubenhensen.nl"
|
||||
# TLS certificate (managed by certbot)
|
||||
stalwart_tls_cert: "/etc/letsencrypt/live/{{ stalwart_server_hostname }}/fullchain.pem"
|
||||
stalwart_tls_key: "/etc/letsencrypt/live/{{ stalwart_server_hostname }}/privkey.pem"
|
||||
|
||||
# Extra config sections (list of {name, options} dicts)
|
||||
stalwart_additional_configs: []
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
name VARCHAR(255) PRIMARY KEY,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
description VARCHAR(255) DEFAULT '',
|
||||
type VARCHAR(20) NOT NULL DEFAULT 'individual',
|
||||
email VARCHAR(255) NOT NULL,
|
||||
quota INTEGER DEFAULT 0,
|
||||
active BOOLEAN DEFAULT true
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS group_members (
|
||||
name VARCHAR(255) NOT NULL,
|
||||
member_of VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (name, member_of)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS emails (
|
||||
name VARCHAR(255) NOT NULL,
|
||||
address VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(20) NOT NULL DEFAULT 'primary',
|
||||
PRIMARY KEY (name, address)
|
||||
);
|
||||
|
||||
-- View for SOGo (expects c_ prefixed columns)
|
||||
CREATE OR REPLACE VIEW sogo_users AS
|
||||
SELECT
|
||||
name AS c_uid,
|
||||
name AS c_name,
|
||||
password AS c_password,
|
||||
description AS c_cn,
|
||||
email AS mail
|
||||
FROM accounts
|
||||
WHERE active = true;
|
||||
@@ -20,3 +20,54 @@
|
||||
name: "{{ stalwart_service_name }}"
|
||||
state: "{{ stalwart_service_state }}"
|
||||
enabled: "{{ stalwart_service_enabled }}"
|
||||
|
||||
- name: Flush handlers to ensure Stalwart is running
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Wait for Stalwart API
|
||||
ansible.builtin.wait_for:
|
||||
port: 8080
|
||||
host: 127.0.0.1
|
||||
delay: 2
|
||||
timeout: 30
|
||||
|
||||
- name: Read TLS certificate
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ stalwart_tls_cert }}"
|
||||
register: tls_cert_content
|
||||
|
||||
- name: Read TLS private key
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ stalwart_tls_key }}"
|
||||
register: tls_key_content
|
||||
|
||||
- name: Push TLS certificate to Stalwart
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:8080/api/settings"
|
||||
method: POST
|
||||
user: "{{ stalwart_fallback_admin_login }}"
|
||||
password: "{{ stalwart_fallback_admin_password }}"
|
||||
force_basic_auth: true
|
||||
body_format: json
|
||||
body:
|
||||
- type: "insert"
|
||||
assert_empty: false
|
||||
values:
|
||||
- - "certificate.default.cert"
|
||||
- "{{ tls_cert_content.content | b64decode }}"
|
||||
- - "certificate.default.private-key"
|
||||
- "{{ tls_key_content.content | b64decode }}"
|
||||
- - "certificate.default.default"
|
||||
- "true"
|
||||
- - "server.tls.certificate"
|
||||
- "default"
|
||||
status_code: [200, 204]
|
||||
|
||||
- name: Reload Stalwart to apply certificate
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:8080/api/reload"
|
||||
method: GET
|
||||
user: "{{ stalwart_fallback_admin_login }}"
|
||||
password: "{{ stalwart_fallback_admin_password }}"
|
||||
force_basic_auth: true
|
||||
status_code: [200, 204]
|
||||
|
||||
@@ -21,6 +21,37 @@
|
||||
group: "{{ stalwart_system_group }}"
|
||||
mode: "0755"
|
||||
|
||||
- name: Create Stalwart database user
|
||||
become: true
|
||||
become_user: postgres
|
||||
community.postgresql.postgresql_user:
|
||||
name: "{{ stalwart_db_user }}"
|
||||
password: "{{ stalwart_db_password }}"
|
||||
when: stalwart_directory_type == "sql"
|
||||
|
||||
- name: Create Stalwart database
|
||||
become: true
|
||||
become_user: postgres
|
||||
community.postgresql.postgresql_db:
|
||||
name: "{{ stalwart_db_name }}"
|
||||
owner: "{{ stalwart_db_user }}"
|
||||
when: stalwart_directory_type == "sql"
|
||||
|
||||
- name: Deploy database schema
|
||||
ansible.builtin.copy:
|
||||
src: schema.sql
|
||||
dest: /tmp/stalwart-schema.sql
|
||||
mode: "0644"
|
||||
when: stalwart_directory_type == "sql"
|
||||
|
||||
- name: Apply database schema
|
||||
become: true
|
||||
become_user: postgres
|
||||
ansible.builtin.command:
|
||||
cmd: psql -d {{ stalwart_db_name }} -f /tmp/stalwart-schema.sql
|
||||
changed_when: false
|
||||
when: stalwart_directory_type == "sql"
|
||||
|
||||
- name: Check if already installed
|
||||
ansible.builtin.stat:
|
||||
path: "{{ stalwart_executable_path }}"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
[server]
|
||||
hostname = "{{ stalwart_server_hostname }}"
|
||||
max-connections = {{ stalwart_server_max_connections }}
|
||||
{% if stalwart_acme_enabled %}
|
||||
tls.certificate = "acme"
|
||||
{% endif %}
|
||||
|
||||
{% for listener in stalwart_server_listeners %}
|
||||
[server.listener.{{ listener.name }}]
|
||||
@@ -19,19 +16,6 @@ protocol = "{{ listener.protocol }}"
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
{% if stalwart_acme_enabled %}
|
||||
[certificate.acme]
|
||||
type = "acme"
|
||||
provider = "letsencrypt"
|
||||
domains = [{% for d in stalwart_acme_domains %}"{{ d }}"{% if not loop.last %}, {% endif %}{% endfor %}]
|
||||
|
||||
[acme.letsencrypt]
|
||||
directory = "{{ stalwart_acme_directory }}"
|
||||
contact = ["{{ stalwart_acme_contact }}"]
|
||||
challenge = "tls-alpn-01"
|
||||
default = true
|
||||
{% endif %}
|
||||
|
||||
[storage]
|
||||
data = "{{ stalwart_storage_data }}"
|
||||
fts = "{{ stalwart_storage_fts }}"
|
||||
@@ -62,6 +46,21 @@ type = "{{ stalwart_directory_type }}"
|
||||
{%- endif %}
|
||||
|
||||
{% endfor %}
|
||||
{% if stalwart_directory_type == "sql" %}
|
||||
columns.secret = "password"
|
||||
columns.description = "description"
|
||||
columns.email = "email"
|
||||
columns.quota = "quota"
|
||||
columns.class = "type"
|
||||
|
||||
[store.postgresql.query]
|
||||
name = "SELECT name, type, password, description, email, quota FROM accounts WHERE name = $1 AND active = true"
|
||||
members = "SELECT member_of FROM group_members WHERE name = $1"
|
||||
recipients = "SELECT name FROM emails WHERE address = $1"
|
||||
emails = "SELECT address FROM emails WHERE name = $1 ORDER BY type DESC, address ASC"
|
||||
secrets = "SELECT password FROM accounts WHERE name = $1 AND active = true"
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% for tracer in stalwart_tracers %}
|
||||
[tracer.{{ tracer.type }}]
|
||||
|
||||
@@ -8,4 +8,4 @@ ufw_allowed_ports:
|
||||
- { port: "587", proto: "tcp", comment: "SMTP submission (STARTTLS)" }
|
||||
- { port: "993", proto: "tcp", comment: "IMAP (implicit TLS)" }
|
||||
- { port: "443", proto: "tcp", comment: "HTTPS (web admin + JMAP)" }
|
||||
- { port: "8080", proto: "tcp", comment: "HTTP (certbot + redirect)" }
|
||||
- { port: "80", proto: "tcp", comment: "HTTP (ACME + redirect)" }
|
||||
|
||||
@@ -61,7 +61,20 @@ spec:
|
||||
secretKeyRef:
|
||||
name: authentik-postgres-credentials
|
||||
key: password
|
||||
- name: VAULT_OIDC_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: vault-oidc-client-secret
|
||||
key: client_secret
|
||||
server:
|
||||
volumes:
|
||||
- name: custom-blueprints
|
||||
configMap:
|
||||
name: authentik-blueprint-vault
|
||||
volumeMounts:
|
||||
- name: custom-blueprints
|
||||
mountPath: /blueprints/custom/vault-oidc.yaml
|
||||
subPath: vault-oidc.yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
@@ -73,6 +86,15 @@ spec:
|
||||
- secretName: authentik-tls
|
||||
hosts:
|
||||
- authentik.rubenhensen.nl
|
||||
worker:
|
||||
volumes:
|
||||
- name: custom-blueprints
|
||||
configMap:
|
||||
name: authentik-blueprint-vault
|
||||
volumeMounts:
|
||||
- name: custom-blueprints
|
||||
mountPath: /blueprints/custom/vault-oidc.yaml
|
||||
subPath: vault-oidc.yaml
|
||||
postgresql:
|
||||
enabled: false
|
||||
redis:
|
||||
|
||||
Reference in New Issue
Block a user