mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Fleet] Support agentless traffic filters (#222082)
Introduces new config key to allow passing app tokens for elasticsearch and fleet server for agentless deployment: ``` xpack.fleet.agentless.deploymentSecrets: fleetAppToken: TOKEN1 elasticsearchAppToken: TOKEN2 ``` This new config will be passed to the agentless-api to be used by agentless agents to support traffic filtering. --------- Co-authored-by: Michel Losier <michel.losier@elastic.co>
This commit is contained in:
parent
ee41136441
commit
fa214dcf1c
5 changed files with 176 additions and 7 deletions
|
@ -41,6 +41,10 @@ export interface FleetConfigType {
|
|||
ca?: string;
|
||||
};
|
||||
};
|
||||
deploymentSecrets?: {
|
||||
fleetAppToken?: string;
|
||||
elasticsearchAppToken?: string;
|
||||
};
|
||||
customIntegrations?: {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
|
|
@ -171,6 +171,12 @@ export const config: PluginConfigDescriptor = {
|
|||
),
|
||||
})
|
||||
),
|
||||
deploymentSecrets: schema.maybe(
|
||||
schema.object({
|
||||
fleetAppToken: schema.maybe(schema.string()),
|
||||
elasticsearchAppToken: schema.maybe(schema.string()),
|
||||
})
|
||||
),
|
||||
customIntegrations: schema.maybe(
|
||||
schema.object({
|
||||
enabled: schema.maybe(schema.boolean({ defaultValue: false })),
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
} from '../../../common/types';
|
||||
|
||||
import { appContextService } from '../app_context';
|
||||
import { agentPolicyService } from '../agent_policy';
|
||||
import { listEnrollmentApiKeys } from '../api_keys';
|
||||
import { fleetServerHostService } from '../fleet_server_host';
|
||||
|
||||
|
@ -92,6 +93,9 @@ describe('Agentless Agent service', () => {
|
|||
mockedAppContextService.getLogger.mockReturnValue(mockedLogger);
|
||||
mockedAppContextService.getExperimentalFeatures.mockReturnValue({ agentless: false } as any);
|
||||
(axios as jest.MockedFunction<typeof axios>).mockReset();
|
||||
jest.spyOn(agentPolicyService, 'getFullAgentPolicy').mockResolvedValue({
|
||||
outputs: { agentless: {} as any },
|
||||
} as any);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
|
@ -117,6 +121,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -185,6 +193,13 @@ describe('Agentless Agent service', () => {
|
|||
team: 'fleet',
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
fleet_app_token: 'fleet-app-token',
|
||||
elasticsearch_app_token: 'es-app-token',
|
||||
},
|
||||
policy_details: {
|
||||
output_name: 'agentless',
|
||||
},
|
||||
}),
|
||||
headers: expect.anything(),
|
||||
httpsAgent: expect.anything(),
|
||||
|
@ -212,6 +227,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest
|
||||
|
@ -270,7 +289,7 @@ describe('Agentless Agent service', () => {
|
|||
expect(createAgentlessAgentReturnValue).toEqual(mockAgentlessDeploymentResponse);
|
||||
expect(axios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
data: expect.objectContaining({
|
||||
fleet_token: 'mocked-fleet-enrollment-api-key',
|
||||
fleet_url: 'http://fleetserver:8220',
|
||||
policy_id: 'mocked-agentless-agent-policy-id',
|
||||
|
@ -281,7 +300,14 @@ describe('Agentless Agent service', () => {
|
|||
team: 'fleet',
|
||||
},
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
fleet_app_token: 'fleet-app-token',
|
||||
elasticsearch_app_token: 'es-app-token',
|
||||
},
|
||||
policy_details: {
|
||||
output_name: 'agentless',
|
||||
},
|
||||
}),
|
||||
headers: expect.anything(),
|
||||
httpsAgent: expect.anything(),
|
||||
method: 'POST',
|
||||
|
@ -308,6 +334,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest
|
||||
|
@ -374,7 +404,7 @@ describe('Agentless Agent service', () => {
|
|||
expect(createAgentlessAgentReturnValue).toEqual(mockAgentlessDeploymentResponse);
|
||||
expect(axios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
data: expect.objectContaining({
|
||||
fleet_token: 'mocked-fleet-enrollment-api-key',
|
||||
fleet_url: 'http://fleetserver:8220',
|
||||
policy_id: 'mocked-agentless-agent-policy-id',
|
||||
|
@ -391,7 +421,14 @@ describe('Agentless Agent service', () => {
|
|||
team: 'fleet',
|
||||
},
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
fleet_app_token: 'fleet-app-token',
|
||||
elasticsearch_app_token: 'es-app-token',
|
||||
},
|
||||
policy_details: {
|
||||
output_name: 'agentless',
|
||||
},
|
||||
}),
|
||||
headers: expect.anything(),
|
||||
httpsAgent: expect.anything(),
|
||||
method: 'POST',
|
||||
|
@ -418,6 +455,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest
|
||||
|
@ -488,7 +529,7 @@ describe('Agentless Agent service', () => {
|
|||
expect(createAgentlessAgentReturnValue).toEqual(mockAgentlessDeploymentResponse);
|
||||
expect(axios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
data: expect.objectContaining({
|
||||
fleet_token: 'mocked-fleet-enrollment-api-key',
|
||||
fleet_url: 'http://fleetserver:8220',
|
||||
policy_id: 'mocked-agentless-agent-policy-id',
|
||||
|
@ -509,7 +550,14 @@ describe('Agentless Agent service', () => {
|
|||
team: 'fleet',
|
||||
},
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
fleet_app_token: 'fleet-app-token',
|
||||
elasticsearch_app_token: 'es-app-token',
|
||||
},
|
||||
policy_details: {
|
||||
output_name: 'agentless',
|
||||
},
|
||||
}),
|
||||
headers: expect.anything(),
|
||||
httpsAgent: expect.anything(),
|
||||
method: 'POST',
|
||||
|
@ -536,6 +584,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -609,6 +661,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -644,6 +700,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getKibanaVersion').mockReturnValue('8.18.0');
|
||||
|
@ -683,6 +743,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest
|
||||
|
@ -723,6 +787,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -840,6 +908,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -904,6 +976,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -964,6 +1040,10 @@ describe('Agentless Agent service', () => {
|
|||
key: '/path/to/key',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
|
@ -998,6 +1078,10 @@ describe('Agentless Agent service', () => {
|
|||
key: '/path/to/key',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
await expect(
|
||||
|
@ -1047,6 +1131,10 @@ describe('Agentless Agent service', () => {
|
|||
key: '/path/to/key',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -1085,6 +1173,10 @@ describe('Agentless Agent service', () => {
|
|||
key: '/path/to/key',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -1126,6 +1218,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -1186,6 +1282,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -1246,6 +1346,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -1306,6 +1410,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -1366,6 +1474,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -1426,6 +1538,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -1486,6 +1602,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
@ -1546,6 +1666,10 @@ describe('Agentless Agent service', () => {
|
|||
ca: '/path/to/ca',
|
||||
},
|
||||
},
|
||||
deploymentSecrets: {
|
||||
fleetAppToken: 'fleet-app-token',
|
||||
elasticsearchAppToken: 'es-app-token',
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isCloudEnabled: true } as any);
|
||||
|
|
|
@ -44,6 +44,7 @@ import {
|
|||
RETRYABLE_HTTP_STATUSES,
|
||||
RETRYABLE_SERVER_CODES,
|
||||
} from '../../../common/constants/agentless';
|
||||
import { agentPolicyService } from '../agent_policy';
|
||||
|
||||
interface AgentlessAgentErrorHandlingMessages {
|
||||
[key: string]: {
|
||||
|
@ -115,8 +116,9 @@ class AgentlessAgentService {
|
|||
and TLS ca: ${agentlessConfig?.api?.tls?.ca ? '[REDACTED]' : 'undefined'}`
|
||||
);
|
||||
const tlsConfig = this.createTlsConfig(agentlessConfig);
|
||||
|
||||
const labels = this.getAgentlessTags(agentlessAgentPolicy);
|
||||
const secrets = this.getAgentlessSecrets();
|
||||
const policyDetails = await this.getPolicyDetails(soClient, agentlessAgentPolicy);
|
||||
|
||||
const requestConfig: AxiosRequestConfig = {
|
||||
url: prependAgentlessApiBasePathToEndpoint(agentlessConfig, '/deployments'),
|
||||
|
@ -127,6 +129,8 @@ class AgentlessAgentService {
|
|||
resources: agentlessAgentPolicy.agentless?.resources,
|
||||
cloud_connectors: agentlessAgentPolicy.agentless?.cloud_connectors,
|
||||
labels,
|
||||
secrets,
|
||||
policy_details: policyDetails,
|
||||
},
|
||||
method: 'POST',
|
||||
...this.getHeaders(tlsConfig, traceId),
|
||||
|
@ -287,6 +291,21 @@ class AgentlessAgentService {
|
|||
return response;
|
||||
}
|
||||
|
||||
private getAgentlessSecrets() {
|
||||
const deploymentSecrets = appContextService.getConfig()?.agentless?.deploymentSecrets;
|
||||
|
||||
if (!deploymentSecrets) return {};
|
||||
|
||||
return {
|
||||
...(deploymentSecrets?.fleetAppToken
|
||||
? { fleet_app_token: deploymentSecrets?.fleetAppToken }
|
||||
: {}),
|
||||
...(deploymentSecrets?.elasticsearchAppToken
|
||||
? { elasticsearch_app_token: deploymentSecrets?.elasticsearchAppToken }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
private getHeaders(tlsConfig: SslConfig, traceId: string | undefined) {
|
||||
return {
|
||||
headers: {
|
||||
|
@ -303,6 +322,20 @@ class AgentlessAgentService {
|
|||
};
|
||||
}
|
||||
|
||||
private async getPolicyDetails(
|
||||
soClient: SavedObjectsClientContract,
|
||||
agentlessAgentPolicy: AgentPolicy
|
||||
) {
|
||||
const fullPolicy = await agentPolicyService.getFullAgentPolicy(
|
||||
soClient,
|
||||
agentlessAgentPolicy.id
|
||||
);
|
||||
|
||||
return {
|
||||
output_name: Object.keys(fullPolicy?.outputs || {})?.[0], // Agentless policies only have one output
|
||||
};
|
||||
}
|
||||
|
||||
private getAgentlessTags(agentlessAgentPolicy: AgentPolicy) {
|
||||
if (!agentlessAgentPolicy.global_data_tags) {
|
||||
return undefined;
|
||||
|
|
|
@ -25,6 +25,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
before(async () => {
|
||||
mockApiServer = await mockAgentlessApiService.listen(8089); // Start the agentless api mock server on port 8089
|
||||
// Ensure fleet default outputs are setup
|
||||
await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxxx').expect(200);
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/fleet/agents');
|
||||
const getPkRes = await supertest
|
||||
.get(`/api/fleet/epm/packages/${FLEET_ELASTIC_AGENT_PACKAGE}`)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue