[Cloud Connector] Add cloud_connectors config in Agentless API (#215421)

## Summary
This PR introduces a dynamic Cloud Connector configuration that allows
flexibility in adding additional cloud provider settings, credentials,
and other necessary fields. The updated structure ensures better
extensibility while maintaining clarity.

`cloud_connectors` will under the [AgentlessPolicy
](184d0a32ad/x-pack/platform/plugins/shared/fleet/common/types/models/agent_policy.ts (L47))
interface
``` json
{
  "cloud_connectors": {
    "target_csp": "aws",
    "enabled": true,
  }
}
```

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Lola 2025-04-22 16:06:45 -04:00 committed by GitHub
parent 29a81d8972
commit 82999015a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 548 additions and 1 deletions

View file

@ -10919,6 +10919,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -11924,6 +11939,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -12226,6 +12256,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -13257,6 +13302,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -14534,6 +14594,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -15537,6 +15612,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -15842,6 +15932,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -16873,6 +16978,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {

View file

@ -10919,6 +10919,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -11924,6 +11939,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -12226,6 +12256,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -13257,6 +13302,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -14534,6 +14594,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -15537,6 +15612,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -15842,6 +15932,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {
@ -16873,6 +16978,21 @@
"agentless": {
"additionalProperties": false,
"properties": {
"cloud_connectors": {
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"target_csp": {
"type": "string"
}
},
"required": [
"enabled"
],
"type": "object"
},
"resources": {
"additionalProperties": false,
"properties": {

View file

@ -16677,6 +16677,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -17349,6 +17359,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -17560,6 +17580,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -18251,6 +18281,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -18921,6 +18961,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -19591,6 +19641,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -19804,6 +19864,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -20494,6 +20564,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object

View file

@ -18919,6 +18919,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -19591,6 +19601,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -19802,6 +19822,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -20493,6 +20523,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -21163,6 +21203,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -21833,6 +21883,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -22046,6 +22106,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object
@ -22736,6 +22806,16 @@ paths:
additionalProperties: false
type: object
properties:
cloud_connectors:
additionalProperties: false
type: object
properties:
enabled:
type: boolean
target_csp:
type: string
required:
- enabled
resources:
additionalProperties: false
type: object

View file

@ -73,7 +73,12 @@ export interface AgentTargetVersion {
percentage: number;
}
export interface CloudConnectors {
target_csp?: string;
enabled?: boolean;
}
export interface AgentlessPolicy {
cloud_connectors?: CloudConnectors;
resources?: {
requests?: {
memory?: string;

View file

@ -14,7 +14,7 @@ import type {
monitoringTypes,
installationStatuses,
} from '../../constants';
import type { ValueOf } from '..';
import type { CloudConnectors, ValueOf } from '..';
import type { PackageSpecManifest, PackageSpecIcon, PackageSpecCategory } from './package_spec';
@ -204,6 +204,7 @@ export interface DeploymentsModesAgentless extends DeploymentsModesDefault {
organization?: string;
division?: string;
team?: string;
cloud_connectors?: CloudConnectors;
resources?: {
requests: {
cpu: string;

View file

@ -398,6 +398,127 @@ describe('Agentless Agent service', () => {
);
});
it('should create agentless agent with cloud_connectors', async () => {
const returnValue = {
id: 'mocked',
regional_id: 'mocked',
};
(axios as jest.MockedFunction<typeof axios>).mockResolvedValueOnce(returnValue);
const soClient = getAgentPolicyCreateMock();
// ignore unrelated unique name constraint
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
jest.spyOn(appContextService, 'getConfig').mockReturnValue({
agentless: {
enabled: true,
api: {
url: 'http://api.agentless.com',
tls: {
certificate: '/path/to/cert',
key: '/path/to/key',
ca: '/path/to/ca',
},
},
},
} as any);
jest
.spyOn(appContextService, 'getCloud')
.mockReturnValue({ isCloudEnabled: true, isServerlessEnabled: true } as any);
jest
.spyOn(appContextService, 'getKibanaVersion')
.mockReturnValue('mocked-kibana-version-infinite');
mockedFleetServerHostService.list.mockResolvedValue({
items: [
{
id: 'mocked-fleet-server-id',
host: 'http://fleetserver:8220',
active: true,
is_default: true,
host_urls: ['http://fleetserver:8220'],
},
],
} as any);
mockedListEnrollmentApiKeys.mockResolvedValue({
items: [
{
id: 'mocked-fleet-enrollment-token-id',
policy_id: 'mocked-fleet-enrollment-policy-id',
api_key: 'mocked-fleet-enrollment-api-key',
},
],
} as any);
const createAgentlessAgentReturnValue = await agentlessAgentService.createAgentlessAgent(
esClient,
soClient,
{
id: 'mocked-agentless-agent-policy-id',
name: 'agentless agent policy',
namespace: 'default',
supports_agentless: true,
agentless: {
resources: {
requests: {
memory: '1Gi',
cpu: '500m',
},
},
cloud_connectors: {
target_csp: 'aws',
enabled: true,
},
},
global_data_tags: [
{
name: 'organization',
value: 'elastic',
},
{
name: 'division',
value: 'cloud',
},
{
name: 'team',
value: 'fleet',
},
],
} as AgentPolicy
);
expect(axios).toHaveBeenCalledTimes(1);
expect(createAgentlessAgentReturnValue).toEqual(returnValue);
expect(axios).toHaveBeenCalledWith(
expect.objectContaining({
data: {
fleet_token: 'mocked-fleet-enrollment-api-key',
fleet_url: 'http://fleetserver:8220',
policy_id: 'mocked-agentless-agent-policy-id',
resources: {
requests: {
memory: '1Gi',
cpu: '500m',
},
},
cloud_connectors: {
target_csp: 'aws',
enabled: true,
},
labels: {
owner: {
org: 'elastic',
division: 'cloud',
team: 'fleet',
},
},
},
headers: expect.anything(),
httpsAgent: expect.anything(),
method: 'POST',
url: 'http://api.agentless.com/api/v1/serverless/deployments',
})
);
});
it('should create agentless agent when no labels are given', async () => {
const returnValue = {
id: 'mocked',

View file

@ -102,6 +102,12 @@ class AgentlessAgentService {
`[Agentless API] Creating agentless agent with fleetUrl ${fleetUrl} and fleet_token: [REDACTED]`
);
if (agentlessAgentPolicy.agentless?.cloud_connectors?.enabled) {
logger.debug(
`[Agentless API] Creating agentless agent with ${agentlessAgentPolicy.agentless?.cloud_connectors?.target_csp} cloud connector enabled for agentless policy ${policyId}`
);
}
logger.debug(
`[Agentless API] Creating agentless agent with TLS cert: ${
agentlessConfig?.api?.tls?.certificate ? '[REDACTED]' : 'undefined'
@ -119,6 +125,7 @@ class AgentlessAgentService {
fleet_url: fleetUrl,
fleet_token: fleetToken,
resources: agentlessAgentPolicy.agentless?.resources,
cloud_connectors: agentlessAgentPolicy.agentless?.cloud_connectors,
labels,
},
method: 'POST',

View file

@ -53,6 +53,13 @@ function validateCPU(s: string) {
}
}
function validateCloudProvider(s: string) {
const csps = ['aws', 'azure', 'gcp'];
if (!csps.includes(s)) {
return 'Invalid cloud provider';
}
}
export const AgentPolicyBaseSchema = {
id: schema.maybe(schema.string()),
space_ids: schema.maybe(schema.arrayOf(schema.string())),
@ -148,6 +155,12 @@ export const AgentPolicyBaseSchema = {
),
agentless: schema.maybe(
schema.object({
cloud_connectors: schema.maybe(
schema.object({
target_csp: schema.maybe(schema.string({ validate: validateCloudProvider })),
enabled: schema.boolean(),
})
),
resources: schema.maybe(
schema.object({
requests: schema.maybe(