Skip to content

Commit

Permalink
[Stack Monitoring] Verify remote cluster client role when CCS is enab…
Browse files Browse the repository at this point in the history
…led (#140738)

* [Stack Monitoring] Verify remote cluster client role when CCS is enabled (#129546)

* Only show UI hint if CCS is enabled

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
miltonhultgren and kibanamachine authored Sep 15, 2022
1 parent 3076f23 commit f014ca4
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface ExternalConfig {
showCgroupMetricsLogstash: boolean;
renderReactApp: boolean;
staleStatusThresholdSeconds: number;
isCcsEnabled: boolean;
}

export const ExternalConfigContext = createContext({} as ExternalConfig);
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useState } from 'react';
import React, { useContext, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiPanel, EuiCallOut, EuiButton } from '@elastic/eui';
Expand All @@ -14,8 +14,10 @@ import { Redirect } from 'react-router-dom';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { ComponentProps } from '../../route_init';
import { MonitoringStartPluginDependencies } from '../../../types';
import { ExternalConfigContext } from '../../contexts/external_config_context';

export const AccessDeniedPage: React.FC<ComponentProps> = () => {
const { isCcsEnabled } = useContext(ExternalConfigContext);
const { services } = useKibana<MonitoringStartPluginDependencies>();
const [hasAccess, setHasAccess] = useState<boolean>(false);

Expand Down Expand Up @@ -62,6 +64,14 @@ export const AccessDeniedPage: React.FC<ComponentProps> = () => {
the monitoring cluster."
/>
</p>
{isCcsEnabled && (
<p>
<FormattedMessage
id="xpack.monitoring.accessDenied.noRemoteClusterClientDescription"
defaultMessage="Since Cross Cluster Search is enabled (`monitoring.ui.ccs.enabled` is set to `true`), make sure your cluster has the `remote_cluster_client` role on at least one node."
/>
</p>
)}
<p>
<EuiButton href="../app/home">
<FormattedMessage
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/monitoring/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export class MonitoringPlugin
'staleStatusThresholdSeconds',
monitoring.ui.kibana.reporting.stale_status_threshold_seconds,
],
['isCcsEnabled', monitoring.ui.ccs.enabled],
];
}

Expand Down
13 changes: 10 additions & 3 deletions x-pack/plugins/monitoring/server/lib/errors/auth_errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { forbidden } from '@hapi/boom';
import { i18n } from '@kbn/i18n';
import { getStatusCode } from './handle_error';
import { ErrorTypes } from '../../types';
import { NO_REMOTE_CLIENT_ROLE_ERROR } from '../../routes/api/v1/check_access/check_access';

export function isAuthError(err: ErrorTypes) {
const statusCode = getStatusCode(err);
Expand All @@ -30,9 +31,15 @@ export function handleAuthError(err: ErrorTypes) {
defaultMessage: 'Invalid authentication for monitoring cluster',
});
} else {
message = i18n.translate('xpack.monitoring.errors.insufficientUserErrorMessage', {
defaultMessage: 'Insufficient user permissions for monitoring data',
});
if (err.message === NO_REMOTE_CLIENT_ROLE_ERROR) {
message = i18n.translate('xpack.monitoring.errors.noRemoteClientRoleErrorMessage', {
defaultMessage: 'Cluster has no remote_cluster_client role',
});
} else {
message = i18n.translate('xpack.monitoring.errors.insufficientUserErrorMessage', {
defaultMessage: 'Insufficient user permissions for monitoring data',
});
}
}

return forbidden(message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import Boom from '@hapi/boom';
import { verifyMonitoringAuth } from '../../../../lib/elasticsearch/verify_monitoring_auth';
import { handleError } from '../../../../lib/errors';
import { LegacyRequest, MonitoringCore } from '../../../../types';
Expand All @@ -24,6 +25,11 @@ export function checkAccessRoute(server: MonitoringCore) {
const response: { has_access?: boolean } = {};
try {
await verifyMonitoringAuth(req);

if (server.config.ui.ccs.enabled) {
await verifyClusterHasRemoteClusterClientRole(req);
}

response.has_access = true; // response data is ignored
} catch (err) {
throw handleError(err, req);
Expand All @@ -32,3 +38,30 @@ export function checkAccessRoute(server: MonitoringCore) {
},
});
}

interface NodesResponse {
nodes: {
[uuid: string]: {
roles: string[];
};
};
}

export const NO_REMOTE_CLIENT_ROLE_ERROR = 'Cluster has no remote_cluster_client role';

async function verifyClusterHasRemoteClusterClientRole(req: LegacyRequest) {
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');

const response: NodesResponse = await callWithRequest(req, 'transport.request', {
method: 'GET',
path: '/_nodes',
});

for (const node of Object.values(response.nodes)) {
if (node.roles.includes('remote_cluster_client')) {
return;
}
}

throw Boom.forbidden(NO_REMOTE_CLIENT_ROLE_ERROR);
}

0 comments on commit f014ca4

Please sign in to comment.