For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Forum Discussion

martyn's avatar
martyn
Icon for Altostratus rankAltostratus
Jul 15, 2025

Checkpoint Web Smartconsole behind reverse proxy.

Does anyone have any experience at trying (and hopefully suceeding) to put a Checkpoint (CP) FW Provider-1 based web smartconsole behind a reverse proxy.

The thing is that CP use local IP addresses to identify one of a selection of management module instances.
And they use webtransport/websockets to connect from these mgmt modules back to a browser for displaying FW policies and log data etc.

That all seems fairly OK but they don't anchor it using the connection ID and so the raw IPs (of what they call the domain blade/instance) get passed to the browser. 

But we would prefer to NAT/hide/reIP the server (domain) side IPs and not have the internal server/domain IPs sent along to the browser.

Part of the conversation, and some wrapper text from me, from the server to the client follows:

***

We wish to use access to various customer domains using the /smartconsole web interface. But the access has to be behind a reverse proxy (F5 vIP) and after the initial logon using the CMA IP behind a vIP (so address the browser sees is a service public one) you get a screen where the domain is listed and after selecting continue you get redirected seperately to the CMA IP in an internal JSON/javascript message. Hence breaking the attempt to have the CMA behind a reverse proxy.

***

{"data":{"loginToDomain":{"transportOtt":"107ad894-253d-4638-aa31-1c3e7d23172a","transportUrl":"https://100.64.20.29:443/smartconsole/transport","__typename":"LoginToDomainResponse"}}}

***

 

1 Reply

  • Hi Martyn,

     

    I have not tried this setup before, so please take caution and ensure you test this in a NON-Prod environment.

     

    From a BIG-IP config perspective you will likely need:

     

    1. HTTPS VS listening on 443 using a FQDN so smart console traffic comes here first
    2. Create a pool of all Checkpoint  MGT servers, if these are on different servers or use different backend IP's this pool will need to be setup more dynamically.  I suggest start with 1 backend first. 
    3. Use a HTTP + Stream + Web Socket Profile  ( may need to remove stream profile  .. TBD)
    4. Use SNAT - Auto-Map 
    5. Need's  an IruleLX plugin created and an irule to call it...    so create the IruleLX first

     

    1. const f5 = require('f5-nodejs');
      const url = require('url');
      const ilx = new f5.ILXServer();
      
      ilx.addMethod('rewriteTransportUrl', function(req, res) {
        const payload = req.params()[0]; // Original JSON string
        let json;
        try {
          json = JSON.parse(payload);
        } catch (e) {
          return res.reply(payload); // Return original if invalid JSON
        }
      
        const newHost = req.params()[1]; // External host (e.g., smartconsole.example.com)
      
        // Access nested transportUrl
        if (json.data && json.data.loginToDomain && json.data.loginToDomain.transportUrl) {
          const oldUrl = url.parse(json.data.loginToDomain.transportUrl);
          const internalHost = oldUrl.host; // Extract internal host (e.g., 100.64.20.29:443)
          const ott = json.data.loginToDomain.transportOtt || ''; // Extract OTT
      
          // Rewrite host
          oldUrl.host = newHost;
          json.data.loginToDomain.transportUrl = url.format(oldUrl);
      
          // Return array: [rewritten JSON, OTT, internalHost]
          res.reply([JSON.stringify(json), ott, internalHost]);
        } else {
          res.reply([payload, '', '']); // No match, return original
        }
      });
      
      ilx.listen();

      Now create the Irule:

    when RULE_INIT {
      # Define subtable for OTT mappings (global, with TTL)
      set static::ott_map_ttl 3600; # 1 hour timeout
      set static::json_uri_pattern "/smartconsole"; # URI pattern for JSON responses (adjust if specific)
    }
    
    when CLIENT_ACCEPTED {
      # Initialize iRulesLX handle (once per connection)
      set rpc_handle [ILX::init "json_rewrite_ws_plugin" "json_rewrite_extension"]
    }
    
    when HTTP_REQUEST {
      # Handle WebSocket upgrade requests to /smartconsole/transport
      if { [HTTP::uri] starts_with "/smartconsole/transport" && [HTTP::header exists "Upgrade"] && [string tolower [HTTP::header "Upgrade"]] eq "websocket" } {
        # Extract OTT from query param (e.g., ?ott=uuid)
        set ott [URI::query [HTTP::uri] ott]
    
        if { $ott ne "" } {
          # Lookup internal host from table
          set internal_host [table lookup -subtable "ott_map" $ott]
          if { $internal_host ne "" } {
            # Route to the correct node (assume port 443)
            node [lindex [split $internal_host ":"] 0] 443
          } else {
            # Fallback: reject or default pool
            reject
          }
        } else {
          # No OTT: reject or log
          log local0. "WebSocket request without OTT: [HTTP::uri]"
          reject
        }
      }
    }
    
    when HTTP_RESPONSE {
      # Collect JSON responses for rewriting (check URI or Content-Type)
      if { [HTTP::header Content-Type] contains "application/json" && [HTTP::status] == 200 && [HTTP::uri] contains $static::json_uri_pattern } {
        HTTP::collect [HTTP::header Content-Length]
      }
    }
    
    when HTTP_RESPONSE_DATA {
      # Call iRulesLX to rewrite JSON and extract OTT/internalHost
      set payload [HTTP::payload]
      set result [ILX::call $rpc_handle "rewriteTransportUrl" $payload [HTTP::host]]
    
      # Result is list: {rewritten_json ott internal_host}
      set rewritten_json [lindex $result 0]
      set ott [lindex $result 1]
      set internal_host [lindex $result 2]
    
      if { $ott ne "" && $internal_host ne "" } {
        # Store mapping in table
        table set -subtable "ott_map" $ott $internal_host $static::ott_map_ttl
      }
    
      # Replace payload with rewritten JSON
      HTTP::payload replace 0 [string length $payload] $rewritten_json
    }

      Ensure the irule is applied to the VS and give it a go...   I have not tested this out so there may be plenty of errors.