# IB Node Security Audit

LLMS index: [llms.txt](/llms.txt)

---

<div id="pslens-context-panel" class="card border-info mb-4 d-none">
  <div class="card-header bg-light text-info py-2 fw-bold d-flex align-items-center border-bottom border-info-subtle">
    <i class="bi bi-info-circle-fill me-2"></i>
    <span>Tailored Operational Context</span>
  </div>
  <div class="card-body p-0">
    <ul class="list-group list-group-flush">
      <li id="row-db" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Target Database:</strong>
        <span id="ctx-db" class="badge bg-secondary font-monospace">&mdash;</span>
      </li>
      <li id="row-type" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Context Type:</strong>
        <span id="ctx-type" class="badge bg-light text-dark border font-monospace text-uppercase">&mdash;</span>
      </li>
      <li id="row-severity" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Alert Severity:</strong>
        <span id="ctx-severity" class="badge">&mdash;</span>
      </li>
      <li id="row-time" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Triggered Time:</strong>
        <span id="ctx-time" class="text-muted small">&mdash;</span>
      </li>
      <li id="row-details" class="list-group-item py-2 d-none">
        <strong id="label-details" class="d-block mb-1">Firing Context:</strong>
        <code id="ctx-details" class="d-block p-2 bg-light border rounded small" style="white-space: pre-wrap; word-break: break-all;">&mdash;</code>
      </li>
    </ul>
  </div>
</div>

<script>
  (function() {
    const params = new URLSearchParams(window.location.search);
    const metadata = params.get('metadata');
    if (!metadata) return;

    try {
      
      const base64 = metadata.replace(/-/g, '+').replace(/_/g, '/');
      const jsonStr = decodeURIComponent(escape(window.atob(base64)));
      const data = JSON.parse(jsonStr);

      if (data) {
        let hasData = false;

        if (data.db) {
          document.getElementById('ctx-db').textContent = data.db;
          document.getElementById('row-db').classList.remove('d-none');
          hasData = true;
        }

        if (data.type) {
          document.getElementById('ctx-type').textContent = data.type;
          document.getElementById('row-type').classList.remove('d-none');
          hasData = true;
        }

        if (data.severity) {
          const severityBadge = document.getElementById('ctx-severity');
          const severity = data.severity.toLowerCase();
          severityBadge.textContent = severity.toUpperCase();
          if (severity === 'critical') {
            severityBadge.className = 'badge bg-danger';
          } else if (severity === 'warning') {
            severityBadge.className = 'badge bg-warning text-dark';
          } else {
            severityBadge.className = 'badge bg-info';
          }
          document.getElementById('row-severity').classList.remove('d-none');
          hasData = true;
        }

        if (data.t) {
          const date = new Date(data.t * 1000);
          document.getElementById('ctx-time').textContent = date.toLocaleString();
          document.getElementById('row-time').classList.remove('d-none');
          hasData = true;
        }

        if (data.details) {
          document.getElementById('ctx-details').textContent = data.details;

          
          const labelDetails = document.getElementById('label-details');
          if (data.type === 'object') {
            labelDetails.textContent = 'Object Metadata Details:';
          } else if (data.type === 'report') {
            labelDetails.textContent = 'Report Description:';
          } else {
            labelDetails.textContent = 'Firing Context:';
          }

          document.getElementById('row-details').classList.remove('d-none');
          hasData = true;
        }

        if (hasData) {
          document.getElementById('pslens-context-panel').classList.remove('d-none');
        }
      }
    } catch (e) {
      console.error('Failed to parse operational context metadata:', e);
    }
  })();
</script>


## IB Node Security Audit Report

**Report ID:** `ib-node-security-audit`
**Category:** Integration Broker

## Purpose

This report audits Integration Broker node user accounts for security issues that go beyond authentication configuration (which is covered by the [Nodes with No Password](/docs/reports/security/security-nodes-no-password/) report). It checks for elevated user privileges on nodes, shared user accounts across multiple nodes, and nodes with no active routings.

## What It Detects

### CRITICAL — Anonymous Node User Analysis

Checks the ANONYMOUS node's associated PeopleSoft user account. Flags as HIGH RISK if:

- The user account is **unlocked** (can log into PIA directly)
- The user has **PeopleTools access** (Application Designer, Data Mover, etc.)

The ANONYMOUS node handles unauthenticated IB traffic. Its user should have minimal privileges and a locked account.

### WARNING — Shared Node Users

Identifies cases where the same PeopleSoft User ID (OPRID) is configured on multiple active nodes. Each node should have its own distinct service account for:

- **Audit trail** — Knowing which node performed an action
- **Security isolation** — Revoking one node's access without affecting others
- **Least privilege** — Tailoring permissions per node's specific needs

### WARNING — Active Nodes with No Active Routings

Finds active non-local nodes that have no active routings in PSIBRTNGDEFN. These nodes may be:

- Leftover from decommissioned integrations
- Candidates for deactivation to reduce attack surface
- Covered only by wildcard (`~~ANY~~`) routings (reported separately)

### WARNING — Node Users with PeopleTools Access

Identifies node service accounts whose permission lists grant access to PeopleTools clients (Application Designer, Data Mover, Query, etc.). Node accounts should never need development tool access.

## Tables Queried

### PSMSGNODEDEFN — Message Node Definitions

|    Field    |                 Description                 |            Values            |
| ----------- | ------------------------------------------- | ---------------------------- |
| MSGNODENAME | Node name (primary key)                     |                              |
| ACTIVE_NODE | Whether the node is active                  | `1` = Active, `0` = Inactive |
| LOCALNODE   | Whether this is a local node                | `1` = Local, `0` = External  |
| USERID      | PeopleSoft user ID associated with the node |                              |
| NODE_TYPE   | Node type                                   |                              |

### PSIBRTNGDEFN — Routing Definitions

|      Field       |     Description     |            Values            |
| ---------------- | ------------------- | ---------------------------- |
| SENDERNODENAME   | Sending node name   | Node name or `~~ANY~~`       |
| RECEIVERNODENAME | Receiving node name |                              |
| EFF_STATUS       | Effective status    | `A` = Active, `I` = Inactive |

### PSOPRDEFN — User Definitions

|  Field   |       Description       |            Values            |
| -------- | ----------------------- | ---------------------------- |
| OPRID    | User ID (primary key)   |                              |
| OPRCLASS | Primary permission list |                              |
| ACCTLOCK | Account lock status     | `0` = Unlocked, `1` = Locked |

### PSAUTHITEM — Menu/Tools Authorizations

Used to check if a permission list grants PeopleTools client access (APPLICATION_DESIGNER, DATA_MOVER, etc.).

## Data Flow

```text
1. Fetch ALL message nodes from PSMSGNODEDEFN
   (batches of 300)
        |
        v
2. Fetch ALL active routings from PSIBRTNGDEFN
   Build set of node names with active routings
        |
        v
3. For each unique UserID on active nodes:
   Look up user in PSOPRDEFN
        |
        v
4. For each unique permission list found:
   Check PSAUTHITEM for PeopleTools access
        |
        v
5. Analyze and categorize findings:
   - Anonymous node user privileges
   - Shared node users (same OPRID on 2+ nodes)
   - Active nodes not in routing coverage set
   - Node users with PeopleTools access
        |
        v
6. Generate Markdown report grouped by severity
```

## Interpreting Results

- **CRITICAL findings on the ANONYMOUS node** indicate that unauthenticated IB traffic is processed under a user with elevated privileges. This is a significant security risk.
- **Shared node users** increase blast radius if one account is compromised and make it harder to trace which node performed specific actions.
- **Nodes with no routings** represent unnecessary attack surface. If a node isn't routing any messages, it should be deactivated.
- **PeopleTools access on node accounts** means a compromised integration could potentially be used to modify PeopleSoft objects.

## Recommendations

1. Lock the ANONYMOUS node user account to prevent direct PIA login
2. Remove PeopleTools access from node service account permission lists
3. Create distinct service accounts for each Integration Broker node
4. Deactivate nodes with no active routings if they are no longer needed
