Skip to content

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 oauth
    • admin.$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"