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