운영체제 (LNX,WIN)

FleetDM API 활용한 Osquery 자동 수행 및 결과 수집 분석

날으는물고기 2024. 8. 19. 00:01

Osquery: Under the Hood

FleetDM API를 통해 설치된 에이전트 호스트 정보를 가져오기 위해서는 다음 단계들이 필요합니다.

1. API Token 발급

FleetDM API를 사용하려면 API 토큰이 필요합니다. FleetDM 웹 인터페이스를 통해 토큰을 발급받아야 합니다.

  1. FleetDM 웹 인터페이스에 로그인합니다.
  2. 오른쪽 상단의 프로필 아이콘을 클릭하고 "API Tokens"를 선택합니다.
  3. "Create an API token"을 클릭하고 토큰을 생성합니다.
  4. 생성된 토큰을 기록해 둡니다. 이 토큰은 API 요청 시 인증에 사용됩니다.

2. API 요청 준비

FleetDM API는 RESTful API로, 호스트 정보를 가져오기 위한 엔드포인트는 /api/v1/fleet/hosts입니다. 요청을 보내기 위해 cURL을 사용하거나 Python 등 다양한 방법을 사용할 수 있습니다.

cURL을 사용한 예시

curl -X GET https://<your_fleetdm_url>/api/v1/fleet/hosts \
  -H "Authorization: Bearer <your_api_token>" \
  -H "Content-Type: application/json"

Python을 사용한 예시

import requests

url = 'https://<your_fleetdm_url>/api/v1/fleet/hosts'
headers = {
    'Authorization': 'Bearer <your_api_token>',
    'Content-Type': 'application/json'
}

response = requests.get(url, headers=headers)

if response.status_code == 200:
    hosts = response.json()
    print(hosts)
else:
    print(f"Failed to retrieve hosts: {response.status_code}")

3. API 응답 처리

API 호출에 성공하면 JSON 형식의 데이터가 반환됩니다. 예를 들어, 반환된 데이터는 다음과 같습니다.

{
  "hosts": [
    {
      "id": 1,
      "hostname": "host1",
      "primary_ip": "192.168.1.1",
      "osquery_version": "4.6.0",
      "status": "active",
      "last_seen_at": "2024-06-26T10:00:00Z"
    },
    {
      "id": 2,
      "hostname": "host2",
      "primary_ip": "192.168.1.2",
      "osquery_version": "4.6.0",
      "status": "offline",
      "last_seen_at": "2024-06-25T15:00:00Z"
    }
  ]
}

4. 추가 옵션

호스트 정보를 가져올 때 특정 필터를 적용하거나, 페이지네이션 등을 설정할 수 있습니다.

예시: 특정 필터 적용

curl -X GET 'https://<your_fleetdm_url>/api/v1/fleet/hosts?status=active' \
  -H "Authorization: Bearer <your_api_token>" \
  -H "Content-Type: application/json"
  • API 토큰을 안전하게 관리하고 노출되지 않도록 주의합니다.
  • HTTPS를 사용하여 통신 데이터를 암호화합니다.
  • 최소 권한 원칙을 적용하여 필요한 권한만 부여된 API 토큰을 사용합니다.

활용 사례

  • 정기적으로 호스트 정보를 가져와서 인벤토리 업데이트
  • 호스트 상태 모니터링 및 리포트 생성
  • 특정 조건에 맞는 호스트에 대한 자동화된 대응 조치 수행

 

FleetDM API를 활용하면 효율적으로 호스트를 관리하고, 보안 이벤트에 빠르게 대응할 수 있습니다. FleetDM API를 사용하여 현재 등록된 쿼리 목록과 스케줄 정보를 가져오는 방법입니다.

1. API Token 발급

API 토큰을 FleetDM 웹 인터페이스에서 발급받습니다. (앞서 설명한 방법 참조)

2. 등록된 쿼리 목록 가져오기

FleetDM API 엔드포인트 /api/v1/fleet/queries를 사용하여 등록된 쿼리 목록을 가져올 수 있습니다.

cURL을 사용한 예시

curl -X GET https://<your_fleetdm_url>/api/v1/fleet/queries \
  -H "Authorization: Bearer <your_api_token>" \
  -H "Content-Type: application/json"

Python을 사용한 예시

import requests

url = 'https://<your_fleetdm_url>/api/v1/fleet/queries'
headers = {
    'Authorization': 'Bearer <your_api_token>',
    'Content-Type': 'application/json'
}

response = requests.get(url, headers=headers)

if response.status_code == 200:
    queries = response.json()
    print(queries)
else:
    print(f"Failed to retrieve queries: {response.status_code}")

3. 스케줄 정보 가져오기

FleetDM API 엔드포인트 /api/v1/fleet/scheduled_queries를 사용하여 스케줄 정보를 가져올 수 있습니다.

cURL을 사용한 예시

curl -X GET https://<your_fleetdm_url>/api/v1/fleet/scheduled_queries \
  -H "Authorization: Bearer <your_api_token>" \
  -H "Content-Type: application/json"

Python을 사용한 예시

import requests

url = 'https://<your_fleetdm_url>/api/v1/fleet/scheduled_queries'
headers = {
    'Authorization': 'Bearer <your_api_token>',
    'Content-Type': 'application/json'
}

response = requests.get(url, headers=headers)

if response.status_code == 200:
    scheduled_queries = response.json()
    print(scheduled_queries)
else:
    print(f"Failed to retrieve scheduled queries: {response.status_code}")

4. API 응답 처리

API 호출에 성공하면 JSON 형식의 데이터가 반환됩니다. 각각의 엔드포인트에서 반환되는 데이터는 다음과 같습니다.

쿼리 목록 예시

{
  "queries": [
    {
      "id": 1,
      "name": "query_name",
      "description": "query_description",
      "query": "SELECT * FROM osquery_info;",
      "author_id": 1,
      "author_name": "admin",
      "created_at": "2024-06-01T12:00:00Z",
      "updated_at": "2024-06-10T12:00:00Z"
    },
    // 추가 쿼리 정보
  ]
}

스케줄 정보 예시

{
  "scheduled_queries": [
    {
      "id": 1,
      "pack_id": 1,
      "query_id": 1,
      "interval": 3600,
      "platform": "all",
      "version": "2.9.0",
      "shard": 100,
      "created_at": "2024-06-01T12:00:00Z",
      "updated_at": "2024-06-10T12:00:00Z"
    },
    // 추가 스케줄 정보
  ]
}

활용 사례

  • 정기적으로 쿼리 및 스케줄 정보를 가져와서 인벤토리 업데이트
  • 특정 조건에 맞는 쿼리를 자동화하여 보안 이벤트 모니터링
  • 쿼리 스케줄을 최적화하여 시스템 자원 사용 효율성 향상

 

이 방법을 통해 FleetDM에서 등록된 쿼리 목록과 스케줄 정보를 쉽게 가져올 수 있습니다. 실제 운영을 위해서 특정 쿼리를 수행하는 n8n 노드를 구성하는 방법입니다.

n8n에서 HTTP Request 노드 구성

FleetDM API를 호출하기 위해 n8n에서 HTTP Request 노드를 사용합니다.

  1. HTTP Request 노드 추가
    • n8n 에디터에서 "HTTP Request" 노드를 추가합니다.
  2. HTTP Request 노드 설정
    • Method: POST
    • URL: https://<your-fleetdm-server>/api/v1/fleet/queries/run
    • Authentication: Bearer Token
      • Access Token: FleetDM API 키 입력
    • Body Parameters
      • query: 실행할 osquery 쿼리 입력
      • hosts: 쿼리를 실행할 호스트 ID 목록 입력

설정이 완료되면 워크플로우를 저장하고 실행합니다. 워크플로우가 실행되면 지정된 호스트에서 osquery 쿼리가 실행되고 결과를 반환받습니다. 이렇게 구성한 n8n 노드를 통해 FleetDM API를 호출하여 osquery 쿼리를 수행할 수 있습니다.

 

추가적인 워크플로우 자동화 작업도 n8n을 통해 손쉽게 설정할 수 있습니다. FleetDM API를 통해 쿼리를 수행한 후 결과를 가져오고, 이를 n8n에서 집계하여 Slack으로 알림을 보내는 워크플로우를 구성할 수 있습니다.

HTTP Request 노드: 쿼리 결과 확인

쿼리가 실행된 후, 쿼리 ID를 통해 결과를 가져옵니다.

  1. HTTP Request 노드 추가
    • Method: GET
    • URL: https://<your-fleetdm-server>/api/v1/fleet/results/<query_id>
    • Authentication: Bearer Token
      • Access Token: FleetDM API 키 입력

Set 노드: 결과 집계

쿼리 결과를 가져온 후 필요한 데이터를 집계합니다.

  1. Set 노드 추가
    • 필요한 데이터를 집계 및 가공합니다.

Slack 노드: 알림 보내기

집계된 데이터를 Slack으로 알림을 보냅니다.

  1. Slack 노드 추가
    • Slack API 키를 사용하여 메시지를 전송합니다.
    • 채널 및 메시지 내용을 설정합니다.

이 워크플로우를 통해 FleetDM API를 호출하여 쿼리를 실행하고, 결과를 가져와 집계한 후, Slack으로 알림을 보낼 수 있습니다. 쿼리 실행 후 결과를 바로 수집할 수 없는 상황을 고려하여, 쿼리 실행 후 일정 시간 간격으로 결과를 확인하고 결과가 준비되면 Slack으로 알림을 보내는 방법으로 n8n에서 반복 작업을 수행하는 워크플로우를 구성할 수 있습니다.

  1. HTTP Request 노드: 쿼리 실행
  2. Wait 노드: 일정 시간 대기
  3. HTTP Request 노드: 쿼리 결과 확인
  4. IF 노드: 결과가 준비되었는지 확인
  5. Set 노드: 결과 집계
  6. Slack 노드: 알림 보내기
  7. Loop 노드: 결과가 준비되지 않은 경우 다시 체크

1. HTTP Request 노드: 쿼리 실행

{
  "parameters": {
    "requestMethod": "POST",
    "url": "https://<your-fleetdm-server>/api/v1/fleet/queries/run",
    "options": {},
    "authentication": "predefinedCredentialType",
    "sendBinaryData": false,
    "jsonParameters": true,
    "bodyParametersJson": {
      "query": "SELECT * FROM osquery_info;",
      "hosts": [1, 2, 3]
    },
    "headerParametersJson": {
      "Authorization": "Bearer YOUR_FLEETDM_API_KEY"
    }
  },
  "name": "Run Query",
  "type": "n8n-nodes-base.httpRequest",
  "typeVersion": 1,
  "position": [450, 300]
}

2. Wait 노드: 일정 시간 대기

{
  "parameters": {
    "interval": "1",
    "unit": "minutes"
  },
  "name": "Wait 1 Minute",
  "type": "n8n-nodes-base.wait",
  "typeVersion": 1,
  "position": [750, 300]
}

3. HTTP Request 노드: 쿼리 결과 확인

{
  "parameters": {
    "requestMethod": "GET",
    "url": "https://<your-fleetdm-server>/api/v1/fleet/results/={{$json[\"query_id\"]}}",
    "options": {},
    "authentication": "predefinedCredentialType",
    "sendBinaryData": false,
    "headerParametersJson": {
      "Authorization": "Bearer YOUR_FLEETDM_API_KEY"
    }
  },
  "name": "Check Results",
  "type": "n8n-nodes-base.httpRequest",
  "typeVersion": 1,
  "position": [1050, 300]
}

4. IF 노드: 결과가 준비되었는지 확인

{
  "parameters": {
    "conditions": {
      "boolean": [
        {
          "value1": "={{$json[\"status\"]}}",
          "value2": "completed"
        }
      ]
    }
  },
  "name": "If Results Ready",
  "type": "n8n-nodes-base.if",
  "typeVersion": 1,
  "position": [1350, 300]
}

5. Set 노드: 결과 집계

{
  "parameters": {
    "keepOnlySet": false,
    "values": {
      "string": [
        {
          "name": "message",
          "value": "Query results: {{$json[\"results\"]}}"
        }
      ]
    },
    "options": {}
  },
  "name": "Aggregate Results",
  "type": "n8n-nodes-base.set",
  "typeVersion": 1,
  "position": [1650, 300]
}

6. Slack 노드: 알림 보내기

{
  "parameters": {
    "authentication": "predefinedCredentialType",
    "channel": "#alerts",
    "text": "{{$json[\"message\"]}}"
  },
  "name": "Send to Slack",
  "type": "n8n-nodes-base.slack",
  "typeVersion": 1,
  "position": [1950, 300]
}

7. Loop 노드: 결과가 준비되지 않은 경우 다시 체크

{
  "parameters": {},
  "name": "Loop",
  "type": "n8n-nodes-base.noOp",
  "typeVersion": 1,
  "position": [1050, 600]
}

이 워크플로우는 쿼리 실행 후 일정 시간 간격으로 결과를 확인하며, 결과가 준비되면 이를 Slack으로 알림을 보냅니다. FleetDM을 통해 전체 시스템에 특정 쿼리를 전송하고, 일정 시간 후에 결과를 수집하여 리포팅하는 전체 워크플로우 예시입니다.

{
  "nodes": [
    {
      "name": "Run Query",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [450, 300],
      "parameters": {
        "requestMethod": "POST",
        "url": "https://<your-fleetdm-server>/api/v1/fleet/queries/run",
        "authentication": "predefinedCredentialType",
        "sendBinaryData": false,
        "jsonParameters": true,
        "bodyParametersJson": {
          "query": "SELECT * FROM osquery_info;",
          "hosts": [1, 2, 3]
        },
        "headerParametersJson": {
          "Authorization": "Bearer YOUR_FLEETDM_API_KEY"
        }
      }
    },
    {
      "name": "Wait 5 Minutes",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [750, 300],
      "parameters": {
        "interval": 5,
        "unit": "minutes"
      }
    },
    {
      "name": "Check Results",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [1050, 300],
      "parameters": {
        "requestMethod": "GET",
        "url": "https://<your-fleetdm-server>/api/v1/fleet/results/={{$json["query_id"]}}",
        "authentication": "predefinedCredentialType",
        "sendBinaryData": false,
        "headerParametersJson": {
          "Authorization": "Bearer YOUR_FLEETDM_API_KEY"
        }
      }
    },
    {
      "name": "If Results Ready",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [1350, 300],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json["status"]}}",
              "value2": "completed"
            }
          ]
        }
      }
    },
    {
      "name": "Loop",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [1050, 600],
      "parameters": {}
    },
    {
      "name": "Aggregate Results",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [1650, 300],
      "parameters": {
        "keepOnlySet": false,
        "values": {
          "string": [
            {
              "name": "message",
              "value": "Query results: {{$json["results"]}}"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "name": "Send to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 1,
      "position": [1950, 300],
      "parameters": {
        "authentication": "predefinedCredentialType",
        "channel": "#alerts",
        "text": "{{$json["message"]}}"
      }
    }
  ],
  "connections": {
    "Run Query": {
      "main": [
        [
          {
            "node": "Wait 5 Minutes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 5 Minutes": {
      "main": [
        [
          {
            "node": "Check Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Results": {
      "main": [
        [
          {
            "node": "If Results Ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Results Ready": {
      "main": [
        [
          {
            "node": "Aggregate Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 5 Minutes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Results": {
      "main": [
        [
          {
            "node": "Send to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

이 워크플로우는 FleetDM을 통해 쿼리를 실행하고, 일정 시간 후 결과를 확인하여, 결과가 준비되면 이를 집계하여 Slack으로 리포팅합니다.

728x90