public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Commit
[Nginx] Use jinja2 for templating nginx configuration
70ca5fde
data/Dockerfiles/nginx/Dockerfile | 18 ++
data/Dockerfiles/nginx/bootstrap.py | 76 ++++++
data/Dockerfiles/nginx/docker-entrypoint.sh | 26 ++
data/conf/nginx/000-map-size.conf | 3 -
data/conf/nginx/dynmaps.conf | 19 --
data/conf/nginx/includes/site-defaults.conf | 242 -------------------
data/conf/nginx/includes/sogo_proxy_auth.conf | 8 -
data/conf/nginx/meta_exporter.conf | 19 --
data/conf/nginx/nginx.conf.j2 | 119 ++++++++++
data/conf/nginx/site.conf | 10 -
data/conf/nginx/sites-default.conf.j2 | 276 ++++++++++++++++++++++
data/conf/nginx/templates/listen_plain.template | 2 -
data/conf/nginx/templates/listen_ssl.template | 3 -
data/conf/nginx/templates/server_name.template.sh | 1 -
data/conf/nginx/templates/sites.template.sh | 38 ---
data/conf/nginx/templates/sogo.template | 1 -
data/conf/nginx/templates/sogo_eas.template.sh | 5 -
docker-compose.yml | 29 +--
18 files changed, 526 insertions(+), 369 deletions(-)
create mode 100644 data/Dockerfiles/nginx/Dockerfile
create mode 100644 data/Dockerfiles/nginx/bootstrap.py
create mode 100755 data/Dockerfiles/nginx/docker-entrypoint.sh
delete mode 100644 data/conf/nginx/000-map-size.conf
delete mode 100644 data/conf/nginx/dynmaps.conf
delete mode 100644 data/conf/nginx/includes/site-defaults.conf
delete mode 100644 data/conf/nginx/includes/sogo_proxy_auth.conf
delete mode 100644 data/conf/nginx/meta_exporter.conf
create mode 100644 data/conf/nginx/nginx.conf.j2
delete mode 100644 data/conf/nginx/site.conf
create mode 100644 data/conf/nginx/sites-default.conf.j2
delete mode 100644 data/conf/nginx/templates/listen_plain.template
delete mode 100644 data/conf/nginx/templates/listen_ssl.template
delete mode 100755 data/conf/nginx/templates/server_name.template.sh
delete mode 100644 data/conf/nginx/templates/sites.template.sh
delete mode 100644 data/conf/nginx/templates/sogo.template
delete mode 100644 data/conf/nginx/templates/sogo_eas.template.sh
Diff
diff --git a/data/Dockerfiles/nginx/Dockerfile b/data/Dockerfiles/nginx/Dockerfile
new file mode 100644
index 00000000..7d2ce34f
--- /dev/null
+++ b/data/Dockerfiles/nginx/Dockerfile
@@ -0,0 +1,18 @@
+FROM nginx:alpine
+LABEL maintainer "The Infrastructure Company GmbH <[email protected]>"
+
+ENV PIP_BREAK_SYSTEM_PACKAGES=1
+
+RUN apk add --no-cache nginx \
+ python3 \
+ py3-pip && \
+ pip install --upgrade pip && \
+ pip install Jinja2
+
+RUN mkdir -p /etc/nginx/includes
+
+COPY ./bootstrap.py /
+COPY ./docker-entrypoint.sh /
+
+ENTRYPOINT ["/docker-entrypoint.sh"]
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/data/Dockerfiles/nginx/bootstrap.py b/data/Dockerfiles/nginx/bootstrap.py
new file mode 100644
index 00000000..0d24ae9b
--- /dev/null
+++ b/data/Dockerfiles/nginx/bootstrap.py
@@ -0,0 +1,76 @@
+import os
+import subprocess
+from jinja2 import Environment, FileSystemLoader
+
+
+def sites_default_conf(env, template_vars):
+ config_name = "sites-default.conf"
+ template = env.get_template(f"{config_name}.j2")
+ config = template.render(template_vars)
+
+ with open(f"/etc/nginx/includes/{config_name}", "w") as f:
+ f.write(config)
+
+def nginx_conf(env, template_vars):
+ config_name = "nginx.conf"
+ template = env.get_template(f"{config_name}.j2")
+ config = template.render(template_vars)
+
+ with open(f"/etc/nginx/{config_name}", "w") as f:
+ f.write(config)
+
+def prepare_template_vars():
+ template_vars = {
+ 'IPV4_NETWORK': os.getenv("IPV4_NETWORK", "172.22.1"),
+ 'TRUSTED_NETWORK': os.getenv("TRUSTED_NETWORK", False),
+ 'SKIP_RSPAMD': os.getenv("SKIP_RSPAMD", "n").lower() in ("y", "yes"),
+ 'SKIP_SOGO': os.getenv("SKIP_SOGO", "n").lower() in ("y", "yes"),
+ 'NGINX_USE_PROXY_PROTOCOL': os.getenv("NGINX_USE_PROXY_PROTOCOL", "n").lower() in ("y", "yes"),
+ 'MAILCOW_HOSTNAME': os.getenv("MAILCOW_HOSTNAME", ""),
+ 'ADDITIONAL_SERVER_NAMES': os.getenv("ADDITIONAL_SERVER_NAMES", "").replace(',', ' '),
+ 'HTTP_PORT': os.getenv("HTTP_PORT", "80"),
+ 'HTTPS_PORT': os.getenv("HTTPS_PORT", "443"),
+ 'SOGOHOST': os.getenv("SOGOHOST", "sogo-mailcow"),
+ 'RSPAMDHOST': os.getenv("RSPAMDHOST", "rspamd-mailcow"),
+ 'PHPFPMHOST': os.getenv("PHPFPMHOST", "php-fpm-mailcow"),
+ }
+
+ ssl_dir = '/etc/ssl/mail/'
+ template_vars['valid_cert_dirs'] = []
+ for d in os.listdir(ssl_dir):
+ full_path = os.path.join(ssl_dir, d)
+ if not os.path.isdir(full_path):
+ continue
+
+ cert_path = os.path.join(full_path, 'cert.pem')
+ key_path = os.path.join(full_path, 'key.pem')
+ domains_path = os.path.join(full_path, 'domains')
+
+ if os.path.isfile(cert_path) and os.path.isfile(key_path) and os.path.isfile(domains_path):
+ with open(domains_path, 'r') as file:
+ domains = file.read().strip()
+ domains_list = domains.split()
+ if domains_list and template_vars["MAILCOW_HOSTNAME"] not in domains_list:
+ template_vars['valid_cert_dirs'].append({
+ 'cert_path': full_path + '/',
+ 'domains': domains
+ })
+
+ return template_vars
+
+def main():
+ env = Environment(loader=FileSystemLoader('./etc/nginx/conf.d'))
+
+ # Render config
+ print("Render config")
+ template_vars = prepare_template_vars()
+ sites_default_conf(env, template_vars)
+ nginx_conf(env, template_vars)
+
+ # Validate config
+ print("Validate config")
+ subprocess.run(["nginx", "-qt"])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/data/Dockerfiles/nginx/docker-entrypoint.sh b/data/Dockerfiles/nginx/docker-entrypoint.sh
new file mode 100755
index 00000000..beb0b4d9
--- /dev/null
+++ b/data/Dockerfiles/nginx/docker-entrypoint.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+until ping ${REDISHOST} -c1 > /dev/null; do
+ echo "Waiting for Redis..."
+ sleep 1
+done
+until ping ${PHPFPMHOST} -c1 > /dev/null; do
+ echo "Waiting for PHP..."
+ sleep 1
+done
+if printf "%s\n" "${SKIP_SOGO}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
+ until ping ${SOGOHOST} -c1 > /dev/null; do
+ echo "Waiting for SOGo..."
+ sleep 1
+ done
+fi
+if printf "%s\n" "${SKIP_RSPAMD}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
+ until ping ${RSPAMDHOST} -c1 > /dev/null; do
+ echo "Waiting for Rspamd..."
+ sleep 1
+ done
+fi
+
+python3 /bootstrap.py
+
+exec "$@"
diff --git a/data/conf/nginx/000-map-size.conf b/data/conf/nginx/000-map-size.conf
deleted file mode 100644
index a834306e..00000000
--- a/data/conf/nginx/000-map-size.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-map_hash_max_size 256;
-map_hash_bucket_size 256;
-
diff --git a/data/conf/nginx/dynmaps.conf b/data/conf/nginx/dynmaps.conf
deleted file mode 100644
index 99c0c6aa..00000000
--- a/data/conf/nginx/dynmaps.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-server {
- listen 8081;
- listen [::]:8081;
- index index.php index.html;
- server_name _;
- error_log /var/log/nginx/error.log;
- access_log /var/log/nginx/access.log;
- root /dynmaps;
-
- location ~ \.php$ {
- try_files $uri =404;
- fastcgi_split_path_info ^(.+\.php)(/.+)$;
- fastcgi_pass phpfpm:9001;
- fastcgi_index index.php;
- include fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- fastcgi_param PATH_INFO $fastcgi_path_info;
- }
-}
diff --git a/data/conf/nginx/includes/site-defaults.conf b/data/conf/nginx/includes/site-defaults.conf
deleted file mode 100644
index 1d03e939..00000000
--- a/data/conf/nginx/includes/site-defaults.conf
+++ /dev/null
@@ -1,242 +0,0 @@
-
- include /etc/nginx/mime.types;
- charset utf-8;
- override_charset on;
-
- server_tokens off;
-
- ssl_protocols TLSv1.2 TLSv1.3;
- ssl_prefer_server_ciphers on;
- ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
- ssl_ecdh_curve X25519:X448:secp384r1:secp256k1;
- ssl_session_cache shared:SSL:50m;
- ssl_session_timeout 1d;
- ssl_session_tickets off;
-
- add_header Strict-Transport-Security "max-age=15768000;";
- add_header X-Content-Type-Options nosniff;
- add_header X-XSS-Protection "1; mode=block";
- add_header X-Robots-Tag none;
- add_header X-Download-Options noopen;
- add_header X-Frame-Options "SAMEORIGIN" always;
- add_header X-Permitted-Cross-Domain-Policies none;
- add_header Referrer-Policy strict-origin;
-
- index index.php index.html;
-
- client_max_body_size 0;
-
- gzip on;
- gzip_disable "msie6";
-
- gzip_vary on;
- gzip_proxied off;
- gzip_comp_level 6;
- gzip_buffers 16 8k;
- gzip_http_version 1.1;
- gzip_min_length 256;
- gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;
-
- location ~ ^/(fonts|js|css|img)/ {
- expires max;
- add_header Cache-Control public;
- }
-
- error_log /var/log/nginx/error.log;
- access_log /var/log/nginx/access.log;
- fastcgi_hide_header X-Powered-By;
- absolute_redirect off;
- root /web;
-
- location / {
- try_files $uri $uri/ @strip-ext;
- }
-
- location /qhandler {
- rewrite ^/qhandler/(.*)/(.*) /qhandler.php?action=$1&hash=$2;
- }
-
- location /edit {
- rewrite ^/edit/(.*)/(.*) /edit.php?$1=$2;
- }
-
- location @strip-ext {
- rewrite ^(.*)$ $1.php last;
- }
-
- location ~ ^/api/v1/(.*)$ {
- try_files $uri $uri/ /json_api.php?query=$1&$args;
- }
-
- location ^~ /.well-known/acme-challenge/ {
- allow all;
- default_type "text/plain";
- }
-
- # If behind reverse proxy, forwards the correct IP
- set_real_ip_from 10.0.0.0/8;
- set_real_ip_from 172.16.0.0/12;
- set_real_ip_from 192.168.0.0/16;
- set_real_ip_from fc00::/7;
- real_ip_header X-Forwarded-For;
- real_ip_recursive on;
-
- rewrite ^/.well-known/caldav$ /SOGo/dav/ permanent;
- rewrite ^/.well-known/carddav$ /SOGo/dav/ permanent;
-
- location ^~ /principals {
- return 301 /SOGo/dav;
- }
-
- location ^~ /inc/lib/ {
- deny all;
- return 403;
- }
-
- location ~ \.php$ {
- try_files $uri =404;
- fastcgi_split_path_info ^(.+\.php)(/.+)$;
- fastcgi_pass phpfpm:9002;
- fastcgi_index index.php;
- include /etc/nginx/fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- fastcgi_param PATH_INFO $fastcgi_path_info;
- fastcgi_read_timeout 3600;
- fastcgi_send_timeout 3600;
- }
-
- location /rspamd/ {
- location /rspamd/auth {
- # proxy_pass is not inherited
- proxy_pass http://rspamd:11334/auth;
- proxy_intercept_errors on;
- proxy_set_header Host $http_host;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_redirect off;
- error_page 401 /_rspamderror.php;
- }
- proxy_pass http://rspamd:11334/;
- proxy_set_header Host $http_host;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_redirect off;
- }
-
- location ~* ^/Autodiscover/Autodiscover.xml {
- fastcgi_split_path_info ^(.+\.php)(/.+)$;
- fastcgi_pass phpfpm:9002;
- include /etc/nginx/fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- try_files /autodiscover.php =404;
- }
-
- location ~* ^/Autodiscover/Autodiscover.json {
- fastcgi_split_path_info ^(.+\.php)(/.+)$;
- fastcgi_pass phpfpm:9002;
- include /etc/nginx/fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- try_files /autodiscover-json.php =404;
- }
-
- location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml {
- fastcgi_split_path_info ^(.+\.php)(/.+)$;
- fastcgi_pass phpfpm:9002;
- include /etc/nginx/fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- try_files /autoconfig.php =404;
- }
-
- location /sogo-auth-verify {
- internal;
- proxy_set_header X-Original-URI $request_uri;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header Host $http_host;
- proxy_set_header Content-Length "";
- proxy_pass http://127.0.0.1:65510/sogo-auth;
- proxy_pass_request_body off;
- }
-
- location ^~ /Microsoft-Server-ActiveSync {
- include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf;
- include /etc/nginx/conf.d/sogo_eas.active;
- proxy_connect_timeout 75;
- proxy_send_timeout 3600;
- proxy_read_timeout 3600;
- proxy_buffer_size 128k;
- proxy_buffers 64 512k;
- proxy_busy_buffers_size 512k;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $http_host;
- client_body_buffer_size 512k;
- client_max_body_size 0;
- }
-
- location ^~ /SOGo {
- location ~* ^/SOGo/so/.*\.(xml|js|html|xhtml)$ {
- include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf;
- include /etc/nginx/conf.d/sogo.active;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $http_host;
- 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 $client_req_scheme://$http_host;
- proxy_set_header x-webobjects-server-port $server_port;
- proxy_hide_header Content-Type;
- add_header Content-Type text/plain;
- break;
- }
- include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf;
- include /etc/nginx/conf.d/sogo.active;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $http_host;
- 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 $client_req_scheme://$http_host;
- proxy_set_header x-webobjects-server-port $server_port;
- proxy_buffer_size 128k;
- proxy_buffers 64 512k;
- proxy_busy_buffers_size 512k;
- proxy_send_timeout 3600;
- proxy_read_timeout 3600;
- client_body_buffer_size 128k;
- client_max_body_size 0;
- break;
- }
-
- location ~* /sogo$ {
- return 301 $client_req_scheme://$http_host/SOGo;
- }
-
- location /SOGo.woa/WebServerResources/ {
- alias /usr/lib/GNUstep/SOGo/WebServerResources/;
- }
-
- location /.woa/WebServerResources/ {
- alias /usr/lib/GNUstep/SOGo/WebServerResources/;
- }
-
- location /SOGo/WebServerResources/ {
- alias /usr/lib/GNUstep/SOGo/WebServerResources/;
- }
-
- location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) {
- alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
- }
-
- include /etc/nginx/conf.d/site.*.custom;
-
- error_page 502 @awaitingupstream;
-
- location @awaitingupstream {
- rewrite ^(.*)$ /_status.502.html break;
- }
-
- location ~ ^/cache/(.*)$ {
- try_files $uri $uri/ /resource.php?file=$1;
- }
diff --git a/data/conf/nginx/includes/sogo_proxy_auth.conf b/data/conf/nginx/includes/sogo_proxy_auth.conf
deleted file mode 100644
index 045b98ad..00000000
--- a/data/conf/nginx/includes/sogo_proxy_auth.conf
+++ /dev/null
@@ -1,8 +0,0 @@
-auth_request /sogo-auth-verify;
-auth_request_set $user $upstream_http_x_user;
-auth_request_set $auth $upstream_http_x_auth;
-auth_request_set $auth_type $upstream_http_x_auth_type;
-proxy_set_header x-webobjects-remote-user "$user";
-proxy_set_header Authorization "$auth";
-proxy_set_header x-webobjects-auth-type "$auth_type";
-
diff --git a/data/conf/nginx/meta_exporter.conf b/data/conf/nginx/meta_exporter.conf
deleted file mode 100644
index 74291b14..00000000
--- a/data/conf/nginx/meta_exporter.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-server {
- listen 9081;
- index index.php index.html;
- server_name _;
- error_log /var/log/nginx/error.log;
- access_log /var/log/nginx/access.log;
- root /meta_exporter;
- client_max_body_size 10M;
- location ~ \.php$ {
- client_max_body_size 10M;
- try_files $uri =404;
- fastcgi_split_path_info ^(.+\.php)(/.+)$;
- fastcgi_pass phpfpm:9001;
- fastcgi_index pipe.php;
- include fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- fastcgi_param PATH_INFO $fastcgi_path_info;
- }
-}
diff --git a/data/conf/nginx/nginx.conf.j2 b/data/conf/nginx/nginx.conf.j2
new file mode 100644
index 00000000..13444129
--- /dev/null
+++ b/data/conf/nginx/nginx.conf.j2
@@ -0,0 +1,119 @@
+user nginx;
+worker_processes auto;
+
+error_log /var/log/nginx/error.log notice;
+pid /var/run/nginx.pid;
+
+
+events {
+ worker_connections 1024;
+}
+
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ sendfile on;
+ #tcp_nopush on;
+
+ keepalive_timeout 65;
+
+ #gzip on;
+
+ # map-size.conf:
+ map_hash_max_size 256;
+ map_hash_bucket_size 256;
+
+ # site.conf:
+ proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g;
+ server_names_hash_max_size 512;
+ server_names_hash_bucket_size 128;
+
+ map $http_x_forwarded_proto $client_req_scheme {
+ default $scheme;
+ https https;
+ }
+
+ # Default
+ server {
+ listen 127.0.0.1:65510; # sogo-auth verify internal
+ listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
+ listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
+ listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
+ listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
+ http2 on;
+
+ ssl_certificate /etc/ssl/mail/cert.pem;
+ ssl_certificate_key /etc/ssl/mail/key.pem;
+
+ server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* {{ ADDITIONAL_SERVER_NAMES }};
+
+ include /etc/nginx/includes/sites-default.conf;
+ }
+
+ # rspamd dynmaps:
+ server {
+ listen 8081;
+ listen [::]:8081;
+ index index.php index.html;
+ server_name _;
+ error_log /var/log/nginx/error.log;
+ access_log /var/log/nginx/access.log;
+ root /dynmaps;
+
+ location ~ \.php$ {
+ try_files $uri =404;
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_pass {{ PHPFPMHOST }}:9001;
+ fastcgi_index index.php;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ }
+ }
+
+ # rspamd meta_exporter:
+ server {
+ listen 9081;
+ index index.php index.html;
+ server_name _;
+ error_log /var/log/nginx/error.log;
+ access_log /var/log/nginx/access.log;
+ root /meta_exporter;
+ client_max_body_size 10M;
+ location ~ \.php$ {
+ client_max_body_size 10M;
+ try_files $uri =404;
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_pass {{ PHPFPMHOST }}:9001;
+ fastcgi_index pipe.php;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ }
+ }
+
+ {% for cert in valid_cert_dirs %}
+ server {
+ listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
+ listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
+ listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
+ listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
+ http2 on;
+
+ ssl_certificate {{ cert.cert_path }}cert.pem;
+ ssl_certificate_key {{ cert.cert_path }}key.pem;
+
+ server_name {{ cert.domains }};
+
+ include /etc/nginx/includes/sites-default.conf;
+ }
+ {% endfor %}
+}
diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf
deleted file mode 100644
index fb40de87..00000000
--- a/data/conf/nginx/site.conf
+++ /dev/null
@@ -1,10 +0,0 @@
-proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g;
-server_names_hash_max_size 512;
-server_names_hash_bucket_size 128;
-
-map $http_x_forwarded_proto $client_req_scheme {
- default $scheme;
- https https;
-}
-
-include /etc/nginx/conf.d/sites.active;
diff --git a/data/conf/nginx/sites-default.conf.j2 b/data/conf/nginx/sites-default.conf.j2
new file mode 100644
index 00000000..783723bf
--- /dev/null
+++ b/data/conf/nginx/sites-default.conf.j2
@@ -0,0 +1,276 @@
+include /etc/nginx/mime.types;
+charset utf-8;
+override_charset on;
+
+server_tokens off;
+
+ssl_protocols TLSv1.2 TLSv1.3;
+ssl_prefer_server_ciphers on;
+ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
+ssl_ecdh_curve X25519:X448:secp384r1:secp256k1;
+ssl_session_cache shared:SSL:50m;
+ssl_session_timeout 1d;
+ssl_session_tickets off;
+
+add_header Strict-Transport-Security "max-age=15768000;";
+add_header X-Content-Type-Options nosniff;
+add_header X-XSS-Protection "1; mode=block";
+add_header X-Robots-Tag none;
+add_header X-Download-Options noopen;
+add_header X-Frame-Options "SAMEORIGIN" always;
+add_header X-Permitted-Cross-Domain-Policies none;
+add_header Referrer-Policy strict-origin;
+
+index index.php index.html;
+
+client_max_body_size 0;
+
+gzip on;
+gzip_disable "msie6";
+
+gzip_vary on;
+gzip_proxied off;
+gzip_comp_level 6;
+gzip_buffers 16 8k;
+gzip_http_version 1.1;
+gzip_min_length 256;
+gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;
+
+location ~ ^/(fonts|js|css|img)/ {
+ expires max;
+ add_header Cache-Control public;
+}
+
+error_log /var/log/nginx/error.log;
+access_log /var/log/nginx/access.log;
+fastcgi_hide_header X-Powered-By;
+absolute_redirect off;
+root /web;
+
+# If behind reverse proxy, forwards the correct IP
+set_real_ip_from 10.0.0.0/8;
+set_real_ip_from 172.16.0.0/12;
+set_real_ip_from 192.168.0.0/16;
+set_real_ip_from fc00::/7;
+{% if not TRUSTED_NETWORK %}
+real_ip_header X-Forwarded-For;
+{% else %}
+set_real_ip_from {{ TRUSTED_NETWORK }};
+real_ip_header proxy_protocol;
+{% endif %}
+real_ip_recursive on;
+
+
+location @strip-ext {
+ rewrite ^(.*)$ $1.php last;
+}
+
+location ^~ /inc/lib/ {
+ deny all;
+ return 403;
+}
+
+location ^~ /.well-known/acme-challenge/ {
+ allow all;
+ default_type "text/plain";
+}
+
+rewrite ^/.well-known/caldav$ /SOGo/dav/ permanent;
+rewrite ^/.well-known/carddav$ /SOGo/dav/ permanent;
+
+
+location / {
+ try_files $uri $uri/ @strip-ext;
+}
+
+location /qhandler {
+ rewrite ^/qhandler/(.*)/(.*) /qhandler.php?action=$1&hash=$2;
+}
+
+location /edit {
+ rewrite ^/edit/(.*)/(.*) /edit.php?$1=$2;
+}
+
+location ~ ^/api/v1/(.*)$ {
+ try_files $uri $uri/ /json_api.php?query=$1&$args;
+}
+
+location ~ ^/cache/(.*)$ {
+ try_files $uri $uri/ /resource.php?file=$1;
+}
+
+location ~ \.php$ {
+ try_files $uri =404;
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_pass {{ PHPFPMHOST }}:9002;
+ fastcgi_index index.php;
+ include /etc/nginx/fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ fastcgi_read_timeout 3600;
+ fastcgi_send_timeout 3600;
+}
+
+location ~* ^/Autodiscover/Autodiscover.xml {
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_pass {{ PHPFPMHOST }}:9002;
+ include /etc/nginx/fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ try_files /autodiscover.php =404;
+}
+
+location ~* ^/Autodiscover/Autodiscover.json {
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_pass {{ PHPFPMHOST }}:9002;
+ include /etc/nginx/fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ try_files /autodiscover-json.php =404;
+}
+
+location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml {
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_pass {{ PHPFPMHOST }}:9002;
+ include /etc/nginx/fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ try_files /autoconfig.php =404;
+}
+
+{% if not SKIP_RSPAMD %}
+location /rspamd/ {
+ proxy_pass http://{{ RSPAMDHOST }}:11334/;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
+ proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
+ proxy_redirect off;
+ proxy_intercept_errors on;
+ error_page 401 /_rspamderror.php;
+}
+{% endif %}
+
+{% if not SKIP_SOGO %}
+location ^~ /principals {
+ return 301 /SOGo/dav;
+}
+
+location /sogo-auth-verify {
+ internal;
+ proxy_set_header X-Original-URI $request_uri;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $http_host;
+ proxy_set_header Content-Length "";
+ proxy_pass http://127.0.0.1:65510/sogo-auth;
+ proxy_pass_request_body off;
+}
+
+location ^~ /Microsoft-Server-ActiveSync {
+ auth_request /sogo-auth-verify;
+ auth_request_set $user $upstream_http_x_user;
+ auth_request_set $auth $upstream_http_x_auth;
+ auth_request_set $auth_type $upstream_http_x_auth_type;
+ proxy_set_header x-webobjects-remote-user "$user";
+ proxy_set_header Authorization "$auth";
+ proxy_set_header x-webobjects-auth-type "$auth_type";
+
+ proxy_pass http://{{ SOGOHOST }}:20000/SOGo/Microsoft-Server-ActiveSync;
+
+ proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
+ proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
+ proxy_connect_timeout 75;
+ proxy_send_timeout 3600;
+ proxy_read_timeout 3600;
+ proxy_buffer_size 128k;
+ proxy_buffers 64 512k;
+ proxy_busy_buffers_size 512k;
+ proxy_set_header Host $http_host;
+ client_body_buffer_size 512k;
+ client_max_body_size 0;
+}
+
+location ^~ /SOGo {
+ location ~* ^/SOGo/so/.*\.(xml|js|html|xhtml)$ {
+ auth_request /sogo-auth-verify;
+ auth_request_set $user $upstream_http_x_user;
+ auth_request_set $auth $upstream_http_x_auth;
+ auth_request_set $auth_type $upstream_http_x_auth_type;
+ proxy_set_header x-webobjects-remote-user "$user";
+ proxy_set_header Authorization "$auth";
+ proxy_set_header x-webobjects-auth-type "$auth_type";
+
+ proxy_pass http://{{ SOGOHOST }}:20000;
+
+ proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
+ proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
+ proxy_set_header Host $http_host;
+ 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 $client_req_scheme://$http_host;
+ proxy_set_header x-webobjects-server-port $server_port;
+ proxy_hide_header Content-Type;
+ add_header Content-Type text/plain;
+ break;
+ }
+ auth_request /sogo-auth-verify;
+ auth_request_set $user $upstream_http_x_user;
+ auth_request_set $auth $upstream_http_x_auth;
+ auth_request_set $auth_type $upstream_http_x_auth_type;
+ proxy_set_header x-webobjects-remote-user "$user";
+ proxy_set_header Authorization "$auth";
+ proxy_set_header x-webobjects-auth-type "$auth_type";
+
+ proxy_pass http://{{ SOGOHOST }}:20000;
+
+ proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
+ proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
+ proxy_set_header Host $http_host;
+ 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 $client_req_scheme://$http_host;
+ proxy_set_header x-webobjects-server-port $server_port;
+ proxy_buffer_size 128k;
+ proxy_buffers 64 512k;
+ proxy_busy_buffers_size 512k;
+ proxy_send_timeout 3600;
+ proxy_read_timeout 3600;
+ client_body_buffer_size 128k;
+ client_max_body_size 0;
+ break;
+}
+
+location ~* /sogo$ {
+ return 301 $client_req_scheme://$http_host/SOGo;
+}
+
+location /SOGo.woa/WebServerResources/ {
+ alias /usr/lib/GNUstep/SOGo/WebServerResources/;
+}
+
+location /.woa/WebServerResources/ {
+ alias /usr/lib/GNUstep/SOGo/WebServerResources/;
+}
+
+location /SOGo/WebServerResources/ {
+ alias /usr/lib/GNUstep/SOGo/WebServerResources/;
+}
+
+location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) {
+ alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
+}
+{% endif %}
+
+
+include /etc/nginx/conf.d/site.*.custom;
+
+error_page 502 @awaitingupstream;
+
+location @awaitingupstream {
+ rewrite ^(.*)$ /_status.502.html break;
+}
+
+location ~* \.php$ {
+ return 404;
+}
+location ~* \.twig$ {
+ return 404;
+}
diff --git a/data/conf/nginx/templates/listen_plain.template b/data/conf/nginx/templates/listen_plain.template
deleted file mode 100644
index a044b22f..00000000
--- a/data/conf/nginx/templates/listen_plain.template
+++ /dev/null
@@ -1,2 +0,0 @@
-listen ${HTTP_PORT};
-listen [::]:${HTTP_PORT};
diff --git a/data/conf/nginx/templates/listen_ssl.template b/data/conf/nginx/templates/listen_ssl.template
deleted file mode 100644
index 40c402d0..00000000
--- a/data/conf/nginx/templates/listen_ssl.template
+++ /dev/null
@@ -1,3 +0,0 @@
-listen ${HTTPS_PORT} ssl;
-listen [::]:${HTTPS_PORT} ssl;
-http2 on;
diff --git a/data/conf/nginx/templates/server_name.template.sh b/data/conf/nginx/templates/server_name.template.sh
deleted file mode 100755
index 291b378f..00000000
--- a/data/conf/nginx/templates/server_name.template.sh
+++ /dev/null
@@ -1 +0,0 @@
-echo "server_name ${MAILCOW_HOSTNAME} autodiscover.* autoconfig.* $(echo ${ADDITIONAL_SERVER_NAMES} | tr ',' ' ');"
diff --git a/data/conf/nginx/templates/sites.template.sh b/data/conf/nginx/templates/sites.template.sh
deleted file mode 100644
index 782c8141..00000000
--- a/data/conf/nginx/templates/sites.template.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-echo '
-server {
- listen 127.0.0.1:65510;
- include /etc/nginx/conf.d/listen_plain.active;
- include /etc/nginx/conf.d/listen_ssl.active;
-
- ssl_certificate /etc/ssl/mail/cert.pem;
- ssl_certificate_key /etc/ssl/mail/key.pem;
-
- include /etc/nginx/conf.d/server_name.active;
-
- include /etc/nginx/conf.d/includes/site-defaults.conf;
-}
-';
-for cert_dir in /etc/ssl/mail/*/ ; do
- if [[ ! -f ${cert_dir}domains ]] || [[ ! -f ${cert_dir}cert.pem ]] || [[ ! -f ${cert_dir}key.pem ]]; then
- continue
- fi
- # do not create vhost for default-certificate. the cert is already in the default server listen
- domains="$(cat ${cert_dir}domains | sed -e 's/^[[:space:]]*//')"
- case "${domains}" in
- "") continue;;
- "${MAILCOW_HOSTNAME}"*) continue;;
- esac
- echo -n '
-server {
- include /etc/nginx/conf.d/listen_ssl.active;
-
- ssl_certificate '${cert_dir}'cert.pem;
- ssl_certificate_key '${cert_dir}'key.pem;
-';
- echo -n '
- server_name '${domains}';
-
- include /etc/nginx/conf.d/includes/site-defaults.conf;
-}
-';
-done
diff --git a/data/conf/nginx/templates/sogo.template b/data/conf/nginx/templates/sogo.template
deleted file mode 100644
index 2c084389..00000000
--- a/data/conf/nginx/templates/sogo.template
+++ /dev/null
@@ -1 +0,0 @@
-proxy_pass http://${IPV4_NETWORK}.248:20000;
diff --git a/data/conf/nginx/templates/sogo_eas.template.sh b/data/conf/nginx/templates/sogo_eas.template.sh
deleted file mode 100644
index b241ef0e..00000000
--- a/data/conf/nginx/templates/sogo_eas.template.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-if printf "%s\n" "${SKIP_SOGO}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
- echo "return 410;"
-else
- echo "proxy_pass http://${IPV4_NETWORK}.248:20000/SOGo/Microsoft-Server-ActiveSync;"
-fi
diff --git a/docker-compose.yml b/docker-compose.yml
index b0324521..31d6f56f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -359,33 +359,26 @@ services:
nginx-mailcow:
depends_on:
- - sogo-mailcow
- - php-fpm-mailcow
- redis-mailcow
- image: nginx:mainline-alpine
+ - php-fpm-mailcow
+ - sogo-mailcow
+ - rspamd-mailcow
+ image: mailcow/nginx:1.00
dns:
- ${IPV4_NETWORK:-172.22.1}.254
- command: /bin/sh -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active &&
- envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active &&
- envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active &&
- . /etc/nginx/conf.d/templates/server_name.template.sh > /etc/nginx/conf.d/server_name.active &&
- . /etc/nginx/conf.d/templates/sites.template.sh > /etc/nginx/conf.d/sites.active &&
- . /etc/nginx/conf.d/templates/sogo_eas.template.sh > /etc/nginx/conf.d/sogo_eas.active &&
- nginx -qt &&
- until ping phpfpm -c1 > /dev/null; do sleep 1; done &&
- until ping sogo -c1 > /dev/null; do sleep 1; done &&
- until ping redis -c1 > /dev/null; do sleep 1; done &&
- until ping rspamd -c1 > /dev/null; do sleep 1; done &&
- exec nginx -g 'daemon off;'"
environment:
- HTTPS_PORT=${HTTPS_PORT:-443}
- HTTP_PORT=${HTTP_PORT:-80}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
+ - ADDITIONAL_SERVER_NAMES=${ADDITIONAL_SERVER_NAMES:-}
- TZ=${TZ}
- SKIP_SOGO=${SKIP_SOGO:-n}
- - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
- - ADDITIONAL_SERVER_NAMES=${ADDITIONAL_SERVER_NAMES:-}
+ - SKIP_RSPAMD=${SKIP_RSPAMD:-n}
+ - PHPFPMHOST=${PHPFPMHOST:-php-fpm-mailcow}
+ - SOGOHOST=${SOGOHOST:-sogo-mailcow}
+ - RSPAMDHOST=${RSPAMDHOST:-rspamd-mailcow}
+ - REDISHOST=${REDISHOST:-redis-mailcow}
+ - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
volumes:
- ./data/web:/web:ro,z
- ./data/conf/rspamd/dynmaps:/dynmaps:ro,z