mirror of
https://github.com/rubenhensen/k8scd.git
synced 2026-09-17 18:22:54 +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
|
## 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
|
## Ports
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ Handled automatically by Stalwart's built-in ACME support (Let's Encrypt, `tls-a
|
|||||||
| 587 | SMTP submission (STARTTLS) |
|
| 587 | SMTP submission (STARTTLS) |
|
||||||
| 993 | IMAP (implicit TLS) |
|
| 993 | IMAP (implicit TLS) |
|
||||||
| 443 | HTTPS (web admin + JMAP + ACME) |
|
| 443 | HTTPS (web admin + JMAP + ACME) |
|
||||||
| 8080 | HTTP |
|
| 80 | HTTP (ACME + redirect) |
|
||||||
|
|
||||||
## If something breaks
|
## If something breaks
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
---
|
---
|
||||||
stalwart_fallback_admin_password: "{{ vault_stalwart_fallback_admin_password }}"
|
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
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
31346664666161363261666662653164623562376161313865323065636664643331303234633263
|
37373639633565366339643336366137666465303365363532393364626333306232636661373331
|
||||||
6164323366303938346231636134323762323634343731340a313966306130333832363764323633
|
6239343934326134386663396366333232613337323266320a303836623065633534653034666661
|
||||||
61326261613839303765303239653439343563333031643030363764653735663337356631613331
|
33643731356161656563313330626166613562356434313961313539643636373665373635613635
|
||||||
3162643836623463370a323834393931373434373636366266376639366561336333653165343166
|
3631636639393563330a666530353733623166313063373439306234613938353366303331336234
|
||||||
66623162343032663034613663373133663563343463613935306366363461616636646630663961
|
37616266363434336639373535303038666231656461373061376666636464393233323963366234
|
||||||
37376132666531323230383530633430353762346237343035393065656230306430376334333937
|
65383062666162343561353961373230323763323432646364663437313361633331313433613465
|
||||||
64383632366333316337323036303532343838343035653631396165313939303465653730303865
|
62663034623330333936623035353235393461333363303435613364643838373164313431636562
|
||||||
35393935636361633135343438666263633165386435656361613136313039303235303134333031
|
30323736326632663032616136643430306330366333353565646432343466386564303432616339
|
||||||
6433
|
32303231363835666537386136376532346632343961396334663130376366373566313965383163
|
||||||
|
34396566363830666437303266623437383064396462643132336332643239326364313632386164
|
||||||
|
38363561643038363065313630636437366636623134666564613937613633643835656236623331
|
||||||
|
32393366643433613230646361316539633236353539316162336237373737316136373466663863
|
||||||
|
36646162323266336436343631383837363338336234653063626565653931633862616662666265
|
||||||
|
34396430313132333536363736333434666634333432336639633963393665616163653237343235
|
||||||
|
36626138356230313963383033643432313839333232386265633330666664666262343433396362
|
||||||
|
66646666356563303136636337323333306466373036313333643135643436393165656539396139
|
||||||
|
3861
|
||||||
|
|||||||
@@ -4,8 +4,11 @@
|
|||||||
become: true
|
become: true
|
||||||
|
|
||||||
roles:
|
roles:
|
||||||
- base
|
- { role: base, tags: [base] }
|
||||||
- ssh
|
- { role: ssh, tags: [ssh] }
|
||||||
- ufw
|
- { role: ufw, tags: [ufw] }
|
||||||
- fail2ban
|
- { role: fail2ban, tags: [fail2ban] }
|
||||||
- stalwart
|
- { 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:
|
options:
|
||||||
tls.implicit: true
|
tls.implicit: true
|
||||||
- name: "https"
|
- name: "https"
|
||||||
bind: "[::]:443"
|
bind: "127.0.0.1:8443"
|
||||||
protocol: "http"
|
protocol: "http"
|
||||||
options:
|
options:
|
||||||
tls.implicit: true
|
tls.implicit: true
|
||||||
- name: "http"
|
- name: "http"
|
||||||
bind: "[::]:8080"
|
bind: "127.0.0.1:8080"
|
||||||
protocol: "http"
|
protocol: "http"
|
||||||
|
|
||||||
# Stores
|
# Stores
|
||||||
@@ -54,11 +54,24 @@ stalwart_stores:
|
|||||||
options:
|
options:
|
||||||
path: "{{ stalwart_data_path }}"
|
path: "{{ stalwart_data_path }}"
|
||||||
compression: "lz4"
|
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
|
# Directory
|
||||||
stalwart_directory_type: "internal"
|
stalwart_directory_type: "sql"
|
||||||
stalwart_directory_options:
|
stalwart_directory_options:
|
||||||
store: "rocksdb"
|
store: "postgresql"
|
||||||
|
|
||||||
|
# Database
|
||||||
|
stalwart_db_name: "stalwart"
|
||||||
|
stalwart_db_user: "stalwart"
|
||||||
|
stalwart_db_password: "{{ vault_stalwart_db_password }}"
|
||||||
|
|
||||||
# Tracers
|
# Tracers
|
||||||
stalwart_tracers:
|
stalwart_tracers:
|
||||||
@@ -76,12 +89,9 @@ stalwart_tracers:
|
|||||||
ansi: false
|
ansi: false
|
||||||
enable: true
|
enable: true
|
||||||
|
|
||||||
# ACME / Let's Encrypt
|
# TLS certificate (managed by certbot)
|
||||||
stalwart_acme_enabled: true
|
stalwart_tls_cert: "/etc/letsencrypt/live/{{ stalwart_server_hostname }}/fullchain.pem"
|
||||||
stalwart_acme_directory: "https://acme-v02.api.letsencrypt.org/directory"
|
stalwart_tls_key: "/etc/letsencrypt/live/{{ stalwart_server_hostname }}/privkey.pem"
|
||||||
stalwart_acme_contact: "mailto:admin@rubenhensen.nl"
|
|
||||||
stalwart_acme_domains:
|
|
||||||
- "mail.rubenhensen.nl"
|
|
||||||
|
|
||||||
# Extra config sections (list of {name, options} dicts)
|
# Extra config sections (list of {name, options} dicts)
|
||||||
stalwart_additional_configs: []
|
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;
|
||||||
@@ -19,4 +19,55 @@
|
|||||||
ansible.builtin.service:
|
ansible.builtin.service:
|
||||||
name: "{{ stalwart_service_name }}"
|
name: "{{ stalwart_service_name }}"
|
||||||
state: "{{ stalwart_service_state }}"
|
state: "{{ stalwart_service_state }}"
|
||||||
enabled: "{{ stalwart_service_enabled }}"
|
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 }}"
|
group: "{{ stalwart_system_group }}"
|
||||||
mode: "0755"
|
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
|
- name: Check if already installed
|
||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
path: "{{ stalwart_executable_path }}"
|
path: "{{ stalwart_executable_path }}"
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
[server]
|
[server]
|
||||||
hostname = "{{ stalwart_server_hostname }}"
|
hostname = "{{ stalwart_server_hostname }}"
|
||||||
max-connections = {{ stalwart_server_max_connections }}
|
max-connections = {{ stalwart_server_max_connections }}
|
||||||
{% if stalwart_acme_enabled %}
|
|
||||||
tls.certificate = "acme"
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% for listener in stalwart_server_listeners %}
|
{% for listener in stalwart_server_listeners %}
|
||||||
[server.listener.{{ listener.name }}]
|
[server.listener.{{ listener.name }}]
|
||||||
@@ -19,19 +16,6 @@ protocol = "{{ listener.protocol }}"
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% 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]
|
[storage]
|
||||||
data = "{{ stalwart_storage_data }}"
|
data = "{{ stalwart_storage_data }}"
|
||||||
fts = "{{ stalwart_storage_fts }}"
|
fts = "{{ stalwart_storage_fts }}"
|
||||||
@@ -62,6 +46,21 @@ type = "{{ stalwart_directory_type }}"
|
|||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{% endfor %}
|
{% 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 %}
|
{% for tracer in stalwart_tracers %}
|
||||||
[tracer.{{ tracer.type }}]
|
[tracer.{{ tracer.type }}]
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ ufw_allowed_ports:
|
|||||||
- { port: "587", proto: "tcp", comment: "SMTP submission (STARTTLS)" }
|
- { port: "587", proto: "tcp", comment: "SMTP submission (STARTTLS)" }
|
||||||
- { port: "993", proto: "tcp", comment: "IMAP (implicit TLS)" }
|
- { port: "993", proto: "tcp", comment: "IMAP (implicit TLS)" }
|
||||||
- { port: "443", proto: "tcp", comment: "HTTPS (web admin + JMAP)" }
|
- { 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:
|
secretKeyRef:
|
||||||
name: authentik-postgres-credentials
|
name: authentik-postgres-credentials
|
||||||
key: password
|
key: password
|
||||||
|
- name: VAULT_OIDC_CLIENT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: vault-oidc-client-secret
|
||||||
|
key: client_secret
|
||||||
server:
|
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:
|
ingress:
|
||||||
enabled: true
|
enabled: true
|
||||||
ingressClassName: nginx
|
ingressClassName: nginx
|
||||||
@@ -73,6 +86,15 @@ spec:
|
|||||||
- secretName: authentik-tls
|
- secretName: authentik-tls
|
||||||
hosts:
|
hosts:
|
||||||
- authentik.rubenhensen.nl
|
- 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:
|
postgresql:
|
||||||
enabled: false
|
enabled: false
|
||||||
redis:
|
redis:
|
||||||
|
|||||||
Reference in New Issue
Block a user