tutorial

How to Monitor Apache Rampart WS-Security Health with Vigilmon

Apache Rampart enforces WS-Security policies for Axis2 services — but policy assertion mismatches, handler chain breakage, and timestamp drift fail silently with HTTP 200 SOAP faults. Learn how to monitor Rampart security module health with Vigilmon HTTP probes and heartbeat monitors.

Apache Rampart is the WS-Security module for Apache Axis2. It sits above WSS4J and adds policy-driven security enforcement — it reads WS-Policy assertions from the WSDL, selects the appropriate WSS4J actions, and drives the Axis2 handler chain to validate incoming messages and secure outgoing ones. Rampart failure modes are particularly hard to detect: a policy assertion in the WSDL that Rampart cannot satisfy causes it to reject every inbound message with a wsp:PolicyViolation SOAP fault returned as HTTP 200, a misconfigured rampart-config module breaks the entire handler chain silently, and a clock skew beyond the timestamp tolerance causes all WS-Security validation to fail without any HTTP-level signal. The Axis2 service responds, the health check turns green, and no secure message has been accepted in hours.

Vigilmon gives you external visibility into Rampart security module health through HTTP probe monitoring and heartbeat monitors that detect when policy-validated messages stop being processed. This tutorial covers both.


Why Rampart Needs External Monitoring

Rampart's policy-driven security enforcement and handler chain architecture create failure modes that port checks and generic HTTP monitors miss:

  • Policy assertion not satisfiable: a WS-Policy assertion in the WSDL that Rampart cannot match (missing algorithm suite, unsupported token type) rejects every request as HTTP 200 with wsp:PolicyViolation
  • Handler chain breakage: a rampart-config misconfiguration or missing module reference can silently drop Rampart from the handler chain, meaning no security processing occurs at all
  • Timestamp tolerance breach: Axis2 and Rampart enforce wsse:Timestamp freshness; clock skew or NTP failure causes all messages to fail validation
  • Module not engaged: if the Rampart module is not engaged on the service, security assertions are silently skipped — Rampart returning no errors while enforcing nothing
  • Crypto provider failure: the underlying WSS4J crypto provider failing causes Rampart to throw during policy evaluation, breaking the handler chain

External monitoring with Vigilmon adds:

  • Proactive alerting when the Rampart health endpoint returns non-200 or unexpected bodies
  • Module engagement and policy validation monitoring via a custom health resource
  • Heartbeat monitoring for successful policy-validated message processing
  • Multi-region availability checking from outside your network perimeter

Step 1: Build a Rampart Health Endpoint

Rampart does not expose an HTTP health interface — you need to add a custom servlet or JAX-RS resource that checks module engagement and policy processor state.

Java Health Servlet (Rampart + Axis2)

// src/main/java/com/example/health/RampartHealthServlet.java
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.description.AxisModule;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.engine.AxisConfiguration;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class RampartHealthServlet extends HttpServlet {

    private ConfigurationContext configContext;

    public RampartHealthServlet(ConfigurationContext configContext) {
        this.configContext = configContext;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("application/json");
        AxisConfiguration axisConfig = configContext.getAxisConfiguration();
        List<String> issues = new ArrayList<>();
        Map<String, Object> info = new LinkedHashMap<>();

        // Check that the Rampart module is present
        AxisModule rampartModule = axisConfig.getModule("rampart");
        if (rampartModule == null) {
            issues.add("rampart_module_not_loaded");
        } else {
            info.put("rampart_version", rampartModule.getModuleDescription());
        }

        // Check that Rampart is engaged on each security-enabled service
        List<String> engagedServices = new ArrayList<>();
        List<String> unengagedServices = new ArrayList<>();
        for (Map.Entry<String, AxisService> entry : axisConfig.getServices().entrySet()) {
            AxisService service = entry.getValue();
            boolean engaged = service.isEngaged("rampart");
            if (engaged) {
                engagedServices.add(entry.getKey());
            } else {
                // Only flag services that declare WS-Security policy
                if (service.getPolicySubject().getAttachedPolicyComponents().iterator().hasNext()) {
                    unengagedServices.add(entry.getKey());
                }
            }
        }
        info.put("engaged_services", engagedServices);
        if (!unengagedServices.isEmpty()) {
            issues.add("rampart_not_engaged: " + String.join(", ", unengagedServices));
        }

        int status = issues.isEmpty() ? 200 : 503;
        resp.setStatus(status);
        PrintWriter out = resp.getWriter();
        out.print("{");
        out.print("\"status\":\"" + (issues.isEmpty() ? "ok" : "degraded") + "\"");
        if (!issues.isEmpty()) {
            out.print(",\"issues\":" + toJsonArray(issues));
        }
        out.print(",\"rampart_version\":\"" + info.getOrDefault("rampart_version", "unknown") + "\"");
        out.print(",\"engaged_service_count\":" + engagedServices.size());
        out.print("}");
    }

    private String toJsonArray(List<String> items) {
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < items.size(); i++) {
            if (i > 0) sb.append(",");
            sb.append("\"").append(items.get(i).replace("\"", "\\\"")).append("\"");
        }
        return sb.append("]").toString();
    }
}

Register the servlet in your web.xml alongside the Axis2 servlet:

<!-- web.xml -->
<servlet>
    <servlet-name>RampartHealth</servlet-name>
    <servlet-class>com.example.health.RampartHealthServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RampartHealth</servlet-name>
    <url-pattern>/health/rampart</url-pattern>
</servlet-mapping>

Node.js Health Sidecar

// health/rampart.js
const express = require('express');
const https = require('https');
const http = require('http');

const app = express();

const AXIS2_SERVICE_URL = process.env.AXIS2_SERVICE_URL || 'http://localhost:8080/axis2/services/SecureService';
const RAMPART_HEALTH_URL = process.env.RAMPART_HEALTH_URL || 'http://localhost:8080/health/rampart';

function fetchUrl(url) {
  return new Promise((resolve, reject) => {
    const client = url.startsWith('https') ? https : http;
    const req = client.get(url, { timeout: 5000 }, res => {
      let body = '';
      res.on('data', chunk => { body += chunk; });
      res.on('end', () => resolve({ status: res.statusCode, body }));
    });
    req.on('error', reject);
    req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
  });
}

app.get('/health/rampart', async (req, res) => {
  const results = [];
  let allOk = true;

  // Proxy the Java Rampart health servlet
  try {
    const { status, body } = await fetchUrl(RAMPART_HEALTH_URL);
    const parsed = JSON.parse(body);
    const ok = status === 200 && parsed.status === 'ok';
    results.push({ check: 'rampart_module', ok, status, detail: parsed });
    if (!ok) allOk = false;
  } catch (err) {
    results.push({ check: 'rampart_module', ok: false, error: err.message });
    allOk = false;
  }

  // Verify the WSDL is reachable and contains policy elements
  try {
    const { status, body } = await fetchUrl(AXIS2_SERVICE_URL + '?wsdl');
    const hasPolicy = body.includes('Policy') && body.includes('definitions');
    const ok = status === 200 && hasPolicy;
    results.push({ check: 'service_wsdl_with_policy', ok, status });
    if (!ok) allOk = false;
  } catch (err) {
    results.push({ check: 'service_wsdl_with_policy', ok: false, error: err.message });
    allOk = false;
  }

  return res.status(allOk ? 200 : 503).json({
    status: allOk ? 'ok' : 'degraded',
    checks: results,
  });
});

app.listen(3011);

Python Health Sidecar

# health_rampart.py
import os, requests
from flask import Flask, jsonify

app = Flask(__name__)

AXIS2_SERVICE_URL = os.environ.get('AXIS2_SERVICE_URL', 'http://localhost:8080/axis2/services/SecureService')
RAMPART_HEALTH_URL = os.environ.get('RAMPART_HEALTH_URL', 'http://localhost:8080/health/rampart')

@app.route('/health/rampart')
def health():
    results = []
    all_ok = True

    try:
        r = requests.get(RAMPART_HEALTH_URL, timeout=5)
        parsed = r.json()
        ok = r.status_code == 200 and parsed.get('status') == 'ok'
        results.append({'check': 'rampart_module', 'ok': ok, 'status': r.status_code, 'detail': parsed})
        if not ok:
            all_ok = False
    except Exception as e:
        results.append({'check': 'rampart_module', 'ok': False, 'error': str(e)})
        all_ok = False

    try:
        r = requests.get(AXIS2_SERVICE_URL + '?wsdl', timeout=5)
        has_policy = 'Policy' in r.text and 'definitions' in r.text
        ok = r.status_code == 200 and has_policy
        results.append({'check': 'service_wsdl_with_policy', 'ok': ok, 'status': r.status_code})
        if not ok:
            all_ok = False
    except Exception as e:
        results.append({'check': 'service_wsdl_with_policy', 'ok': False, 'error': str(e)})
        all_ok = False

    code = 200 if all_ok else 503
    return jsonify({'status': 'ok' if all_ok else 'degraded', 'checks': results}), code

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3011)

Step 2: Configure Vigilmon HTTP Monitor for Rampart

  1. Log in to vigilmon.online and go to Monitors → New Monitor
  2. Choose HTTP / HTTPS
  3. Set the URL to your Rampart health endpoint: https://your-app.example.com/health/rampart
  4. Set the check interval to 1 minute
  5. Under Expected response, configure:
    • Status code: 200
    • Response body contains: "status":"ok"
    • Response time threshold: 5000ms
  6. Under Alert channels, assign your Slack or PagerDuty channel
  7. Save the monitor

Add a secondary monitor for the Axis2 service WSDL with policy:

  • URL: https://your-app.example.com/axis2/services/SecureService?wsdl
  • Expected: 200, body contains: Policy
  • Interval: 2 minutes
  • Alert channel: P1 (policy WSDL unavailability means no client can establish a secure channel)

Add a monitor for the Axis2 services list page:

  • URL: https://your-app.example.com/axis2/services/listServices
  • Expected: 200, body contains: SecureService
  • Interval: 5 minutes
  • Alert channel: P2 Slack (service list availability confirms deployment but not security state)

Vigilmon's multi-region probe consensus filters transient network issues from genuine handler chain failures.


Step 3: Heartbeat Monitoring for Rampart Policy-Validated Operations

Module engagement monitoring confirms Rampart is loaded and engaged, but not that it is correctly enforcing policies. A handler chain that processes messages but silently skips signature verification still looks healthy.

Vigilmon heartbeat monitors catch these gaps: your service logic pings Vigilmon after each successfully policy-validated and processed operation.

Set Up the Heartbeat Monitor

  1. In Vigilmon, go to Monitors → New Monitor → Heartbeat
  2. Set the name: secure-axis2-rampart-processing
  3. Set the expected interval: 10 minutes
  4. Set the grace period: 15 minutes
  5. Save — copy the heartbeat URL, e.g. https://vigilmon.online/heartbeat/abc123xyz

Wire It Into an Axis2 Service Implementation

// SecureDocumentServiceSkeleton.java
import org.apache.axis2.context.MessageContext;
import java.net.http.*;
import java.net.URI;

public class SecureDocumentServiceSkeleton {

    private final HttpClient http = HttpClient.newHttpClient();
    private final String heartbeatUrl = System.getenv("VIGILMON_HEARTBEAT_URL");

    public SubmitDocumentResponse submitDocument(SubmitDocumentRequest request) {
        MessageContext mc = MessageContext.getCurrentMessageContext();
        // If Rampart ran successfully, security results are stored in the context
        // Verify a security result is present before pinging heartbeat
        boolean securityProcessed = mc != null &&
            mc.getProperty("RECV_RESULTS") != null;

        SubmitDocumentResponse response = documentService.process(request);

        if (response.isSuccess() && securityProcessed) {
            pingHeartbeat();
        }

        return response;
    }

    private void pingHeartbeat() {
        if (heartbeatUrl == null) return;
        try {
            http.sendAsync(
                HttpRequest.newBuilder(URI.create(heartbeatUrl)).GET().build(),
                HttpResponse.BodyHandlers.discarding()
            );
        } catch (Exception ignored) {}
    }
}

Wire It Into an Axis2 Handler for Cross-Cutting Pings

// VigilmonHeartbeatHandler.java
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.handlers.AbstractHandler;
import org.apache.axis2.engine.Handler;
import java.net.http.*;
import java.net.URI;

public class VigilmonHeartbeatHandler extends AbstractHandler {

    private final HttpClient http = HttpClient.newHttpClient();
    private final String heartbeatUrl = System.getenv("VIGILMON_HEARTBEAT_URL");

    @Override
    public Handler.InvocationResponse invoke(MessageContext msgContext) {
        // Only ping on successful inbound processing (response phase)
        if (!msgContext.isServerSide() || msgContext.getFaultContext() != null) {
            return Handler.InvocationResponse.CONTINUE;
        }
        if (heartbeatUrl == null) return Handler.InvocationResponse.CONTINUE;
        try {
            http.sendAsync(
                HttpRequest.newBuilder(URI.create(heartbeatUrl)).GET().build(),
                HttpResponse.BodyHandlers.discarding()
            );
        } catch (Exception ignored) {}
        return Handler.InvocationResponse.CONTINUE;
    }
}

Register the handler in axis2.xml in the OutFlow phase after Rampart processing:

<!-- axis2.xml -->
<phaseOrder type="OutFlow">
    <phase name="MessageOut">
        <handler name="VigilmonHeartbeatHandler"
                 class="com.example.health.VigilmonHeartbeatHandler"/>
    </phase>
</phaseOrder>

Step 4: Alert Routing for Rampart Failures

Rampart failures range from a module not engaged on one service (partial exposure) to the handler chain broken on all services (all secure traffic rejected). Route alerts by severity:

| Monitor | Alert Channel | Priority | |---|---|---| | HTTP: /health/rampart module + engagement state | Slack + PagerDuty | P1 | | HTTP: Secure service WSDL with Policy elements | Slack + PagerDuty | P1 | | Heartbeat: successful policy-validated message | Slack + PagerDuty | P1 | | HTTP: Axis2 services list availability | Slack | P2 |

Set response time thresholds as early warning signals:

  • Alert at 2000ms for the Rampart health endpoint (slow module state checks may indicate Axis2 thread saturation)
  • Alert at 3000ms for the service WSDL (slow policy WSDL delivery blocks client security negotiation)
  • Alert at 10000ms for actual service invocations (WS-Security policy evaluation and cryptographic operations under Rampart are expensive; elevated latency indicates saturation)

For services with strict compliance requirements, set up a status page in Vigilmon showing Rampart module engagement health alongside heartbeat monitor state — audit teams can use this page to verify continuous security enforcement.


Summary

Rampart failures are policy-enforcement failures — Axis2 is running, messages are being received, but the WS-Security handler chain is broken or the policy assertions are not being satisfied. External monitoring catches these before callers experience silent security degradation:

| Monitor Type | What It Covers | |---|---| | HTTP monitor on /health/rampart | Rampart module load, engagement on policy-enabled services | | HTTP monitor on ?wsdl with Policy check | WS-Policy assertion availability for client security negotiation | | Heartbeat monitor on policy-validated processing | End-to-end Rampart enforcement and business logic liveness | | HTTP monitor on Axis2 services list | Service deployment confirmation |

Get started free at vigilmon.online — your first Rampart security monitor is running in under two minutes.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →