- Infoblox WAPI integration (read-only) for resolving IPs to FQDNs - backend/helpers/infobloxApi.js — Basic auth, host/PTR/IPv6 lookups - backend/routes/infoblox.js — /api/infoblox endpoints - Globe icon on Reporting page hostName/dns columns for one-click DNS lookup - Pending: WAPI credentials with API access permissions - Scan Posture page for Access Ops platform/version tracking - backend/routes/scanPosture.js - frontend/src/components/pages/ScanPosturePage.js - Page visibility and nav drawer entries - Ivanti findings OS field enrichment - Migration to add os_name/os_class/os_version columns - Backfill script for existing findings - ivantiFindings route updates to persist OS data on sync
153 lines
5.5 KiB
JavaScript
153 lines
5.5 KiB
JavaScript
// Infoblox DNS Lookup Routes (read-only)
|
|
// Provides reverse DNS lookups via the Infoblox WAPI proxy at api-proxy-ease.charterlab.com.
|
|
// Used by the Reporting page to resolve IPs to their authoritative FQDN.
|
|
|
|
const express = require('express');
|
|
const { requireAuth, requireGroup } = require('../middleware/auth');
|
|
const {
|
|
isConfigured,
|
|
missingVars,
|
|
lookupAnyByIp,
|
|
lookupByIpv6,
|
|
lookupByName,
|
|
} = require('../helpers/infobloxApi');
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Router factory
|
|
// ---------------------------------------------------------------------------
|
|
function createInfobloxRouter() {
|
|
const router = express.Router();
|
|
|
|
// All Infoblox routes require auth + Admin or Standard_User
|
|
router.use(requireAuth(), requireGroup('Admin', 'Standard_User'));
|
|
|
|
/**
|
|
* GET /status
|
|
*
|
|
* Returns whether the Infoblox integration is configured.
|
|
*/
|
|
router.get('/status', (_req, res) => {
|
|
if (!isConfigured) {
|
|
return res.status(503).json({ configured: false, error: 'Infoblox API is not configured.', missingVars });
|
|
}
|
|
return res.json({ configured: true });
|
|
});
|
|
|
|
/**
|
|
* GET /lookup/ip/:ip
|
|
*
|
|
* Look up host records by IPv4 address. Tries host record first, then
|
|
* falls back to PTR record (many IPs only have PTR in Infoblox).
|
|
*
|
|
* @param {string} ip - IPv4 address to look up
|
|
* @response 200 - { records: [...], source: 'host'|'ptr' }
|
|
* @response 404 - { error: string } — no records found
|
|
* @response 503 - not configured
|
|
*/
|
|
router.get('/lookup/ip/:ip', async (req, res) => {
|
|
if (!isConfigured) {
|
|
return res.status(503).json({ error: 'Infoblox API is not configured.', missingVars });
|
|
}
|
|
|
|
const { ip } = req.params;
|
|
if (!ip || !ip.trim()) {
|
|
return res.status(400).json({ error: 'IP address is required.' });
|
|
}
|
|
|
|
try {
|
|
const result = await lookupAnyByIp(ip.trim());
|
|
if (!result.ok) {
|
|
return res.status(502).json({ error: 'Infoblox lookup failed.', details: result.error });
|
|
}
|
|
if (result.records.length === 0) {
|
|
return res.status(404).json({ error: `No host or PTR records found for IP ${ip}.` });
|
|
}
|
|
return res.json({ records: result.records, source: result.source });
|
|
} catch (err) {
|
|
console.error('[infoblox]', err.message);
|
|
if (err.message.includes('timed out')) {
|
|
return res.status(504).json({ error: 'Infoblox request timed out.' });
|
|
}
|
|
return res.status(502).json({ error: 'Infoblox request failed.', details: err.message });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /lookup/ipv6/:ip
|
|
*
|
|
* Look up host records by IPv6 address.
|
|
*
|
|
* @param {string} ip - IPv6 address to look up
|
|
* @response 200 - { records: [...] }
|
|
* @response 404 - no records found
|
|
*/
|
|
router.get('/lookup/ipv6/:ip', async (req, res) => {
|
|
if (!isConfigured) {
|
|
return res.status(503).json({ error: 'Infoblox API is not configured.', missingVars });
|
|
}
|
|
|
|
const { ip } = req.params;
|
|
if (!ip || !ip.trim()) {
|
|
return res.status(400).json({ error: 'IPv6 address is required.' });
|
|
}
|
|
|
|
try {
|
|
const result = await lookupByIpv6(ip.trim());
|
|
if (!result.ok) {
|
|
return res.status(result.status || 502).json({ error: 'Infoblox lookup failed.', details: result.error });
|
|
}
|
|
if (result.records.length === 0) {
|
|
return res.status(404).json({ error: `No host records found for IPv6 ${ip}.` });
|
|
}
|
|
return res.json({ records: result.records });
|
|
} catch (err) {
|
|
console.error('[infoblox]', err.message);
|
|
if (err.message.includes('timed out')) {
|
|
return res.status(504).json({ error: 'Infoblox request timed out.' });
|
|
}
|
|
return res.status(502).json({ error: 'Infoblox request failed.', details: err.message });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /lookup/name/:fqdn
|
|
*
|
|
* Look up host records by FQDN (exact match).
|
|
*
|
|
* @param {string} fqdn - Fully qualified domain name
|
|
* @response 200 - { records: [...] }
|
|
* @response 404 - no records found
|
|
*/
|
|
router.get('/lookup/name/:fqdn', async (req, res) => {
|
|
if (!isConfigured) {
|
|
return res.status(503).json({ error: 'Infoblox API is not configured.', missingVars });
|
|
}
|
|
|
|
const { fqdn } = req.params;
|
|
if (!fqdn || !fqdn.trim()) {
|
|
return res.status(400).json({ error: 'FQDN is required.' });
|
|
}
|
|
|
|
try {
|
|
const result = await lookupByName(fqdn.trim());
|
|
if (!result.ok) {
|
|
return res.status(result.status || 502).json({ error: 'Infoblox lookup failed.', details: result.error });
|
|
}
|
|
if (result.records.length === 0) {
|
|
return res.status(404).json({ error: `No host records found for ${fqdn}.` });
|
|
}
|
|
return res.json({ records: result.records });
|
|
} catch (err) {
|
|
console.error('[infoblox]', err.message);
|
|
if (err.message.includes('timed out')) {
|
|
return res.status(504).json({ error: 'Infoblox request timed out.' });
|
|
}
|
|
return res.status(502).json({ error: 'Infoblox request failed.', details: err.message });
|
|
}
|
|
});
|
|
|
|
return router;
|
|
}
|
|
|
|
module.exports = createInfobloxRouter;
|