Running IDSTower behind a reverse proxy

When you need this

  • Another web server (Apache, nginx) already owns ports 80 and 443 on the same machine.

  • You terminate TLS centrally at a proxy or load balancer.

  • You want to publish IDSTower under a sub-path, such as https://company.com/idstower.

Two ways to coexist

Move IDSTower to a free port – the simplest option, and it needs no proxy at all. See Configuring custom http port for IDSTower, and prefer it unless you need a single public entry point.

Put a proxy in front – IDSTower listens on an internal port while the existing web server accepts the public traffic and forwards it. The rest of this page covers that setup.

Important

Your managed Suricata hosts are API clients too: they pull rules and indicators and post heartbeats to the URL IDSTower hands them at deployment time. That URL must resolve from the hosts and reach IDSTower – so when the proxy is the only exposed entry point, it must be the proxy’s URL.

Behind a reverse proxy – the settings

  1. Edit /opt/idstower/appsettings.json so that the Hosting section reads like the below (leave PathPrefix out unless you are deploying under a sub-path):

    "Hosting": {
      "URL": "http://*:8080",
      "PathPrefix": "/idstower",
      "ExternalUrl": "https://company.com/idstower"
    }
    
    • URL – the address IDSTower itself listens on; point it at an internal port. localhost and 127.0.0.1 are rejected even behind a proxy, so a loopback-only listener is not supported: use a wildcard or a real address. A wildcard makes ExternalUrl mandatory.

    • ExternalUrl – the complete public URL that browsers and the Suricata hosts use, including the path prefix when you have one.

    • PathPrefix – sub-path deployments only. It must start with /, must not end with one, and must match the location the proxy publishes.

  2. Restart the service and confirm it came up:

    $ sudo systemctl restart idstower.service
    $ sudo systemctl status idstower.service
    
  3. If a cluster already exists, push the new URL to its hosts from Clusters -> Cluster -> Hosts Tab -> Host Actions -> All Hosts -> Host -> Refresh Stale Config.

Warning

IDSTower does not read X-Forwarded-Proto or X-Forwarded-Host, so it cannot tell that the proxy already terminated TLS: it decides whether to send HSTS and redirect to HTTPS from the effective external URL (ExternalUrl when that is set), not from the incoming request. The configuration that keeps the scheme consistent on both sides is to run HTTPS on IDSTower as well (see Configuring https on IDSTower) and let the proxy re-encrypt to it. Use the same path prefix on both sides.

Example proxy configuration

Keep the proxy’s read timeout above AdvancedSettings -> GeneratedPayloadWaitSeconds (default 30 seconds, maximum 300 – see Configuration Guide): that is how long a host’s rules pull may wait server-side while another request is still compiling the rules. Rule imports can be large files, so the request body limit needs to be generous too.

nginx, published at the root:

server {
    listen 443 ssl;
    server_name idstower.company.com;
    ssl_certificate     /etc/ssl/certs/idstower.crt;
    ssl_certificate_key /etc/ssl/private/idstower.key;
    client_max_body_size 100m;
    location / {
        proxy_pass http://192.168.1.10:8080;
        proxy_set_header Host $host;
        proxy_read_timeout 300s;
    }
}

Apache, published at the root (needs mod_proxy, mod_proxy_http and mod_ssl; add your usual SSLCertificateFile / SSLCertificateKeyFile directives):

<VirtualHost *:443>
    ServerName idstower.company.com
    SSLEngine on
    ProxyPreserveHost On
    ProxyPass        / http://192.168.1.10:8080/ timeout=300
    ProxyPassReverse / http://192.168.1.10:8080/
</VirtualHost>

For a sub-path deployment with "PathPrefix": "/idstower", publish the same prefix and forward it unchanged – proxy_pass carries no path of its own here, which is what stops nginx stripping it:

location /idstower/ {
    proxy_pass http://192.168.1.10:8080;
    proxy_set_header Host $host;
    proxy_read_timeout 300s;
    client_max_body_size 100m;
}

Verify

  1. Open the public URL in a browser and sign in.

  2. On a managed Suricata host, run a rules update by hand:

    $ sudo bash /etc/suricata/suricata_rules_update.sh --skip-delay
    
  3. Confirm the host still reports healthy under Clusters -> Cluster -> Hosts.

Common mistakes

  • The hosts cannot resolve the proxy name. ExternalUrl must resolve and be reachable from every managed host, not just from your workstation – deployment probes the API from each host and fails the run when it cannot connect.

  • ExternalUrl left at the internal address. Browsers reach IDSTower through the proxy while the hosts are still sent to an address the proxy does not publish.

  • PathPrefix set on IDSTower, but the proxy strips it or never sends it. It must be identical on both sides.

  • Proxy read timeout shorter than the rules compilation wait. Host rule updates then fail intermittently under load, exactly when several hosts pull at once.

  • Existing hosts never told about the new URL. Changing ExternalUrl only reaches them after a Refresh Stale Config.