Skip to content

Instantly share code, notes, and snippets.

@asifbacchus
Last active September 2, 2022 10:54
Show Gist options
  • Save asifbacchus/122f43a39a1e24066ed50a3af579f5dc to your computer and use it in GitHub Desktop.
Save asifbacchus/122f43a39a1e24066ed50a3af579f5dc to your computer and use it in GitHub Desktop.
NGINX configuration where the machine hosting mailcow is acting as a reverse proxy. Includes section where certain domain names/CNAMEs can be redirected to webmail (SOGo) by default instead of the admin panel and said panel is accessed via the `/config` sub-uri instead.
# these are common security headers that you can easily add to locations or
# entire server blocks by including this file
# include /etc/nginx/headersSecurity.conf;
add_header Feature-Policy "geolocation 'self'";
add_header Referrer-Policy "same-origin" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options noopen;
add_header X-Frame-Options SAMEORIGIN;
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-UA-Compatible "IE=edge";
add_header X-XSS-Protection "1; mode=block" always;
# prevent search engines from indexing sites on this server
add_header X-Robots-Tag none;
#
# mailcow reverse-proxy configuration
# direct-access to host: root = config (default mailcow setup)
# CNAME for webmail or mailserver: root = SOGo, configuration at /config
#
#
# HTTP catch-all for all unconfigured domain names
server {
listen 80 default_server;
listen [::]:80 default_server;
# silently drop the connection
return 444;
}
#
# HTTPS catch-all for all unconfigured domain names
# (user gets HTTPS certificate mismatch error and continues anyways to an invalid domain)
server {
listen 443 default_server ssl http2;
listen [::]:443 default_server ssl http2;
# ssl certificates for this server
ssl_certificate /path/to/your-certificate-full-chain.crt;
ssl_certificate_key /path/to/your-certificate-private-key.key;
# ssl configuration for this server
include /etc/nginx/mozIntermediate_ssl.conf
# silently drop the connection
return 444;
}
#
# Redirect HTTP to HTTPS for *configured* domain names on this server
server {
listen 80;
listen [::]:80;
# specify configured mailcow host server name and CNAMEs
server_name
host.domain.tld
mail.domain.tld
webmail.domain.tld
autodiscover.domain.tld
autoconfig.domain.tld
;
# redirect to properly formed HTTPS request
return 301 https://$host$request_uri;
}
#
# proxy to mailcow using default setup (root = admin backend)
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
# specify mailcow-related server names which should redirect as per default setup
server_name
autodiscover.domain.tld
autoconfig.domain.tld
;
# ssl certificates for this server
ssl_certificate /path/to/your-certificate-full-chain.crt;
ssl_certificate_key /path/to/your-certificate-private-key.key;
# ssl configuration for this server
include /etc/nginx/mozIntermediate_ssl.conf
# security headers
include /etc/nginx/headersSecurity.conf
# client body size settings (you can set a reasonable size here, 0 means no limit)
client_max_body_size 0;
# common proxy settings (reduces annoying proxy buffer messages in logs when dealing with NGINX in docker)
proxy_buffers 16 16k;
proxy_buffer_size 16k;
# set common proxy headers
proxy_set_header Host $http_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 $scheme;
# set upstream locations
# (this allows NGINX to start without crashing if mailcow is not running, make sure the port is correct!)
set $upstream_mailcow http://127.0.0.1:8080;
# proxy ActiveSync connections
location /Microsoft-Server-ActiveSync {
# proxy connect options
proxy_connect_timeout 75s;
proxy_send_timeout 3650s;
proxy_receive_timeout 3650s;
proxy_buffers 64 256k;
# set body buffer size
client_body_buffer_size 512k;
# proxy to mailcow
proxy_pass $upstream_mailcow/Microsoft-Server-ActiveSync;
}
# proxy /config to Mailcow Admin Panel
location ^~ /config/ {
proxy_pass $upstream_mailcow/;
}
# proxy root to Mailcow Admin Panel
location / {
# rewrite case variations of 'config'
rewrite (?i)^/config/ /config/ redirect;
# rewrite 'webmail' to 'SOGo'
rewrite (?i)^/webmail/ /SOGo/ redirect;
# proxy to mailcow
proxy_pass $upstream_mailcow;
}
}
#
# proxy to mailcow using webmail-first setup (root = SOGo, /config = admin backend)
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
# specify mailcow-related server names which should redirect to SOGo by default
server_name
mail.domain.tld
webmail.domain.tld
;
# ssl certificates for this server
ssl_certificate /path/to/your-certificate-full-chain.crt;
ssl_certificate_key /path/to/your-certificate-private-key.key;
# ssl configuration for this server
include /etc/nginx/mozIntermediate_ssl.conf
# security headers
include /etc/nginx/headersSecurity.conf
# client body size settings (you can set a reasonable size here, 0 means no limit)
client_max_body_size 0;
# common proxy settings (reduces annoying proxy buffer messages in logs when dealing with NGINX in docker)
proxy_buffers 16 16k;
proxy_buffer_size 16k;
# set common proxy headers
proxy_set_header Host $http_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 $scheme;
# set upstream locations
# (this allows NGINX to start without crashing if mailcow is not running, make sure the port is correct!)
set $upstream_mailcow http://127.0.0.1:8080;
# static assets
location ^~ /resources/webmail/ {
# cache graphics files
location ~* \.(?:jpe?g|gif|png|ico|gz|svgz?) {
# you can set a different expiry here if you want, this works well though
expires 30d;
add_header Cache-Control "public";
}
# find requested files or return 404
try_files $uri =404;
}
# override logo
# OPTIONAL: if you have a custom logo you want to serve instead of the SOGo logo, here's where to do it
location = /SOGo.woa/WebServerResources/img/sogo-full.svg {
rewrite ^ /file/path/to/your/logo_file.svg break;
}
# proxy /config to Mailcow Admin Panel
location ^~ /config/ {
proxy_pass $upstream_mailcow/;
}
# default location (SOGo)
location / {
# rewrite case variations of config
rewrite (?i)^/config/ /config/ redirect;
# proxy everything else to SOGo
rewrite ^/$ /SOGo/;
proxy_pass $upstream_mailcow;
}
}
#
# Shared SSL configuration
# Based on Mozilla 'Intermediate' configuration (TLS1.2 and TLS1.3)
# last updated: 2022-02-10
#
# SSL certificate and key are defined in the server blocks
# SSL parameters
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# SSL protocls and ciphers
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Diffie-Hellman parameters
ssl_dhparam /path/to/dhparam.pem;
# HSTS (6 months)
# other common max-age values:
# max-age=31536000 (1 year)
# max-age=63072000 (2 years)
add_header Strict-Transport-Security "max-age=15768000" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
# verify chain of trust (this path should work as-is on all debian/ubuntu systems)
ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
# configuration templates recommend setting a resolver here,
# it should properly be set your nginx.conf or a linked configuration file
@daemonvic
Copy link

First of all thank you for taking the time and effort in writing this document it was very helpful I finally got the server running as it should, am having the same situation as @netstat-peanut where my admin username got redirect to the SoGo web mail application instead of logging off, i know your time is very important so when have a chance to take a look into this issue, let us know please.

Be safe and thank you again for everything!

@asifbacchus
Copy link
Author

@daemonvic aww damn, I forgot that replies to gists don't work like issues! I figured my reply to netstat-peanut would have copied here. Oh well:

Yes, I have the exact same issue and have had that issue for 3-4 years now. I believe it is caused by the fact that the mailcow PHP configuration backend uses a session cookie to keep the user logged in. By default, session cookies expire when the browser window (usually all browser windows, actually) are closed. This is why you can click back or navigate back to the admin backend and the previous user is still logged-in. If you close all your browser windows and then open a new one and access the backend, you'll notice that you are no longer logged in.

I usually recommend doing all configuration panel stuff in an incognito or in-private window so that you can just close that window and no cookie persists. This way it does not disrupt your other normal open browser windows. However, it is something you need to remember to do and it is pretty annoying.

The issue doesn't have anything to do with the altered NGINX setup since we are just rewriting URLs and moving stuff around. This is why I'm 99% sure it's the PHP cookie but, to be honest, I've been too lazy to fully investigate the issue. I've learned to live with it.

The workaround would be to have the PHP cookie cleared on logout or use a stateless token-style login but I have not heard anything back from anyone on the mailcow team in, like I said, 3-4 years about this. I can understand it may not be a priority, but it certainly is a little annoying.

Sorry for the bad news... but I hope this maybe clears up what's going on?

@asifbacchus
Copy link
Author

UPDATE

  • more versatile and modern configuration
  • allows specifying which domains/CNAMEs to auto-redirect to SOGo and which ones to leave with the default behaviour
  • use /config instead of /admin since that might confuse or scare end-users
  • automatically case-correct /config and add trailing slash
  • add redirect for /webmail to redirect to SOGo because it makes sense
  • integrate autodiscover and autoconfig into the main location blocks instead of separate servers
  • handle ActiveSync properly
  • cache static webmail assets (image files)
  • option to override SOGo logo with custom file via NGINX configuration instead of mucking around with Docker config

Many times I like listing my actual host machine under the 'default' redirect section along with autodiscover and autoconfig and then listing the mail.domain.tld and webmail.domain.tld under the redirected location block as shown in the example file. That way, admins who know the server name (e.g. host.domain.tld) can use that and be automatically sent to the admin panel while end-users who only know webmail.domain.tld or maybe mail.domain.tld are always sent to SOGo first and must use /config to access the admin panel. Just something to consider in your implementation.

make sure you update the certificate paths!

Let me know if there are any issues or if I made any typos... copied this here pretty quickly. Otherwise, hope it helps!

@netstat-peanut
Copy link

Hi, thank you for your comments; nice to see some activity here. I tried a few different angles to rectify the logout problem but never managed to address it properly. I suspect it's doable, albeit hacky, with a few creative proxy_pass rewrites but I ended up reverting to mailcow at domain root due to the (possible) security concern. I'm the only user at present so I also added basic auth for the time being. I'll look into incorporating your revisions in the rest of the config. Thanks again for the update!

@Green2Matter
Copy link

Green2Matter commented Aug 26, 2022

Hi @asifbacchus
Thanks for this config, it helped me to redo my mailcow setup! One thing I can't get to work is rewriting sogo logo. I keep getting 404 error (nginx log: "GET /SOGo.woa/WebServerResources/img/sogo-full.svg?lm=1657003071 HTTP/2.0" 404 146). I tried different file's path, web server file's ownership and so on. Still 404... Content type isn't correct (in answer headers) but I think it's related to missing or somehow not accessible file...
Answer headers:

:status: 404
Content-Type: text/html
X-Content-Type-Options: nosniff
Date: Fri, 26 Aug 2022 07:47:21 GMT
X-XSS-Protection: 1; mode=block
Referrer-Policy: same-origin
Content-Length: 146
Server: nginx
Strict-Transport-Security: max-age=15768000

EDIT
It's all about nginx default root: error log: open() "/etc/nginx/htmllogo.svg" failed
But even if I put root directive or absolute path this error remains the same. Nginx keeps prepending default root (/etc/nginx/html)...
EDIT2
Somehow it worked:

    location = /SOGo.woa/WebServerResources/img/sogo-full.svg {
         rewrite ^ /logo.svg break;
    }

I had to restart nginx and not only reload. I don't know why I couldn't use absolut path...

@asifbacchus
Copy link
Author

@Green2Matter If you were getting that specific error open() "/etc/nginx/htmllogo.svg" failed then it's likely you forgot the leading slash in your rewrite line -- you were using a relative instead of absolute path. The giveaway is in the concatenation ".../htmllogo.svg".
Regardless, I'm glad you got it working!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment