Example NGINX-Reverse Proxy Configs for Check-MK

Hi,

I’m posting my NGINX-Reverse-Proxy-Configuration for Check-MK here, since I find that not so easy and to took me some seconds figuring out.

This is the required setup here:

  • Check-MK-RAW Edition (This is tested with CRE 2.2.0p37)
  • an SSL Certificate (Maybe Letsencrypt)
  • The Check-MK instance is configured with it’s own apache process ( for example listening on IP 127.0.0.1 Port 5000)
  • A registered internet domain and a record pointing to the server
  • The site will be publicly accessible at a secret sub-uri, like that:
    https://www.mymonitoring.de/secret-sfdsfdsjfkldsj/
  • A current OS Version installed (This is tested successfully with Ubuntu 24.04)

In order to be able to present a flexible NGINX-Konfiguration, which everybody can use, I created a simple template, which fills in your variable values by utilizing mo and generates a functional nginx config.

Here’s the template:

# required file name: nginx.conf.template

server {
    listen 80;
    server_name {{VHOST_NAME}};

    root /var/www/empty;

    # Redirect from HTTP to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name {{VHOST_NAME}};

    root /var/www/empty;

    # SSL-Certificate and Key
    ssl_certificate     {{SSL_CERT_FILE}};
    ssl_certificate_key {{SSL_KEY_FILE}};

    # Recommended SSL Settings
    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-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256";
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;
    ssl_stapling on;
    ssl_stapling_verify on;

    # replace all site links within the check-mk generated content, so ACCESS_BASE_PATH is added
    sub_filter '/{{CHECK_MK_SITE_NAME}}/check_mk/' '/{{ACCESS_BASE_PATH}}/{{CHECK_MK_SITE_NAME}}/check_mk/';
    sub_filter_once off;
    sub_filter_types text/plain application/json;

    location /{{ACCESS_BASE_PATH}} {

        # Redirect from Pure ACCESS_BASE_PATH to Loginpage of the check_mk instance for convenience
        if ($request_uri = /{{ACCESS_BASE_PATH}}/ ) {
            return 301 /{{ACCESS_BASE_PATH}}/{{CHECK_MK_SITE_NAME}}/check_mk/login.py;
        }

        # Redirect from /ACCESS_BASE_PATH/check_mk/ to Loginpage of the check_mk instance for convenience
        if ($request_uri = /{{ACCESS_BASE_PATH}}/check_mk/ ) {
            return 301 /{{ACCESS_BASE_PATH}}/{{CHECK_MK_SITE_NAME}}/check_mk/login.py;
        }

        # strip (possible multiple occurances of) ACCESS_BASE_PATH before handing it over to the backend instance
        rewrite ^/({{ACCESS_BASE_PATH}}/)+(.*)$ /$2 break;
        proxy_pass http://{{SITE_LISTEN_IP}}:{{SITE_LISTEN_PORT}};  # Proxy Pass to the Backend Check-MK Instance

        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 $scheme;

        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        send_timeout 60s;

        # The following line is important to pass on Cookies. Else it would result in CSRF-Errors
        proxy_cookie_path / "/; Secure; HttpOnly; SameSite=None"; # für Cookies

        gzip on;
        gzip_types text/plain application/json application/javascript text/css text/xml;
    }
}

As second file here’s the configuration script, where you can fill in your values. The script must be executed to generate your nginx config. The script must be located in the same directory as the above nginx.conf.template. After generation you can copy it to the final location (Ubuntu/Debian: /etc/nginx/sites-available).

You may also create a directory /var/www/empty with appropriate permissions and an information (index.html) for the unauthenticated public as this is the configured root directory. If you do not, an error will be displayed.

mo is a simple shellscript implementing the mustache templating engine. It can be downloaded from here:

#!/bin/bash

        VHOST_NAME=www.mymonitoring.com                         # publically reachable Url
     SSL_CERT_FILE=/etc/ssl/certs/mymonitoring-bundle.pem       # Path of the file containing ssl cert and intermediate certs
      SSL_KEY_FILE=/etc/ssl/private/mymonitoring.key            # Path of the file containing ssl private key
  ACCESS_BASE_PATH=secret-sdfdsfs                               # "Hidden" Base URL of your Monitoring Instance, like: https://$VHOST_NAME/secret-sdfdsfs/
CHECK_MK_SITE_NAME=mycmksite                                    # Your Check-MK Site Name
    SITE_LISTEN_IP=127.0.0.1                                    # The IP of the checkmk instance
  SITE_LISTEN_PORT=5000                                         # The Port of the checkmk instance

export VHOST_NAME SSL_CERT_FILE SSL_KEY_FILE ACCESS_BASE_PATH CHECK_MK_SITE_NAME SITE_LISTEN_IP SITE_LISTEN_PORT

mo < nginx.conf.template > nginx.conf.processed

Regards,
XJack

Currently the config has still an error, which causes an 404 not found if you hit the reload button. The ACCESS_BASE_PATH is feeded as parameter to index.py, causing the error. I’m looking into that.

Edit 1: Problem fixed by modifying the rewrite statement.

Edit 2: I also changed envsubst templating to mustache templating with the mo script (see location of the script above). This eliminates the necessity of a workaround to escape the $ characters.

Here’s another template (for use with the mk_config script and mo from above). It’s simpler, because it does not use the secret sub uri and instead passes the / path directly to check-mk:

# required file name: nginx.conf.template

server {
    listen 80;
    server_name {{VHOST_NAME}};

    root /var/www/empty;

    # Redirect from HTTP to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name {{VHOST_NAME}};

    root /var/www/empty;

    # SSL-Certificate and Key
    ssl_certificate     {{SSL_CERT_FILE}};
    ssl_certificate_key {{SSL_KEY_FILE}};

    # Recommended SSL Settings
    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-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256";
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;
    ssl_stapling on;
    ssl_stapling_verify on;

    location / {
        # redirect / URL to cmk start page
        if ($request_uri = / ) {
            return 301 /{{CHECK_MK_SITE_NAME}}/check_mk/;
        }

        proxy_pass http://{{SITE_LISTEN_IP}}:{{SITE_LISTEN_PORT}};  # Redirect to the 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 $scheme;

        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        send_timeout 60s;

        gzip on;
        gzip_types text/plain application/json application/javascript text/css text/xml;
    }
}

There is still a bug in the complex config from post #1: On initial load the tactical overview widget presents the correct links.

But upon automatic background reload, the links do no longer contain the secret-sub-uri. This concerns only the middle and right columns of tactical overview. When I manually reload the whole page, the links are correct again. I actually don’t know how to fix that.