mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Fleet] Unenrolling agent invalidate related ES API keys (#61630)
This commit is contained in:
parent
df53260f71
commit
678d2206c6
5 changed files with 99 additions and 11 deletions
|
@ -24,7 +24,7 @@ export const postAgentAcksHandlerBuilder = function(
|
|||
return async (context, request, response) => {
|
||||
try {
|
||||
const soClient = ackService.getSavedObjectsClientContract(request);
|
||||
const res = APIKeyService.parseApiKey(request.headers);
|
||||
const res = APIKeyService.parseApiKeyFromHeaders(request.headers);
|
||||
const agent = await ackService.getAgentByAccessAPIKeyId(soClient, res.apiKeyId as string);
|
||||
const agentEvents = request.body.events as AgentEvent[];
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ export const postAgentCheckinHandler: RequestHandler<
|
|||
> = async (context, request, response) => {
|
||||
try {
|
||||
const soClient = getInternalUserSOClient(request);
|
||||
const res = APIKeyService.parseApiKey(request.headers);
|
||||
const res = APIKeyService.parseApiKeyFromHeaders(request.headers);
|
||||
const agent = await AgentService.getAgentByAccessAPIKeyId(soClient, res.apiKeyId);
|
||||
const { actions } = await AgentService.agentCheckin(
|
||||
soClient,
|
||||
|
@ -216,7 +216,7 @@ export const postAgentEnrollHandler: RequestHandler<
|
|||
> = async (context, request, response) => {
|
||||
try {
|
||||
const soClient = getInternalUserSOClient(request);
|
||||
const { apiKeyId } = APIKeyService.parseApiKey(request.headers);
|
||||
const { apiKeyId } = APIKeyService.parseApiKeyFromHeaders(request.headers);
|
||||
const enrollmentAPIKey = await APIKeyService.getEnrollmentAPIKeyById(soClient, apiKeyId);
|
||||
|
||||
if (!enrollmentAPIKey || !enrollmentAPIKey.active) {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
import { SavedObjectsClientContract } from 'src/core/server';
|
||||
import { AgentSOAttributes } from '../../types';
|
||||
import { AGENT_SAVED_OBJECT_TYPE } from '../../constants';
|
||||
import { getAgent } from './crud';
|
||||
import * as APIKeyService from '../api_keys';
|
||||
|
||||
export async function unenrollAgents(
|
||||
soClient: SavedObjectsClientContract,
|
||||
|
@ -15,9 +17,7 @@ export async function unenrollAgents(
|
|||
const response = [];
|
||||
for (const id of toUnenrollIds) {
|
||||
try {
|
||||
await soClient.update<AgentSOAttributes>(AGENT_SAVED_OBJECT_TYPE, id, {
|
||||
active: false,
|
||||
});
|
||||
await unenrollAgent(soClient, id);
|
||||
response.push({
|
||||
id,
|
||||
success: true,
|
||||
|
@ -33,3 +33,22 @@ export async function unenrollAgents(
|
|||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function unenrollAgent(soClient: SavedObjectsClientContract, agentId: string) {
|
||||
const agent = await getAgent(soClient, agentId);
|
||||
|
||||
await Promise.all([
|
||||
agent.access_api_key_id
|
||||
? APIKeyService.invalidateAPIKey(soClient, agent.access_api_key_id)
|
||||
: undefined,
|
||||
agent.default_api_key
|
||||
? APIKeyService.invalidateAPIKey(
|
||||
soClient,
|
||||
APIKeyService.parseApiKey(agent.default_api_key).apiKeyId
|
||||
)
|
||||
: undefined,
|
||||
]);
|
||||
await soClient.update<AgentSOAttributes>(AGENT_SAVED_OBJECT_TYPE, agentId, {
|
||||
active: false,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../constants';
|
|||
import { EnrollmentAPIKeySOAttributes, EnrollmentAPIKey } from '../../types';
|
||||
import { createAPIKey } from './security';
|
||||
|
||||
export { invalidateAPIKey } from './security';
|
||||
export * from './enrollment_api_key';
|
||||
|
||||
export async function generateOutputApiKey(
|
||||
|
@ -77,7 +78,7 @@ export async function getEnrollmentAPIKeyById(
|
|||
return enrollmentAPIKey;
|
||||
}
|
||||
|
||||
export function parseApiKey(headers: KibanaRequest['headers']) {
|
||||
export function parseApiKeyFromHeaders(headers: KibanaRequest['headers']) {
|
||||
const authorizationHeader = headers.authorization;
|
||||
|
||||
if (!authorizationHeader) {
|
||||
|
@ -93,9 +94,11 @@ export function parseApiKey(headers: KibanaRequest['headers']) {
|
|||
}
|
||||
|
||||
const apiKey = authorizationHeader.split(' ')[1];
|
||||
if (!apiKey) {
|
||||
throw new Error('Authorization header is malformed');
|
||||
}
|
||||
|
||||
return parseApiKey(apiKey);
|
||||
}
|
||||
|
||||
export function parseApiKey(apiKey: string) {
|
||||
const apiKeyId = Buffer.from(apiKey, 'base64')
|
||||
.toString('utf8')
|
||||
.split(':')[0];
|
||||
|
|
|
@ -5,17 +5,58 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import { setupIngest } from './agents/services';
|
||||
|
||||
export default function({ getService }: FtrProviderContext) {
|
||||
export default function(providerContext: FtrProviderContext) {
|
||||
const { getService } = providerContext;
|
||||
const esArchiver = getService('esArchiver');
|
||||
const supertest = getService('supertest');
|
||||
const esClient = getService('es');
|
||||
|
||||
describe('fleet_unenroll_agent', () => {
|
||||
let accessAPIKeyId: string;
|
||||
let outputAPIKeyId: string;
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('fleet/agents');
|
||||
});
|
||||
setupIngest(providerContext);
|
||||
beforeEach(async () => {
|
||||
const { body: accessAPIKeyBody } = await esClient.security.createApiKey({
|
||||
body: {
|
||||
name: `test access api key: ${uuid.v4()}`,
|
||||
},
|
||||
});
|
||||
accessAPIKeyId = accessAPIKeyBody.id;
|
||||
const { body: outputAPIKeyBody } = await esClient.security.createApiKey({
|
||||
body: {
|
||||
name: `test output api key: ${uuid.v4()}`,
|
||||
},
|
||||
});
|
||||
outputAPIKeyId = outputAPIKeyBody.id;
|
||||
const {
|
||||
body: { _source: agentDoc },
|
||||
} = await esClient.get({
|
||||
index: '.kibana',
|
||||
id: 'agents:agent1',
|
||||
});
|
||||
// @ts-ignore
|
||||
agentDoc.agents.access_api_key_id = accessAPIKeyId;
|
||||
agentDoc.agents.default_api_key = Buffer.from(
|
||||
`${outputAPIKeyBody.id}:${outputAPIKeyBody.api_key}`
|
||||
).toString('base64');
|
||||
|
||||
await esClient.update({
|
||||
index: '.kibana',
|
||||
id: 'agents:agent1',
|
||||
refresh: 'true',
|
||||
body: {
|
||||
doc: agentDoc,
|
||||
},
|
||||
});
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('fleet/agents');
|
||||
});
|
||||
|
@ -54,6 +95,31 @@ export default function({ getService }: FtrProviderContext) {
|
|||
expect(body.results[0].success).to.be(true);
|
||||
});
|
||||
|
||||
it('should invalidate related API keys', async () => {
|
||||
const { body } = await supertest
|
||||
.post(`/api/ingest_manager/fleet/agents/unenroll`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
ids: ['agent1'],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.keys('results', 'success');
|
||||
expect(body.success).to.be(true);
|
||||
|
||||
const {
|
||||
body: { api_keys: accessAPIKeys },
|
||||
} = await esClient.security.getApiKey({ id: accessAPIKeyId });
|
||||
expect(accessAPIKeys).length(1);
|
||||
expect(accessAPIKeys[0].invalidated).eql(true);
|
||||
|
||||
const {
|
||||
body: { api_keys: outputAPIKeys },
|
||||
} = await esClient.security.getApiKey({ id: outputAPIKeyId });
|
||||
expect(outputAPIKeys).length(1);
|
||||
expect(outputAPIKeys[0].invalidated).eql(true);
|
||||
});
|
||||
|
||||
it('allow to unenroll using a kibana query', async () => {
|
||||
const { body } = await supertest
|
||||
.post(`/api/ingest_manager/fleet/agents/unenroll`)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue