From ef9fffe0946b1a1b29ba5c6f455b91d149cfa4b9 Mon Sep 17 00:00:00 2001 From: Paul Richardson Date: Tue, 15 Oct 2024 22:17:46 +0100 Subject: [PATCH] fix: All responses from gateway should have a json header * jolokia-agent.ts * Without the json header, all responses return with a content-type of text/html and the response body is blank when built and installed * Mandating a json header ensures the jolokia data is correctly populated in the response body * rbac.ts * Adds tracing for debugging purposes * gateway-api.ts * Adds a custom limit to each of the gateway endpoints * proxy-dev-server.js * Fixes the logger due to no longer using express-pino-logger --- docker/gateway/proxy-dev-server.js | 8 +++--- docker/gateway/src/gateway-api.ts | 5 ++-- .../src/jolokia-agent/jolokia-agent.ts | 5 ++++ docker/gateway/src/jolokia-agent/rbac.ts | 26 ++++++++++++++++--- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/docker/gateway/proxy-dev-server.js b/docker/gateway/proxy-dev-server.js index e8c99a23..78a11ebf 100644 --- a/docker/gateway/proxy-dev-server.js +++ b/docker/gateway/proxy-dev-server.js @@ -1,10 +1,12 @@ const express = require('express') const { createProxyMiddleware } = require('http-proxy-middleware') const pino = require('pino') -const expressPinoLogger = require('express-pino-logger') +const pinoHttpLogger = require('pino-http') -const logger = pino({ level: process.env.LOG_LEVEL || 'info' }) -const expressLogger = expressPinoLogger(logger) +const level = process.env.LOG_LEVEL || 'info' + +const logger = pino({ level: level }) +const expressLogger = pinoHttpLogger({ logger: logger }) const app = express() app.use(expressLogger) diff --git a/docker/gateway/src/gateway-api.ts b/docker/gateway/src/gateway-api.ts index e05f36aa..053f7133 100644 --- a/docker/gateway/src/gateway-api.ts +++ b/docker/gateway/src/gateway-api.ts @@ -134,7 +134,7 @@ gatewayServer .get((req, res) => { proxyJolokiaAgent(req, res, gatewayOptions) }) - .post(express.json({ type: '*/json', strict: false }), (req, res) => { + .post(express.json({ type: '*/json', limit: '50mb', strict: false }), (req, res) => { proxyJolokiaAgent(req, res, gatewayOptions) }) @@ -156,7 +156,8 @@ gatewayServer.route('*').all((req, res) => { * * Needs to be added last to avoid being overwritten by the proxy middleware */ -gatewayServer.use(express.json({ type: '*/json', strict: false })) +gatewayServer.use(express.json({ type: '*/json', limit: '50mb', strict: false })) +gatewayServer.use(express.urlencoded({ extended: false })) /* * Exports the running server for use in unit testing diff --git a/docker/gateway/src/jolokia-agent/jolokia-agent.ts b/docker/gateway/src/jolokia-agent/jolokia-agent.ts index 7d45a637..4d0e94b1 100644 --- a/docker/gateway/src/jolokia-agent/jolokia-agent.ts +++ b/docker/gateway/src/jolokia-agent/jolokia-agent.ts @@ -57,6 +57,11 @@ function response(agentInfo: AgentInfo, res: SimpleResponse) { agentInfo.response.removeHeader('www-authenticate') } + /* + * Ensure that the response content-type is json + */ + agentInfo.response.setHeader('content-type', 'application/json') + agentInfo.response.status(res.status).send(res.body) } diff --git a/docker/gateway/src/jolokia-agent/rbac.ts b/docker/gateway/src/jolokia-agent/rbac.ts index bbdfe7e7..f4b7a9e3 100644 --- a/docker/gateway/src/jolokia-agent/rbac.ts +++ b/docker/gateway/src/jolokia-agent/rbac.ts @@ -24,6 +24,7 @@ import { isOptimisedMBeanInfo, } from './globals' import { isRecord, toStringArray } from '../utils' +import { logger } from '../logger' interface JmxUnionRequest { type: string @@ -511,6 +512,8 @@ export function check(request: MBeanRequest, role: string) { } function checkACLs(role: string, jolokia: JmxUnionRequest) { + logger.trace(`Checking ACLs for role ${role} on request ${jolokia.type}`) + let rbac // lookup ACL by domain and type if (jolokia.properties && jolokia.properties.type) { @@ -536,6 +539,8 @@ function checkACLs(role: string, jolokia: JmxUnionRequest) { } function checkACL(role: string, jolokia: JmxUnionRequest, name: string) { + logger.trace(`Checking ACL for role ${role} on request ${jolokia.type} named ${name}`) + const acl = ACL[name] if (!acl) { return null @@ -553,10 +558,17 @@ function checkACL(role: string, jolokia: JmxUnionRequest, name: string) { member = jolokia.type.toLowerCase() } + logger.trace(`Checking ACL member: ${member}`) + if (Array.isArray(acl)) { + logger.trace(`Checking ACL isArray: ${acl}`) + const entry = acl .map(a => Object.entries(a)[0]) - .find(e => e[0] === member || (regex.test(e[0]) && new RegExp(e[0].slice(1, -1)).test(member))) + .find(e => { + if (e[0] === member) return true + return regex.test(e[0]) && new RegExp(e[0].slice(1, -1)).test(member) + }) if (entry) { return checkRoles(role, jolokia, name, entry[0], entry[1]) } @@ -591,8 +603,16 @@ function checkACL(role: string, jolokia: JmxUnionRequest, name: string) { } function checkRoles(role: string, jolokia: JmxUnionRequest, name: string, key: string, roles: unknown) { - const allowed = { allowed: true, reason: `Role '${role}' allowed by '${name}[${key}]: ${roles}'` } - const denied = { allowed: false, reason: `Role '${role}' denied by '${name}[${key}]: ${roles}'` } + logger.trace(`CheckRoles role: ${role} name: ${name} key: ${key} roles ${roles} jolokia {type: ${jolokia.type}}`) + + const allowed = { + allowed: true, + reason: `Role '${role}' allowed by '${name}[${key}]: ${roles}' for request {type: ${jolokia.type}, domain: ${jolokia.domain}}`, + } + const denied = { + allowed: false, + reason: `Role '${role}' denied by '${name}[${key}]: ${roles}' for request {type: ${jolokia.type}, domain: ${jolokia.domain}}`, + } if (typeof roles === 'string') { roles = roles