Networking
IPs
Kolab requires at least one public IP to function.
On a single node deployment one IP is enough. For a cluster-setup at least 3 internal ip's are required, that are claimed by services via metallb:
- Postfix requires a dedicated IP for port 25 email receiving
- Coturn requires a dedicated IP
- Managesieve requires a dedicated IP because it is not proxied via the nginx proxy.
- The nginx proxy claims one IP for all other services, forwarding the client ip as necessary.
The dedicated IP requirement stems from how services are exposed via metallb, and the requirement to see client-ips. It would in principle be possible to consolidate all services on a single IP by routing everything over a proxy, but this requires making all components compatible with the proxy protocol to forward the client ip (postfix via postscreen). Metallb currently requires dedicated ip's because the service must be on the same node as the destination pod for the client-ip to be accurate.
Client-IP
Various components require that the client-ip is available for them to function properly:
- Postfix requires the client IP on port 25, for various checks
- Coturn requires the client IP for RDP traffic routing.
- The NGINX proxy requires the client IP for imap/submission for rate-limiting and access control checks.
For http based traffic the client ip may also be required (e.g. for dav/activesync 2fa), but over http the X-Forwarded-For header may be used (set by ingress by default).
Exposed endpoints
- Port 80/443 (via ingress):
/: Kolab 4 UI/api: Kolab 4 API/oauth: Laravel passport oauthadmin.$domain/horizon: Laravel Horizon Dashboard/Microsoft-Server-ActiveSync: Activesync via NGINX Proxy to roundcube container/dav: Dav via NGINX Proxy to imap container/webmail: Roundcube via NGINX Proxy to roundcube container/chwala: Chwala file api via NGINX Proxy to roundcube container/browser, /hosting, /cool: Collabora http endpoints via NGINX Proxy to collabora container/meetmedia: Kolab Meet http endpoint/.well-known: Various .well-known endpoints for autoconfig/mail/config-v1.1.xml: Mail autoconfig/prometheus: Prometheus/select: Victorialogs API/alertmanager: Alertmanager UI
- Port 143:993 (via metallb, proxy ip):
- IMAP via NGINX Proxy
- Port 465/587(via metallb, proxy ip):
- Submission for clients
- Port 25 (via metallb, smtp ip):
- SMTP for mail delivery
- Port 3478 (via metallb, turn ip)
- Coturn for Webrtc
DNS
To successfully send and receive mail, various dns related aspects must be configured properly.
PTR records
A PTR record must be available for all ip's that appear as source addresses when sending emails, so that a dns reverse lookup resolves to a domain that also points to the same IP via A record. (postfix reject_unknown_client_hostname).
MTA-STS
MTA-STS applies to inbound email from other parties. It requests from a compliant mailexchanger to only deliver email over tls, and only to the ip's listed in the mta-sts policy
- Before submitting mail the MTA-STS policy on https://mta_sts.$domain/.well-known/mta-sts.txt will be looked up, which looks like this:
version: STSv1 mode: enforce mx: mx01.kolabnow.com max_age: 604800 - It will also lookup _mta-sts.$domain with a policy id that should change when the policy changes.
- Email will only be delivered to the servers listed in the policy file.
Relevant DNS records:
mta-sts.$domain CNAME kolab.klab.cc._mta-sts.$domain TXT "v=STSv1; id=2024031901;"
The mta-sts. CNAME record is only required if we don't provide the policy directly from that subdomain.
DKIM
DKIM is applied to individual emails, and ensures the header information of each mail has not been tampered with. Each mail is signed with a private key, the recipient verifies the signature after looking up the public key on the senders domain (d= in signature). Amavis implements dkim signing and verification.
The published dkim key must match the selector used in the signature (s=dkim1), which is used to select the correct dkim public key in dns (on the domain in the d= in the signature). By using different selectors, multiple dkim signatures are possible, e.g. for key rotation.
Relevant DNS records:
dkim1._domainkey.kolab TXT (
"v=DKIM1; p="
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7JdTEn/T2MhB6KLbATJj"
"4SGernbem4d7dAW7/kVRbiMB2EtPpQCR98eeXOOHcufXVc3w4BocXEnD47JPpkFY"
"JBXWF32m4Y2SapBYbsXndN9fyXRrHO9wPJlW5QK5i9D/bRUznfaBJm54y+BuX0Ln"
"/ippqFe6z3LPjmro9Y9WpRzevYG/TT69Iug5v4U/PA1/rEv+zZGQvNxInZYF7O2M"
"FDbD2pYi7l4hWADP+iwOEc+Li5vPlEvOaUlSQCb06sc0/QBHDDyU2WaEJiYy/Mk2"
"xCmSI44f3mQghmiNsu7vlPiztAYKjNyiVk8iWDttL9OV9qQyxHL18Q74UzJR6Ayl"
"rwIDAQAB"
)
To delegate the dkim key:
dkim1._domainkey.kolab-secondary CNAME dkim1._domainkey.kolab.klab.cc
SPF
SPF defines from which ip's email may come.
The spf policy will be retrieved on the domain in the SMTP HELO/FROM (return path). The policy must match the sending ip.
Relevant DNS records:
kolab TXT "v=spf1 mx a:kolab.klab.cc mx:kolab.klab.cc -all"
DMARC
The dmarc policy tells the recipient what to do after checking spf and dkim.
Relevant DNS records:
_dmarc.kolab TXT "v=DMARC1; p=quarantine; adkim=s; aspf=s; rua=mailto:admin@kolab.klab.cc; ruf=mailto:admin@kolab.klab.cc"
The above policy sets a strict adkim/aspf policy, and instructs to quarantine mail if the check fails.
Autodiscovery
For Outlook:
autodiscover.kolab CNAME kolab.klab.cc.
For service discovery:
_autodiscover._tcp.kolab SRV 0 0 443 kolab.klab.cc.
_caldav._tcp.kolab SRV 0 0 80 kolab.klab.cc.
_caldavs._tcp.kolab SRV 0 1 443 kolab.klab.cc.
_carddav._tcp.kolab SRV 0 0 80 kolab.klab.cc.
_carddavs._tcp.kolab SRV 0 1 443 kolab.klab.cc.
_imap._tcp.kolab SRV 0 0 143 kolab.klab.cc.
_imaps._tcp.kolab SRV 0 1 993 kolab.klab.cc.
_sieve._tcp.kolab SRV 0 0 4190 kolab.klab.cc.
_submission._tcp.kolab SRV 0 1 587 kolab.klab.cc.
_webdav._tcp.kolab SRV 0 0 80 kolab.klab.cc.
_webdavs._tcp.kolab SRV 0 1 443 kolab.klab.cc.
Enable tls reporting via tlsrpt for failed delivery attempts:
_smtp._tls.kolab TXT "v=TLSRPTv1; rua=mailto:admin@kolab.klab.cc"
Example: kolab.klab.cc subdomain configuration
kolab A 185.254.79.10
kolab MX 10 kolab.klab.cc.
kolab TXT "v=spf1 mx a:kolab.klab.cc mx:kolab.klab.cc -all"
autodiscover.kolab CNAME kolab.klab.cc.
_autodiscover._tcp.kolab SRV 0 0 443 kolab.klab.cc.
_caldav._tcp.kolab SRV 0 0 80 kolab.klab.cc.
_caldavs._tcp.kolab SRV 0 1 443 kolab.klab.cc.
_carddav._tcp.kolab SRV 0 0 80 kolab.klab.cc.
_carddavs._tcp.kolab SRV 0 1 443 kolab.klab.cc.
_imap._tcp.kolab SRV 0 0 143 kolab.klab.cc.
_imaps._tcp.kolab SRV 0 1 993 kolab.klab.cc.
_sieve._tcp.kolab SRV 0 0 4190 kolab.klab.cc.
_submission._tcp.kolab SRV 0 1 587 kolab.klab.cc.
_webdav._tcp.kolab SRV 0 0 80 kolab.klab.cc.
_webdavs._tcp.kolab SRV 0 1 443 kolab.klab.cc.
_smtp._tls.kolab TXT "v=TLSRPTv1; rua=mailto:admin@kolab.klab.cc"
_dmarc.kolab TXT "v=DMARC1; p=quarantine; adkim=s; aspf=s; rua=mailto:admin@kolab.klab.cc; ruf=mailto:admin@kolab.klab.cc"
mta-sts.kolab CNAME kolab.klab.cc.
_mta-sts.kolab TXT "v=STSv1; id=2024031901;"
dkim1._domainkey.kolab TXT (
"v=DKIM1; p="
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7JdTEn/T2MhB6KLbATJj"
"4SGernbem4d7dAW7/kVRbiMB2EtPpQCR98eeXOOHcufXVc3w4BocXEnD47JPpkFY"
"JBXWF32m4Y2SapBYbsXndN9fyXRrHO9wPJlW5QK5i9D/bRUznfaBJm54y+BuX0Ln"
"/ippqFe6z3LPjmro9Y9WpRzevYG/TT69Iug5v4U/PA1/rEv+zZGQvNxInZYF7O2M"
"FDbD2pYi7l4hWADP+iwOEc+Li5vPlEvOaUlSQCb06sc0/QBHDDyU2WaEJiYy/Mk2"
"xCmSI44f3mQghmiNsu7vlPiztAYKjNyiVk8iWDttL9OV9qQyxHL18Q74UzJR6Ayl"
"rwIDAQAB"
)
Example: Additional configuration of a secondary domain
kolab-secondary MX 10 kolab.klab.cc.
kolab-secondary TXT "v=spf1 mx include:kolab.klab.cc -all"
_dmarc.kolab-secondary TXT "v=DMARC1; p=quarantine; adkim=s; aspf=s; rua=mailto:admin@kolab.klab.cc; ruf=mailto:admin@kolab.klab.cc"
dkim1._domainkey.kolab-secondary CNAME dkim1._domainkey.kolab.klab.cc
_autodiscover._tcp.kolab-secondary SRV 0 0 443 kolab.klab.cc.
_caldav._tcp.kolab-secondary SRV 0 0 80 kolab.klab.cc.
_caldavs._tcp.kolab-secondary SRV 0 1 443 kolab.klab.cc.
_carddav._tcp.kolab-secondary SRV 0 0 80 kolab.klab.cc.
_carddavs._tcp.kolab-secondary SRV 0 1 443 kolab.klab.cc.
_imap._tcp.kolab-secondary SRV 0 0 143 kolab.klab.cc.
_imaps._tcp.kolab-secondary SRV 0 1 993 kolab.klab.cc.
_sieve._tcp.kolab-secondary SRV 0 0 4190 kolab.klab.cc.
_submission._tcp.kolab-secondary SRV 0 1 587 kolab.klab.cc.
_webdav._tcp.kolab-secondary SRV 0 0 80 kolab.klab.cc.
_webdavs._tcp.kolab-secondary SRV 0 1 443 kolab.klab.cc.
_smtp._tls.kolab-secondary TXT "v=TLSRPTv1; rua=mailto:admin@kolab.klab.cc"
NAT
The following illustrates an iptables configuration assuming:
- The external ip of the deployment is 185.254.0.1
- The internal ip for http is 10.0.1.1
- The internal ip for smtp is 10.0.1.2
- The internal ip for imap is 10.0.1.3
- The internal ip for webrtc is 10.0.1.4
- The external interface is enP49p3s0f0+
Inbound
iptables -t nat -A PREROUTING -p tcp -m tcp -d "185.254.0.1" --dport 80 -j DNAT --to-destination "10.0.1.1"
iptables -t nat -A PREROUTING -p tcp -m tcp -d "185.254.0.1" --dport 443 -j DNAT --to-destination "10.0.1.1"
# Postfix has a dedicated ip for port 25
iptables -t nat -A PREROUTING -p tcp -m tcp -d "185.254.0.1" --dport 25 -j DNAT --to-destination "10.0.1.2"
# The nginx proxy has a dedicated ip
iptables -t nat -A PREROUTING -p tcp -m tcp -d "185.254.0.1" --dport 143 -j DNAT --to-destination "10.0.2.3"
iptables -t nat -A PREROUTING -p tcp -m tcp -d "185.254.0.1" --dport 465 -j DNAT --to-destination "10.0.2.3"
iptables -t nat -A PREROUTING -p tcp -m tcp -d "185.254.0.1" --dport 587 -j DNAT --to-destination "10.0.2.3"
iptables -t nat -A PREROUTING -p tcp -m tcp -d "185.254.0.1" --dport 993 -j DNAT --to-destination "10.0.2.3"
iptables -t nat -A PREROUTING -p tcp -m tcp -d "185.254.0.1" --dport 4190 -j DNAT --to-destination "10.0.2.3"
# meet webrtc ports
iptables -t nat -A PREROUTING -p tcp -m tcp -d "185.254.0.1" --dport 44444:44446 -j DNAT --to-destination "10.0.2.4"
iptables -t nat -A PREROUTING -p udp -m udp -d "185.254.0.1" --dport 44444:44446 -j DNAT --to-destination "10.0.2.4"
Outbound
# http
iptables -A FORWARD -s "10.0.1.0/24" -d "0.0.0.0/0" -p "tcp" -m "tcp" --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "10.0.1.0/24" -s "0.0.0.0/0" -p "tcp" -m "tcp" --sport 80 -m state --state ESTABLISHED,RELATED -j ACCEPT
# https
iptables -A FORWARD -s "10.0.1.0/24" -d "0.0.0.0/0" -p "tcp" -m "tcp" --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "10.0.1.0/24" -s "0.0.0.0/0" -p "tcp" -m "tcp" --sport 443 -m state --state ESTABLISHED,RELATED -j ACCEPT
# imap
iptables -A FORWARD -s "10.0.1.0/24" -d "0.0.0.0/0" -p "tcp" -m "tcp" --dport 143 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "10.0.1.0/24" -s "0.0.0.0/0" -p "tcp" -m "tcp" --sport 143 -m state --state ESTABLISHED,RELATED -j ACCEPT
# imaps
iptables -A FORWARD -s "10.0.1.0/24" -d "0.0.0.0/0" -p "tcp" -m "tcp" --dport 993 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "10.0.1.0/24" -s "0.0.0.0/0" -p "tcp" -m "tcp" --sport 993 -m state --state ESTABLISHED,RELATED -j ACCEPT
# smtp
iptables -A FORWARD -s "10.0.1.0/24" -d "0.0.0.0/0" -p "tcp" -m "tcp" --dport 25 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "10.0.1.0/24" -s "0.0.0.0/0" -p "tcp" -m "tcp" --sport 25 -m state --state ESTABLISHED,RELATED -j ACCEPT
# smtps
iptables -A FORWARD -s "10.0.1.0/24" -d "0.0.0.0/0" -p "tcp" -m "tcp" --dport 465 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "10.0.1.0/24" -s "0.0.0.0/0" -p "tcp" -m "tcp" --sport 465 -m state --state ESTABLISHED,RELATED -j ACCEPT
# submission
iptables -A FORWARD -s "10.0.1.0/24" -d "0.0.0.0/0" -p "tcp" -m "tcp" --dport 587 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "10.0.1.0/24" -s "0.0.0.0/0" -p "tcp" -m "tcp" --sport 587 -m state --state ESTABLISHED,RELATED -j ACCEPT
# sieve
iptables -A FORWARD -s "10.0.1.0/24" -d "0.0.0.0/0" -p "tcp" -m "tcp" --dport 4190 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "10.0.1.0/24" -s "0.0.0.0/0" -p "tcp" -m "tcp" --sport 4190 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -s "10.0.1.0/24" -d "0.0.0.0/0" -p "udp" -m "udp" --dport 4190 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "10.0.1.0/24" -s "0.0.0.0/0" -p "udp" -m "udp" --sport 4190 -m state --state ESTABLISHED,RELATED -j ACCEPT
# sieve
iptables -A FORWARD -s "0/0" -d "10.0.2.3/28" -p "tcp" -m "tcp" --dport 4190 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "0/0" -s "10.0.2.3/28" -p "tcp" -m "tcp" --sport 4190 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -s "0/0" -d "10.0.2.3/28" -p "udp" -m "udp" --dport 4190 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "0/0" -s "10.0.2.3/28" -p "udp" -m "udp" --sport 4190 -m state --state ESTABLISHED,RELATED -j ACCEPT
# webrtc
iptables -A FORWARD -s "0/0" -d "0.0.0.0/0" -p "tcp" -m "tcp" --dport 44444:44446 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "0/0" -s "0.0.0.0/0" -p "tcp" -m "tcp" --sport 44444:44446 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -s "0/0" -d "0.0.0.0/0" -p "udp" -m "udp" --dport 44444:44446 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d "0/0" -s "0.0.0.0/0" -p "udp" -m "udp" --sport 44444:44446 -m state --state ESTABLISHED,RELATED -j ACCEPT
# Set source ip of outgoing packets
iptables -t nat -A POSTROUTING -o "enP49p3s0f0+" -s 10.0.1.0/24 -j SNAT --to-source "185.254.0.1"