mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Fleet] Add tamper protection toggle in agent policy settings (#157335)
## Summary - [x] Adds a section to allow Agent tampering in Agent Policy > Settings - [x] Link to open Uninstall command flyout is enabled when Agent tampering is switched on - [x] Agent tampering section is only visible if the license is platinum or above - [x] Unit tests Add this to your kibana.dev.yml for testing ``` xpack.fleet.enableExperimental: - agentTamperProtectionEnabled ``` ## Screenshots Toggle is hidden if license is below platinum  Link is disabled  Link is enabled when switch is toggled on  With Flyout opened 
This commit is contained in:
parent
9910615a3d
commit
3d5d1a99e7
3 changed files with 175 additions and 2 deletions
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { waitFor, fireEvent, act } from '@testing-library/react';
|
||||
import type { RenderResult } from '@testing-library/react';
|
||||
|
||||
import { createFleetTestRendererMock } from '../../../../../../mock';
|
||||
import type { TestRenderer } from '../../../../../../mock';
|
||||
|
||||
import { allowedExperimentalValues } from '../../../../../../../common/experimental_features';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../../../../services/experimental_features';
|
||||
|
||||
import type { NewAgentPolicy, AgentPolicy } from '../../../../../../../common/types';
|
||||
|
||||
import { useLicense } from '../../../../../../hooks/use_license';
|
||||
|
||||
import type { LicenseService } from '../../../../../../../common/services';
|
||||
|
||||
import type { ValidationResults } from '../agent_policy_validation';
|
||||
|
||||
import { AgentPolicyAdvancedOptionsContent } from '.';
|
||||
|
||||
jest.mock('../../../../../../hooks/use_license');
|
||||
|
||||
const mockedUseLicence = useLicense as jest.MockedFunction<typeof useLicense>;
|
||||
|
||||
describe('Agent policy advanced options content', () => {
|
||||
let testRender: TestRenderer;
|
||||
let renderResult: RenderResult;
|
||||
|
||||
const mockAgentPolicy: Partial<NewAgentPolicy | AgentPolicy> = {
|
||||
id: 'agent-policy-1',
|
||||
name: 'some-agent-policy',
|
||||
is_managed: false,
|
||||
is_protected: false,
|
||||
};
|
||||
|
||||
const mockUpdateAgentPolicy = jest.fn();
|
||||
const mockValidation = jest.fn() as unknown as ValidationResults;
|
||||
const usePlatinumLicense = () =>
|
||||
mockedUseLicence.mockReturnValue({
|
||||
hasAtLeast: () => true,
|
||||
isPlatinum: () => true,
|
||||
} as unknown as LicenseService);
|
||||
|
||||
const render = () => {
|
||||
// remove when feature flag is removed
|
||||
ExperimentalFeaturesService.init({
|
||||
...allowedExperimentalValues,
|
||||
agentTamperProtectionEnabled: true,
|
||||
});
|
||||
|
||||
renderResult = testRender.render(
|
||||
<AgentPolicyAdvancedOptionsContent
|
||||
agentPolicy={mockAgentPolicy}
|
||||
updateAgentPolicy={mockUpdateAgentPolicy}
|
||||
validation={mockValidation}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
testRender = createFleetTestRendererMock();
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('Agent tamper protection toggle', () => {
|
||||
it('should be visible if license is at least platinum', () => {
|
||||
usePlatinumLicense();
|
||||
render();
|
||||
expect(renderResult.queryByTestId('tamperProtectionSwitch')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not be visible if license is below platinum', () => {
|
||||
mockedUseLicence.mockReturnValueOnce({
|
||||
isPlatinum: () => false,
|
||||
hasAtLeast: () => false,
|
||||
} as unknown as LicenseService);
|
||||
render();
|
||||
expect(renderResult.queryByTestId('tamperProtectionSwitch')).not.toBeInTheDocument();
|
||||
});
|
||||
it('switched to true enables the uninstall command link', async () => {
|
||||
usePlatinumLicense();
|
||||
render();
|
||||
await act(async () => {
|
||||
fireEvent.click(renderResult.getByTestId('tamperProtectionSwitch'));
|
||||
});
|
||||
waitFor(() => {
|
||||
expect(renderResult.getByTestId('tamperProtectionSwitch')).toBeChecked();
|
||||
expect(renderResult.getByTestId('uninstallCommandLink')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
it('switched to false disables the uninstall command link', () => {
|
||||
usePlatinumLicense();
|
||||
render();
|
||||
expect(renderResult.getByTestId('tamperProtectionSwitch')).not.toBeChecked();
|
||||
expect(renderResult.getByTestId('uninstallCommandLink')).toBeDisabled();
|
||||
});
|
||||
it('should update agent policy when switched on', async () => {
|
||||
usePlatinumLicense();
|
||||
render();
|
||||
await act(async () => {
|
||||
(await renderResult.findByTestId('tamperProtectionSwitch')).click();
|
||||
});
|
||||
expect(mockUpdateAgentPolicy).toHaveBeenCalledWith({ is_protected: true });
|
||||
});
|
||||
});
|
||||
});
|
|
@ -25,6 +25,7 @@ import {
|
|||
EuiFlexItem,
|
||||
EuiBetaBadge,
|
||||
EuiBadge,
|
||||
EuiSwitch,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -35,14 +36,15 @@ import {
|
|||
DEFAULT_MAX_AGENT_POLICIES_WITH_INACTIVITY_TIMEOUT,
|
||||
} from '../../../../../../../common/constants';
|
||||
import type { NewAgentPolicy, AgentPolicy } from '../../../../types';
|
||||
import { useStartServices, useConfig, useGetAgentPolicies } from '../../../../hooks';
|
||||
import { useStartServices, useConfig, useGetAgentPolicies, useLicense } from '../../../../hooks';
|
||||
|
||||
import { AgentPolicyPackageBadge } from '../../../../components';
|
||||
import { UninstallCommandFlyout } from '../../../../../../components';
|
||||
|
||||
import { AgentPolicyDeleteProvider } from '../agent_policy_delete_provider';
|
||||
import type { ValidationResults } from '../agent_policy_validation';
|
||||
|
||||
import { policyHasFleetServer } from '../../../../services';
|
||||
import { ExperimentalFeaturesService, policyHasFleetServer } from '../../../../services';
|
||||
|
||||
import {
|
||||
useOutputOptions,
|
||||
|
@ -101,6 +103,10 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
|
|||
'package_policies' in agentPolicy &&
|
||||
agentPolicy?.package_policies?.some((packagePolicy) => packagePolicy.is_managed);
|
||||
|
||||
const { agentTamperProtectionEnabled } = ExperimentalFeaturesService.get();
|
||||
const licenseService = useLicense();
|
||||
const [isUninstallCommandFlyoutOpen, setIsUninstallCommandFlyoutOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiDescribedFormGroup
|
||||
|
@ -119,6 +125,13 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
|
|||
/>
|
||||
}
|
||||
>
|
||||
{isUninstallCommandFlyoutOpen && (
|
||||
<UninstallCommandFlyout
|
||||
target="agent"
|
||||
policyId={agentPolicy.id}
|
||||
onClose={() => setIsUninstallCommandFlyoutOpen(false)}
|
||||
/>
|
||||
)}
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
key="description"
|
||||
|
@ -285,6 +298,48 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
|
|||
}}
|
||||
/>
|
||||
</EuiDescribedFormGroup>
|
||||
{agentTamperProtectionEnabled && licenseService.isPlatinum() && (
|
||||
<EuiDescribedFormGroup
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentPolicyForm.tamperingLabel"
|
||||
defaultMessage="Agent tamper protection"
|
||||
/>
|
||||
</h4>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentPolicyForm.tamperingDescription"
|
||||
defaultMessage="Prevent agents from being uninstalled locally. When enabled, agents can only be uninstalled using an authorization token in the uninstall command. Click { linkName } for the full command."
|
||||
values={{ linkName: <strong>Get uninstall command</strong> }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiSwitch
|
||||
label={i18n.translate('xpack.fleet.agentPolicyForm.tamperingSwitchLabel', {
|
||||
defaultMessage: 'Prevent agent tampering',
|
||||
})}
|
||||
checked={agentPolicy.is_protected ?? false}
|
||||
onChange={(e) => {
|
||||
updateAgentPolicy({ is_protected: e.target.checked });
|
||||
}}
|
||||
data-test-subj="tamperProtectionSwitch"
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiLink
|
||||
onClick={() => {
|
||||
setIsUninstallCommandFlyoutOpen(true);
|
||||
}}
|
||||
disabled={agentPolicy.is_protected === false}
|
||||
data-test-subj="uninstallCommandLink"
|
||||
>
|
||||
{i18n.translate('xpack.fleet.agentPolicyForm.tamperingUninstallLink', {
|
||||
defaultMessage: 'Get uninstall command',
|
||||
})}
|
||||
</EuiLink>
|
||||
</EuiDescribedFormGroup>
|
||||
)}
|
||||
<EuiDescribedFormGroup
|
||||
title={
|
||||
<h4>
|
||||
|
|
|
@ -53,6 +53,7 @@ const pickAgentPolicyKeysToSend = (agentPolicy: AgentPolicy) =>
|
|||
'download_source_id',
|
||||
'fleet_server_host_id',
|
||||
'agent_features',
|
||||
'is_protected',
|
||||
]);
|
||||
|
||||
const FormWrapper = styled.div`
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue