mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Cloud Security] [Agentless] [Serverless] Enable Serverless projects to transition to using Agentless API solution in Kibana (#190371)
This commit is contained in:
parent
1e64b9e4b2
commit
c2933dee94
15 changed files with 289 additions and 90 deletions
|
@ -35,6 +35,7 @@ enabled:
|
|||
- x-pack/test_serverless/functional/test_suites/security/config.cloud_security_posture.basic.ts
|
||||
- x-pack/test_serverless/functional/test_suites/security/config.cloud_security_posture.essentials.ts
|
||||
- x-pack/test_serverless/functional/test_suites/security/config.cloud_security_posture.agentless.ts
|
||||
- x-pack/test_serverless/functional/test_suites/security/config.cloud_security_posture.agentless_api.ts
|
||||
- x-pack/test_serverless/functional/test_suites/security/config.saved_objects_management.ts
|
||||
- x-pack/test_serverless/functional/test_suites/security/config.context_awareness.ts
|
||||
- x-pack/test_serverless/functional/test_suites/security/common_configs/config.group1.ts
|
||||
|
|
|
@ -363,8 +363,8 @@ describe('PackagePolicyInputPanel', () => {
|
|||
isAgentlessPackagePolicy: jest.fn(),
|
||||
isAgentlessAgentPolicy: jest.fn(),
|
||||
isAgentlessIntegration: jest.fn(),
|
||||
isAgentlessCloudEnabled: true,
|
||||
isAgentlessServerlessEnabled: false,
|
||||
isAgentlessApiEnabled: true,
|
||||
isDefaultAgentlessPolicyEnabled: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -398,8 +398,8 @@ describe('PackagePolicyInputPanel', () => {
|
|||
isAgentlessPackagePolicy: jest.fn(),
|
||||
isAgentlessAgentPolicy: jest.fn(),
|
||||
isAgentlessIntegration: jest.fn(),
|
||||
isAgentlessCloudEnabled: true,
|
||||
isAgentlessServerlessEnabled: false,
|
||||
isAgentlessApiEnabled: true,
|
||||
isDefaultAgentlessPolicyEnabled: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -49,37 +49,18 @@ describe('useAgentless', () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should should not return return isAgentless when agentless is not enabled', () => {
|
||||
it('should not return isAgentless when agentless is not enabled', () => {
|
||||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessCloudEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessServerlessEnabled).toBeFalsy();
|
||||
});
|
||||
it('should should return agentlessAPIUrl when agentless config is set', () => {
|
||||
const agentlessAPIUrl = 'https://agentless.api.url';
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
api: {
|
||||
url: agentlessAPIUrl,
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessCloudEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessServerlessEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessApiEnabled).toBeFalsy();
|
||||
expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return isAgentlessEnabled as falsy if agentlessAPIUrl and experimental feature agentless is truthy without cloud or serverless', () => {
|
||||
const agentlessAPIUrl = 'https://agentless.api.url';
|
||||
it('should return isAgentlessEnabled as falsy if agentless.enabled is true and experimental feature agentless is truthy without cloud or serverless', () => {
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
api: {
|
||||
url: agentlessAPIUrl,
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
} as any);
|
||||
|
||||
|
@ -90,18 +71,14 @@ describe('useAgentless', () => {
|
|||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessCloudEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessServerlessEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessApiEnabled).toBeFalsy();
|
||||
expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return isAgentlessEnabled and isAgentlessCloudEnabled as truthy with isCloudEnabled', () => {
|
||||
const agentlessAPIUrl = 'https://agentless.api.url';
|
||||
it('should return isAgentlessEnabled and isAgentlessApiEnabled as truthy with isCloudEnabled', () => {
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
enabled: true,
|
||||
api: {
|
||||
url: agentlessAPIUrl,
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
|
@ -115,19 +92,10 @@ describe('useAgentless', () => {
|
|||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessEnabled).toBeTruthy();
|
||||
expect(result.current.isAgentlessCloudEnabled).toBeTruthy();
|
||||
expect(result.current.isAgentlessServerlessEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessApiEnabled).toBeTruthy();
|
||||
expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy();
|
||||
});
|
||||
it('should return isAgentlessEnabled and isAgentlessServerlessEnabled as truthy with isServerlessEnabled', () => {
|
||||
const agentlessAPIUrl = 'https://agentless.api.url';
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
api: {
|
||||
url: agentlessAPIUrl,
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
it('should return isAgentlessEnabled and isDefaultAgentlessPolicyEnabled as truthy with isServerlessEnabled and experimental feature agentless is truthy', () => {
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({
|
||||
agentless: true,
|
||||
} as any);
|
||||
|
@ -142,8 +110,27 @@ describe('useAgentless', () => {
|
|||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessEnabled).toBeTruthy();
|
||||
expect(result.current.isAgentlessCloudEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessServerlessEnabled).toBeTruthy();
|
||||
expect(result.current.isAgentlessApiEnabled).toBeFalsy();
|
||||
expect(result.current.isDefaultAgentlessPolicyEnabled).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return isAgentlessEnabled as falsy and isDefaultAgentlessPolicyEnabled as falsy with isServerlessEnabled and experimental feature agentless is falsy', () => {
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({
|
||||
agentless: false,
|
||||
} as any);
|
||||
|
||||
(useStartServices as MockFn).mockReturnValue({
|
||||
cloud: {
|
||||
isServerlessEnabled: true,
|
||||
isCloudEnabled: false,
|
||||
},
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useAgentless());
|
||||
|
||||
expect(result.current.isAgentlessEnabled).toBeFalsy();
|
||||
expect(result.current.isAgentlessApiEnabled).toBeFalsy();
|
||||
expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -224,6 +211,7 @@ describe('useSetupTechnology', () => {
|
|||
it('should set agentless setup technology if agent policy supports agentless in edit page', async () => {
|
||||
(useConfig as MockFn).mockReturnValue({
|
||||
agentless: {
|
||||
enabled: true,
|
||||
api: {
|
||||
url: 'https://agentless.api.url',
|
||||
},
|
||||
|
|
|
@ -29,10 +29,11 @@ export const useAgentless = () => {
|
|||
const isServerless = !!cloud?.isServerlessEnabled;
|
||||
const isCloud = !!cloud?.isCloudEnabled;
|
||||
|
||||
const isAgentlessCloudEnabled = isCloud && !!config.agentless?.enabled;
|
||||
const isAgentlessServerlessEnabled = isServerless && agentlessExperimentalFeatureEnabled;
|
||||
const isAgentlessApiEnabled = (isCloud || isServerless) && config.agentless?.enabled;
|
||||
const isDefaultAgentlessPolicyEnabled =
|
||||
!isAgentlessApiEnabled && isServerless && agentlessExperimentalFeatureEnabled;
|
||||
|
||||
const isAgentlessEnabled = isAgentlessCloudEnabled || isAgentlessServerlessEnabled;
|
||||
const isAgentlessEnabled = isAgentlessApiEnabled || isDefaultAgentlessPolicyEnabled;
|
||||
|
||||
const isAgentlessAgentPolicy = (agentPolicy: AgentPolicy | undefined) => {
|
||||
if (!agentPolicy) return false;
|
||||
|
@ -62,8 +63,8 @@ export const useAgentless = () => {
|
|||
return isAgentlessEnabled && packagePolicy.policy_ids.includes(AGENTLESS_POLICY_ID);
|
||||
};
|
||||
return {
|
||||
isAgentlessCloudEnabled,
|
||||
isAgentlessServerlessEnabled,
|
||||
isAgentlessApiEnabled,
|
||||
isDefaultAgentlessPolicyEnabled,
|
||||
isAgentlessEnabled,
|
||||
isAgentlessAgentPolicy,
|
||||
isAgentlessIntegration,
|
||||
|
@ -90,7 +91,7 @@ export function useSetupTechnology({
|
|||
isEditPage?: boolean;
|
||||
agentPolicies?: AgentPolicy[];
|
||||
}) {
|
||||
const { isAgentlessEnabled, isAgentlessCloudEnabled, isAgentlessServerlessEnabled } =
|
||||
const { isAgentlessEnabled, isAgentlessApiEnabled, isDefaultAgentlessPolicyEnabled } =
|
||||
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
|
||||
|
@ -110,7 +111,7 @@ export function useSetupTechnology({
|
|||
setSelectedSetupTechnology(SetupTechnology.AGENTLESS);
|
||||
return;
|
||||
}
|
||||
if (isAgentlessCloudEnabled && selectedSetupTechnology === SetupTechnology.AGENTLESS) {
|
||||
if (isAgentlessApiEnabled && selectedSetupTechnology === SetupTechnology.AGENTLESS) {
|
||||
const nextNewAgentlessPolicy = {
|
||||
...newAgentlessPolicy,
|
||||
name: getAgentlessAgentPolicyNameFromPackagePolicyName(packagePolicy.name),
|
||||
|
@ -122,7 +123,7 @@ export function useSetupTechnology({
|
|||
}
|
||||
}
|
||||
}, [
|
||||
isAgentlessCloudEnabled,
|
||||
isAgentlessApiEnabled,
|
||||
isEditPage,
|
||||
newAgentlessPolicy,
|
||||
packagePolicy.name,
|
||||
|
@ -145,10 +146,10 @@ export function useSetupTechnology({
|
|||
}
|
||||
};
|
||||
|
||||
if (isAgentlessServerlessEnabled) {
|
||||
if (isDefaultAgentlessPolicyEnabled) {
|
||||
fetchAgentlessPolicy();
|
||||
}
|
||||
}, [isAgentlessServerlessEnabled]);
|
||||
}, [isDefaultAgentlessPolicyEnabled]);
|
||||
|
||||
const handleSetupTechnologyChange = useCallback(
|
||||
(setupTechnology: SetupTechnology) => {
|
||||
|
@ -157,14 +158,14 @@ export function useSetupTechnology({
|
|||
}
|
||||
|
||||
if (setupTechnology === SetupTechnology.AGENTLESS) {
|
||||
if (isAgentlessCloudEnabled) {
|
||||
if (isAgentlessApiEnabled) {
|
||||
setNewAgentPolicy(newAgentlessPolicy as NewAgentPolicy);
|
||||
setSelectedPolicyTab(SelectedPolicyTab.NEW);
|
||||
updateAgentPolicies([newAgentlessPolicy] as AgentPolicy[]);
|
||||
}
|
||||
// tech debt: remove this when Serverless uses the Agentless API
|
||||
// https://github.com/elastic/security-team/issues/9781
|
||||
if (isAgentlessServerlessEnabled) {
|
||||
if (isDefaultAgentlessPolicyEnabled) {
|
||||
setNewAgentPolicy(newAgentlessPolicy as AgentPolicy);
|
||||
updateAgentPolicies([newAgentlessPolicy] as AgentPolicy[]);
|
||||
setSelectedPolicyTab(SelectedPolicyTab.EXISTING);
|
||||
|
@ -183,8 +184,8 @@ export function useSetupTechnology({
|
|||
[
|
||||
isAgentlessEnabled,
|
||||
selectedSetupTechnology,
|
||||
isAgentlessCloudEnabled,
|
||||
isAgentlessServerlessEnabled,
|
||||
isAgentlessApiEnabled,
|
||||
isDefaultAgentlessPolicyEnabled,
|
||||
setNewAgentPolicy,
|
||||
newAgentlessPolicy,
|
||||
setSelectedPolicyTab,
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-ser
|
|||
|
||||
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
|
||||
import { isAgentlessCloudEnabled } from '../../../services/utils/agentless';
|
||||
import { isAgentlessApiEnabled } from '../../../services/utils/agentless';
|
||||
|
||||
import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../common/services/agentless_policy_helper';
|
||||
|
||||
|
@ -65,7 +65,7 @@ export async function renameAgentlessAgentPolicy(
|
|||
packagePolicy: PackagePolicy,
|
||||
name: string
|
||||
) {
|
||||
if (!isAgentlessCloudEnabled()) {
|
||||
if (!isAgentlessApiEnabled()) {
|
||||
return;
|
||||
}
|
||||
// If agentless is enabled for cloud, we need to rename the agent policy
|
||||
|
|
|
@ -111,7 +111,7 @@ describe('Agentless Agent service', () => {
|
|||
namespace: 'default',
|
||||
supports_agentless: true,
|
||||
} as AgentPolicy)
|
||||
).rejects.toThrowError(new AgentlessAgentCreateError('Agentless agent not supported'));
|
||||
).rejects.toThrowError(new AgentlessAgentCreateError('missing agentless configuration'));
|
||||
});
|
||||
|
||||
it('should throw AgentlessAgentCreateError if agentless configuration is not found', async () => {
|
||||
|
|
|
@ -22,7 +22,7 @@ import { appContextService } from '../app_context';
|
|||
|
||||
import { listEnrollmentApiKeys } from '../api_keys';
|
||||
import { listFleetServerHosts } from '../fleet_server_host';
|
||||
import { prependAgentlessApiBasePathToEndpoint } from '../utils/agentless';
|
||||
import { prependAgentlessApiBasePathToEndpoint, isAgentlessApiEnabled } from '../utils/agentless';
|
||||
|
||||
class AgentlessAgentService {
|
||||
public async createAgentlessAgent(
|
||||
|
@ -33,8 +33,10 @@ class AgentlessAgentService {
|
|||
const logger = appContextService.getLogger();
|
||||
logger.debug(`Creating agentless agent ${agentlessAgentPolicy.id}`);
|
||||
|
||||
if (!appContextService.getCloud()?.isCloudEnabled) {
|
||||
logger.error('Creating agentless agent not supported in non-cloud environments');
|
||||
if (!isAgentlessApiEnabled) {
|
||||
logger.error(
|
||||
'Creating agentless agent not supported in non-cloud or non-serverless environments'
|
||||
);
|
||||
throw new AgentlessAgentCreateError('Agentless agent not supported');
|
||||
}
|
||||
if (!agentlessAgentPolicy.supports_agentless) {
|
||||
|
|
|
@ -309,6 +309,7 @@ jest.mock('./app_context', () => ({
|
|||
getExperimentalFeatures: jest.fn().mockReturnValue({
|
||||
agentless: false,
|
||||
}),
|
||||
getConfig: jest.fn(),
|
||||
getInternalUserSOClientForSpaceId: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
|
|
@ -43,7 +43,7 @@ import { type InputsOverride, packagePolicyService } from './package_policy';
|
|||
import { preconfigurePackageInputs } from './package_policy';
|
||||
import { appContextService } from './app_context';
|
||||
import type { UpgradeManagedPackagePoliciesResult } from './managed_package_policies';
|
||||
import { isAgentlessServerlessEnabled } from './utils/agentless';
|
||||
import { isDefaultAgentlessPolicyEnabled } from './utils/agentless';
|
||||
|
||||
interface PreconfigurationResult {
|
||||
policies: Array<{ id: string; updated_at: string }>;
|
||||
|
@ -164,7 +164,7 @@ export async function ensurePreconfiguredPackagesAndPolicies(
|
|||
}
|
||||
|
||||
if (
|
||||
!isAgentlessServerlessEnabled() &&
|
||||
!isDefaultAgentlessPolicyEnabled() &&
|
||||
preconfiguredAgentPolicy?.supports_agentless !== undefined
|
||||
) {
|
||||
throw new FleetError(
|
||||
|
|
|
@ -10,9 +10,9 @@ import { securityMock } from '@kbn/security-plugin/server/mocks';
|
|||
import { appContextService } from '../app_context';
|
||||
|
||||
import {
|
||||
isAgentlessCloudEnabled,
|
||||
isAgentlessApiEnabled,
|
||||
isAgentlessEnabled,
|
||||
isAgentlessServerlessEnabled,
|
||||
isDefaultAgentlessPolicyEnabled,
|
||||
prependAgentlessApiBasePathToEndpoint,
|
||||
} from './agentless';
|
||||
|
||||
|
@ -23,9 +23,10 @@ mockedAppContextService.getSecuritySetup.mockImplementation(() => ({
|
|||
...securityMock.createSetup(),
|
||||
}));
|
||||
|
||||
describe('isAgentlessCloudEnabled', () => {
|
||||
describe('isAgentlessApiEnabled', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockedAppContextService.getConfig.mockReset();
|
||||
});
|
||||
it('should return false if cloud is not enabled', () => {
|
||||
jest.spyOn(appContextService, 'getConfig').mockReturnValue({
|
||||
|
@ -35,7 +36,7 @@ describe('isAgentlessCloudEnabled', () => {
|
|||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: false } as any);
|
||||
|
||||
expect(isAgentlessCloudEnabled()).toBe(false);
|
||||
expect(isAgentlessApiEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if cloud is enabled but agentless is not', () => {
|
||||
|
@ -46,7 +47,7 @@ describe('isAgentlessCloudEnabled', () => {
|
|||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
||||
expect(isAgentlessCloudEnabled()).toBe(false);
|
||||
expect(isAgentlessApiEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if cloud is enabled and agentless is enabled', () => {
|
||||
|
@ -57,13 +58,14 @@ describe('isAgentlessCloudEnabled', () => {
|
|||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
||||
expect(isAgentlessCloudEnabled()).toBe(true);
|
||||
expect(isAgentlessApiEnabled()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAgentlessServerlessEnabled', () => {
|
||||
describe('isDefaultAgentlessPolicyEnabled', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockedAppContextService.getConfig.mockReset();
|
||||
});
|
||||
|
||||
it('should return false if serverless is not enabled', () => {
|
||||
|
@ -74,7 +76,7 @@ describe('isAgentlessServerlessEnabled', () => {
|
|||
.spyOn(appContextService, 'getCloud')
|
||||
.mockReturnValue({ isServerlessEnabled: false } as any);
|
||||
|
||||
expect(isAgentlessServerlessEnabled()).toBe(false);
|
||||
expect(isDefaultAgentlessPolicyEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if serverless is enabled but agentless is not', () => {
|
||||
|
@ -83,7 +85,7 @@ describe('isAgentlessServerlessEnabled', () => {
|
|||
.mockReturnValue({ agentless: false } as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any);
|
||||
|
||||
expect(isAgentlessServerlessEnabled()).toBe(false);
|
||||
expect(isDefaultAgentlessPolicyEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if serverless is enabled and agentless is enabled', () => {
|
||||
|
@ -92,13 +94,14 @@ describe('isAgentlessServerlessEnabled', () => {
|
|||
.mockReturnValue({ agentless: true } as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any);
|
||||
|
||||
expect(isAgentlessServerlessEnabled()).toBe(true);
|
||||
expect(isDefaultAgentlessPolicyEnabled()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAgentlessEnabled', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockedAppContextService.getConfig.mockReset();
|
||||
});
|
||||
|
||||
it('should return false if cloud and serverless are not enabled', () => {
|
||||
|
@ -138,8 +141,8 @@ describe('isAgentlessEnabled', () => {
|
|||
|
||||
it('should return true if cloud is enabled and agentless is enabled', () => {
|
||||
jest
|
||||
.spyOn(appContextService, 'getExperimentalFeatures')
|
||||
.mockReturnValue({ agentless: true } as any);
|
||||
.spyOn(appContextService, 'getConfig')
|
||||
.mockReturnValue({ agentless: { enabled: true } } as any);
|
||||
jest
|
||||
.spyOn(appContextService, 'getCloud')
|
||||
.mockReturnValue({ isCloudEnabled: true, isServerlessEnabled: false } as any);
|
||||
|
@ -163,7 +166,10 @@ describe('prependAgentlessApiBasePathToEndpoint', () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should prepend the agentless api base path to the endpoint', () => {
|
||||
it('should prepend the agentless api base path to the endpoint with ess if in cloud', () => {
|
||||
jest
|
||||
.spyOn(appContextService, 'getCloud')
|
||||
.mockReturnValue({ isCloudEnabled: true, isServerlessEnabled: false } as any);
|
||||
const agentlessConfig = {
|
||||
api: {
|
||||
url: 'https://agentless-api.com',
|
||||
|
@ -176,7 +182,27 @@ describe('prependAgentlessApiBasePathToEndpoint', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should prepend the agentless api base path to the endpoint with serverless if in serverless', () => {
|
||||
jest
|
||||
.spyOn(appContextService, 'getCloud')
|
||||
.mockReturnValue({ isCloudEnabled: false, isServerlessEnabled: true } as any);
|
||||
const agentlessConfig = {
|
||||
api: {
|
||||
url: 'https://agentless-api.com',
|
||||
},
|
||||
} as any;
|
||||
const endpoint = '/deployments';
|
||||
|
||||
expect(prependAgentlessApiBasePathToEndpoint(agentlessConfig, endpoint)).toBe(
|
||||
'https://agentless-api.com/api/v1/serverless/deployments'
|
||||
);
|
||||
});
|
||||
|
||||
it('should prepend the agentless api base path to the endpoint with a dynamic path', () => {
|
||||
jest
|
||||
.spyOn(appContextService, 'getCloud')
|
||||
.mockReturnValue({ isCloudEnabled: true, isServerlessEnabled: false } as any);
|
||||
|
||||
const agentlessConfig = {
|
||||
api: {
|
||||
url: 'https://agentless-api.com',
|
||||
|
|
|
@ -8,21 +8,23 @@
|
|||
import { appContextService } from '..';
|
||||
import type { FleetConfigType } from '../../config';
|
||||
|
||||
export const isAgentlessCloudEnabled = () => {
|
||||
export const isAgentlessApiEnabled = () => {
|
||||
const cloudSetup = appContextService.getCloud();
|
||||
return Boolean(cloudSetup?.isCloudEnabled && appContextService.getConfig()?.agentless?.enabled);
|
||||
const isHosted = cloudSetup?.isCloudEnabled || cloudSetup?.isServerlessEnabled;
|
||||
return Boolean(isHosted && appContextService.getConfig()?.agentless?.enabled);
|
||||
};
|
||||
export const isAgentlessServerlessEnabled = () => {
|
||||
export const isDefaultAgentlessPolicyEnabled = () => {
|
||||
const cloudSetup = appContextService.getCloud();
|
||||
return Boolean(
|
||||
cloudSetup?.isServerlessEnabled && appContextService.getExperimentalFeatures().agentless
|
||||
);
|
||||
};
|
||||
export const isAgentlessEnabled = () => {
|
||||
return isAgentlessCloudEnabled() || isAgentlessServerlessEnabled();
|
||||
return isAgentlessApiEnabled() || isDefaultAgentlessPolicyEnabled();
|
||||
};
|
||||
|
||||
const AGENTLESS_API_BASE_PATH = '/api/v1/ess';
|
||||
const AGENTLESS_ESS_API_BASE_PATH = '/api/v1/ess';
|
||||
const AGENTLESS_SERVERLESS_API_BASE_PATH = '/api/v1/serverless';
|
||||
|
||||
type AgentlessApiEndpoints = '/deployments' | `/deployments/${string}`;
|
||||
|
||||
|
@ -30,5 +32,9 @@ export const prependAgentlessApiBasePathToEndpoint = (
|
|||
agentlessConfig: FleetConfigType['agentless'],
|
||||
endpoint: AgentlessApiEndpoints
|
||||
) => {
|
||||
return `${agentlessConfig.api.url}${AGENTLESS_API_BASE_PATH}${endpoint}`;
|
||||
const cloudSetup = appContextService.getCloud();
|
||||
const endpointPrefix = cloudSetup?.isServerlessEnabled
|
||||
? AGENTLESS_SERVERLESS_API_BASE_PATH
|
||||
: AGENTLESS_ESS_API_BASE_PATH;
|
||||
return `${agentlessConfig.api.url}${endpointPrefix}${endpoint}`;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 { CLOUD_CREDENTIALS_PACKAGE_VERSION } from '@kbn/cloud-security-posture-plugin/common/constants';
|
||||
import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils';
|
||||
import { createTestConfig } from '../../config.base';
|
||||
|
||||
export default createTestConfig({
|
||||
serverlessProject: 'security',
|
||||
junit: {
|
||||
reportName: 'Serverless Security Cloud Security Agentless API Onboarding Functional Tests',
|
||||
},
|
||||
kbnServerArgs: [
|
||||
`--xpack.fleet.packages.0.name=cloud_security_posture`,
|
||||
`--xpack.fleet.packages.0.version=${CLOUD_CREDENTIALS_PACKAGE_VERSION}`,
|
||||
|
||||
`--xpack.fleet.agents.fleet_server.hosts=["https://ftr.kibana:8220"]`,
|
||||
`--xpack.fleet.internal.fleetServerStandalone=true`,
|
||||
|
||||
// Agentless Configuration based on Serverless Security Dev Yaml - config/serverless.security.dev.yml
|
||||
`--xpack.fleet.agentless.enabled=true`,
|
||||
`--xpack.fleet.agentless.api.url=http://localhost:8089`,
|
||||
`--xpack.fleet.agentless.api.tls.certificate=${KBN_CERT_PATH}`,
|
||||
`--xpack.fleet.agentless.api.tls.key=${KBN_KEY_PATH}`,
|
||||
`--xpack.fleet.agentless.api.tls.ca=${CA_CERT_PATH}`,
|
||||
`--xpack.cloud.serverless.project_id=some_fake_project_id`,
|
||||
],
|
||||
// load tests in the index file
|
||||
testFiles: [require.resolve('./ftr/cloud_security_posture/agentless_api')],
|
||||
});
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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 { CLOUD_CREDENTIALS_PACKAGE_VERSION } from '@kbn/cloud-security-posture-plugin/common/constants';
|
||||
import * as http from 'http';
|
||||
import expect from '@kbn/expect';
|
||||
import { setupMockServer } from './mock_agentless_api';
|
||||
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const mockAgentlessApiService = setupMockServer();
|
||||
const pageObjects = getPageObjects([
|
||||
'svlCommonPage',
|
||||
'cspSecurity',
|
||||
'security',
|
||||
'header',
|
||||
'cisAddIntegration',
|
||||
]);
|
||||
|
||||
const CIS_AWS_OPTION_TEST_ID = 'cisAwsTestId';
|
||||
|
||||
const AWS_SINGLE_ACCOUNT_TEST_ID = 'awsSingleTestId';
|
||||
|
||||
describe('Agentless API Serverless', function () {
|
||||
let mockApiServer: http.Server;
|
||||
let cisIntegration: typeof pageObjects.cisAddIntegration;
|
||||
|
||||
before(async () => {
|
||||
mockApiServer = mockAgentlessApiService.listen(8089); // Start the usage api mock server on port 8089
|
||||
await pageObjects.svlCommonPage.loginAsAdmin();
|
||||
cisIntegration = pageObjects.cisAddIntegration;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
mockApiServer.close();
|
||||
});
|
||||
|
||||
it(`should create agentless-agent`, async () => {
|
||||
const integrationPolicyName = `cloud_security_posture-${new Date().toISOString()}`;
|
||||
await cisIntegration.navigateToAddIntegrationCspmWithVersionPage(
|
||||
CLOUD_CREDENTIALS_PACKAGE_VERSION
|
||||
);
|
||||
|
||||
await cisIntegration.clickOptionButton(CIS_AWS_OPTION_TEST_ID);
|
||||
await cisIntegration.clickOptionButton(AWS_SINGLE_ACCOUNT_TEST_ID);
|
||||
|
||||
await cisIntegration.inputIntegrationName(integrationPolicyName);
|
||||
|
||||
await cisIntegration.selectSetupTechnology('agentless');
|
||||
await cisIntegration.selectAwsCredentials('direct');
|
||||
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
await cisIntegration.clickSaveButton();
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
await cisIntegration.navigateToIntegrationCspList();
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
expect(await cisIntegration.getFirstCspmIntegrationPageIntegration()).to.be(
|
||||
integrationPolicyName
|
||||
);
|
||||
expect(await cisIntegration.getFirstCspmIntegrationPageAgent()).to.be(
|
||||
`Agentless policy for ${integrationPolicyName}`
|
||||
);
|
||||
});
|
||||
|
||||
it(`should create default agent-based agent`, async () => {
|
||||
const integrationPolicyName = `cloud_security_posture-${new Date().toISOString()}`;
|
||||
|
||||
await cisIntegration.navigateToAddIntegrationCspmWithVersionPage(
|
||||
CLOUD_CREDENTIALS_PACKAGE_VERSION
|
||||
);
|
||||
|
||||
await cisIntegration.clickOptionButton(CIS_AWS_OPTION_TEST_ID);
|
||||
await cisIntegration.clickOptionButton(AWS_SINGLE_ACCOUNT_TEST_ID);
|
||||
|
||||
await cisIntegration.inputIntegrationName(integrationPolicyName);
|
||||
|
||||
await cisIntegration.clickSaveButton();
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
const agentPolicyName = await cisIntegration.getAgentBasedPolicyValue();
|
||||
|
||||
await cisIntegration.navigateToIntegrationCspList();
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
expect(await cisIntegration.getFirstCspmIntegrationPageIntegration()).to.be(
|
||||
integrationPolicyName
|
||||
);
|
||||
expect(await cisIntegration.getFirstCspmIntegrationPageAgent()).to.be(agentPolicyName);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('cloud_security_posture', function () {
|
||||
this.tags(['cloud_security_posture_agentless']);
|
||||
loadTestFile(require.resolve('./create_agent'));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { createServer } from '@mswjs/http-middleware';
|
||||
|
||||
import { http, HttpResponse, StrictResponse } from 'msw';
|
||||
|
||||
export const setupMockServer = () => {
|
||||
const server = createServer(deploymentHandler);
|
||||
return server;
|
||||
};
|
||||
|
||||
interface AgentlessApiResponse {
|
||||
status: number;
|
||||
}
|
||||
|
||||
const deploymentHandler = http.post(
|
||||
'api/v1/serverless/deployments',
|
||||
async ({ request }): Promise<StrictResponse<AgentlessApiResponse>> => {
|
||||
return HttpResponse.json({
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
);
|
Loading…
Add table
Add a link
Reference in a new issue