mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Fleet] Introduce new config setting xpack.fleet.agentless.isDefault
to set agentless deployment by default (#216535)
## Summary Introduce a new fleet setting `xpack.fleet.agentless.isDefault` for defaulting the deployment mode to agentless and highlighting the agentless deployment mode as `Recommended` for the AI4DSOC project. ## Screens recordings AI4DSOC: https://github.com/user-attachments/assets/1fe6df6b-29e0-492c-955e-006e73673322 Otherwise: https://github.com/user-attachments/assets/e803df49-cbbb-4889-bef1-422abbd6df53 Relates: https://github.com/elastic/security-team/issues/11789
This commit is contained in:
parent
5402d2b90e
commit
7d3f672f2e
10 changed files with 185 additions and 18 deletions
|
@ -7,3 +7,6 @@ xpack.features.overrides:
|
|||
### The following features are Security features hidden in Role management UI for this specific tier.
|
||||
securitySolutionTimeline.hidden: true
|
||||
securitySolutionNotes.hidden: true
|
||||
|
||||
## Agentless deployment by default
|
||||
xpack.fleet.agentless.isDefault: true
|
|
@ -260,6 +260,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
|
|||
'xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled (boolean?)',
|
||||
'xpack.fleet.agents.enabled (boolean?)',
|
||||
'xpack.fleet.agentless.enabled (boolean?)',
|
||||
'xpack.fleet.agentless.isDefault (boolean?)',
|
||||
'xpack.fleet.enableExperimental (array?)',
|
||||
'xpack.fleet.internal.activeAgentsSoftLimit (number?)',
|
||||
'xpack.fleet.internal.fleetServerStandalone (boolean?)',
|
||||
|
|
|
@ -32,6 +32,7 @@ export interface FleetConfigType {
|
|||
};
|
||||
agentless?: {
|
||||
enabled: boolean;
|
||||
isDefault?: boolean;
|
||||
api?: {
|
||||
url?: string;
|
||||
tls?: {
|
||||
|
|
|
@ -360,6 +360,7 @@ describe('PackagePolicyInputPanel', () => {
|
|||
beforeEach(() => {
|
||||
useAgentlessMock.mockReturnValue({
|
||||
isAgentlessEnabled: true,
|
||||
isAgentlessDefault: false,
|
||||
isAgentlessAgentPolicy: jest.fn(),
|
||||
isAgentlessIntegration: jest.fn(),
|
||||
});
|
||||
|
@ -392,6 +393,7 @@ describe('PackagePolicyInputPanel', () => {
|
|||
beforeEach(() => {
|
||||
useAgentlessMock.mockReturnValue({
|
||||
isAgentlessEnabled: false,
|
||||
isAgentlessDefault: false,
|
||||
isAgentlessAgentPolicy: jest.fn(),
|
||||
isAgentlessIntegration: jest.fn(),
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
EuiRadioGroup,
|
||||
EuiDescribedFormGroup,
|
||||
EuiSpacer,
|
||||
EuiBadge,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { SetupTechnology } from '../../../../../types';
|
||||
|
@ -25,11 +26,13 @@ export const SetupTechnologySelector = ({
|
|||
allowedSetupTechnologies,
|
||||
setupTechnology,
|
||||
onSetupTechnologyChange,
|
||||
isAgentlessDefault,
|
||||
}: {
|
||||
disabled: boolean;
|
||||
allowedSetupTechnologies: SetupTechnology[];
|
||||
setupTechnology: SetupTechnology;
|
||||
onSetupTechnologyChange: (value: SetupTechnology) => void;
|
||||
isAgentlessDefault: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<EuiDescribedFormGroup
|
||||
|
@ -64,12 +67,21 @@ export const SetupTechnologySelector = ({
|
|||
id="xpack.fleet.setupTechnology.agentlessInputDisplay"
|
||||
defaultMessage="Agentless"
|
||||
/>{' '}
|
||||
<EuiBetaBadge
|
||||
label="Beta"
|
||||
size="s"
|
||||
tooltipContent="This module is not yet GA. Please help us by reporting any bugs."
|
||||
alignment="middle"
|
||||
/>
|
||||
{isAgentlessDefault ? (
|
||||
<EuiBadge>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.setupTechnology.agentlessDeployment.recommendedBadge"
|
||||
defaultMessage="Recommended"
|
||||
/>
|
||||
</EuiBadge>
|
||||
) : (
|
||||
<EuiBetaBadge
|
||||
label="Beta"
|
||||
size="s"
|
||||
tooltipContent="This module is not yet GA. Please help us by reporting any bugs."
|
||||
alignment="middle"
|
||||
/>
|
||||
)}
|
||||
</strong>
|
||||
<EuiText size="s">
|
||||
<p>
|
||||
|
|
|
@ -110,6 +110,82 @@ describe('useAgentless', () => {
|
|||
|
||||
expect(result.current.isAgentlessEnabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return isAgentlessDefault as falsey when agentless is disabled and isDefault is true', () => {
|
||||
(useStartServices as MockFn).mockReturnValue({
|
||||
cloud: {
|
||||
isServerlessEnabled: true,
|
||||
isCloudEnabled: false,
|
||||
},
|
||||
});
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
enabled: false,
|
||||
isDefault: true,
|
||||
},
|
||||
} as any);
|
||||
|
||||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessDefault).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return isAgentlessDefault as falsey when agentless is enabled and isDefault is false', () => {
|
||||
(useStartServices as MockFn).mockReturnValue({
|
||||
cloud: {
|
||||
isServerlessEnabled: true,
|
||||
isCloudEnabled: false,
|
||||
},
|
||||
});
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
enabled: true,
|
||||
isDefault: false,
|
||||
},
|
||||
} as any);
|
||||
|
||||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessDefault).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return isAgentlessDefault as truthy when agentless is enabled and isDefault is true', () => {
|
||||
(useStartServices as MockFn).mockReturnValue({
|
||||
cloud: {
|
||||
isServerlessEnabled: true,
|
||||
isCloudEnabled: false,
|
||||
},
|
||||
});
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
enabled: true,
|
||||
isDefault: true,
|
||||
},
|
||||
} as any);
|
||||
|
||||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessDefault).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return isAgentlessDefault as falsey when agentless is enabled and isDefault is true, but serverless and cloud disabled', () => {
|
||||
(useStartServices as MockFn).mockReturnValue({
|
||||
cloud: {
|
||||
isServerlessEnabled: false,
|
||||
isCloudEnabled: false,
|
||||
},
|
||||
});
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
enabled: true,
|
||||
isDefault: true,
|
||||
},
|
||||
} as any);
|
||||
|
||||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessDefault).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useSetupTechnology', () => {
|
||||
|
@ -297,6 +373,62 @@ describe('useSetupTechnology', () => {
|
|||
expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED);
|
||||
});
|
||||
|
||||
it('should be agentless when agentless is enabled and isDefault is true', () => {
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
enabled: true,
|
||||
isDefault: true,
|
||||
api: {
|
||||
url: 'https://agentless.api.url',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useSetupTechnology({
|
||||
setNewAgentPolicy,
|
||||
newAgentPolicy: newAgentPolicyMock,
|
||||
setSelectedPolicyTab: setSelectedPolicyTabMock,
|
||||
packagePolicy: packagePolicyMock,
|
||||
updatePackagePolicy: updatePackagePolicyMock,
|
||||
packageInfo: {
|
||||
policy_templates: [
|
||||
{
|
||||
name: 'template1',
|
||||
title: 'Template 1',
|
||||
deployment_modes: {
|
||||
default: {
|
||||
enabled: true,
|
||||
},
|
||||
agentless: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'template2',
|
||||
title: 'Template 2',
|
||||
deployment_modes: {
|
||||
default: {
|
||||
enabled: true,
|
||||
},
|
||||
agentless: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
} as PackageInfo,
|
||||
})
|
||||
);
|
||||
|
||||
expect(result.current.allowedSetupTechnologies).toStrictEqual([
|
||||
SetupTechnology.AGENTLESS,
|
||||
SetupTechnology.AGENT_BASED,
|
||||
]);
|
||||
expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENTLESS);
|
||||
});
|
||||
|
||||
it('should be agent-based when agentless is enabled and selected integration is agent-based by default', () => {
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
|
|
|
@ -38,6 +38,7 @@ export const useAgentless = () => {
|
|||
const isCloud = !!cloud?.isCloudEnabled;
|
||||
|
||||
const isAgentlessEnabled = (isCloud || isServerless) && config.agentless?.enabled === true;
|
||||
const isAgentlessDefault = isAgentlessEnabled && config.agentless?.isDefault === true;
|
||||
|
||||
const isAgentlessAgentPolicy = (agentPolicy: AgentPolicy | undefined) => {
|
||||
if (!agentPolicy) return false;
|
||||
|
@ -54,6 +55,7 @@ export const useAgentless = () => {
|
|||
|
||||
return {
|
||||
isAgentlessEnabled,
|
||||
isAgentlessDefault,
|
||||
isAgentlessAgentPolicy,
|
||||
isAgentlessIntegration,
|
||||
};
|
||||
|
@ -80,7 +82,7 @@ export function useSetupTechnology({
|
|||
agentPolicies?: AgentPolicy[];
|
||||
integrationToEnable?: string;
|
||||
}) {
|
||||
const { isAgentlessEnabled } = useAgentless();
|
||||
const { isAgentlessEnabled, isAgentlessDefault } = useAgentless();
|
||||
|
||||
// this is a placeholder for the new agent-BASED policy that will be used when the user switches from agentless to agent-based and back
|
||||
const orginalAgentPolicyRef = useRef<NewAgentPolicy>({ ...newAgentPolicy });
|
||||
|
@ -102,12 +104,12 @@ export function useSetupTechnology({
|
|||
const shouldBeDefault =
|
||||
isAgentlessEnabled &&
|
||||
(isOnlyAgentlessIntegration(packageInfo, integrationToEnable) ||
|
||||
isAgentlessSetupDefault(packageInfo, integrationToEnable))
|
||||
isAgentlessSetupDefault(isAgentlessDefault, packageInfo, integrationToEnable))
|
||||
? SetupTechnology.AGENTLESS
|
||||
: SetupTechnology.AGENT_BASED;
|
||||
setDefaultSetupTechnology(shouldBeDefault);
|
||||
setSelectedSetupTechnology(shouldBeDefault);
|
||||
}, [isAgentlessEnabled, packageInfo, integrationToEnable]);
|
||||
}, [isAgentlessEnabled, isAgentlessDefault, packageInfo, integrationToEnable]);
|
||||
|
||||
const agentlessPolicyName = getAgentlessAgentPolicyNameFromPackagePolicyName(packagePolicy.name);
|
||||
|
||||
|
@ -184,15 +186,18 @@ export function useSetupTechnology({
|
|||
};
|
||||
}
|
||||
|
||||
const isAgentlessSetupDefault = (packageInfo?: PackageInfo, integrationToEnable?: string) => {
|
||||
const isAgentlessSetupDefault = (
|
||||
isAgentlessDefault: boolean,
|
||||
packageInfo?: PackageInfo,
|
||||
integrationToEnable?: string
|
||||
) => {
|
||||
if (
|
||||
packageInfo &&
|
||||
packageInfo.policy_templates &&
|
||||
packageInfo.policy_templates.length > 0 &&
|
||||
((integrationToEnable &&
|
||||
packageInfo?.policy_templates?.find((p) => p.name === integrationToEnable)?.deployment_modes
|
||||
?.agentless.is_default) ||
|
||||
packageInfo?.policy_templates?.every((p) => p.deployment_modes?.agentless.is_default))
|
||||
isAgentlessDefault ||
|
||||
((packageInfo?.policy_templates ?? []).length > 0 &&
|
||||
((integrationToEnable &&
|
||||
packageInfo?.policy_templates?.find((p) => p.name === integrationToEnable)?.deployment_modes
|
||||
?.agentless.is_default) ||
|
||||
packageInfo?.policy_templates?.every((p) => p.deployment_modes?.agentless.is_default)))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -367,7 +367,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
"'package-policy-create' and 'package-policy-replace-define-step' cannot both be registered as UI extensions"
|
||||
);
|
||||
}
|
||||
const { isAgentlessIntegration } = useAgentless();
|
||||
const { isAgentlessIntegration, isAgentlessDefault } = useAgentless();
|
||||
|
||||
const replaceStepConfigurePackagePolicy =
|
||||
replaceDefineStepView && packageInfo?.name ? (
|
||||
|
@ -421,6 +421,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
// agentless doesn't need system integration
|
||||
setWithSysMonitoring(value === SetupTechnology.AGENT_BASED);
|
||||
}}
|
||||
isAgentlessDefault={isAgentlessDefault}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -462,6 +463,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
formState,
|
||||
extensionView,
|
||||
isAgentlessIntegration,
|
||||
isAgentlessDefault,
|
||||
selectedSetupTechnology,
|
||||
integrationToEnable,
|
||||
isAgentlessSelected,
|
||||
|
|
|
@ -110,6 +110,13 @@ describe('Config schema', () => {
|
|||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
it('should allow to specify xpack.fleet.agentless.isDefault flag ', () => {
|
||||
expect(() => {
|
||||
config.schema.validate({
|
||||
agentless: { isDefault: true },
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
it('should allow to specify a agentless.api.url in xpack.fleet.agentless.api without the tls config ', () => {
|
||||
expect(() => {
|
||||
config.schema.validate({
|
||||
|
|
|
@ -37,6 +37,7 @@ export const config: PluginConfigDescriptor = {
|
|||
},
|
||||
agentless: {
|
||||
enabled: true,
|
||||
isDefault: true,
|
||||
},
|
||||
enableExperimental: true,
|
||||
developer: {
|
||||
|
@ -151,6 +152,7 @@ export const config: PluginConfigDescriptor = {
|
|||
agentless: schema.maybe(
|
||||
schema.object({
|
||||
enabled: schema.boolean({ defaultValue: false }),
|
||||
isDefault: schema.maybe(schema.boolean({ defaultValue: false })),
|
||||
api: schema.maybe(
|
||||
schema.object({
|
||||
url: schema.maybe(schema.uri({ scheme: ['http', 'https'] })),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue