mirror of
https://github.com/ConnorGadbois/altin.git
synced 2026-10-21 20:46:17 -05:00
818 lines
22 KiB
YAML
818 lines
22 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Altin Management API
|
|
version: 1.0.0
|
|
description: >
|
|
REST API for managing agents, tasks, keys, and users in the Altin framework.
|
|
|
|
Every endpoint requires an `Authorization` header containing the JWT returned by
|
|
`POST /api/login`, except for `POST /api/login` itself and the `/api/users`
|
|
endpoints, which are unauthenticated.
|
|
servers:
|
|
- url: http://localhost:5000
|
|
description: Debug server
|
|
security:
|
|
- BearerAuth: []
|
|
components:
|
|
securitySchemes:
|
|
BearerAuth:
|
|
type: apiKey
|
|
in: header
|
|
name: Authorization
|
|
description: JWT token returned by `POST /api/login`, sent as the raw header value
|
|
schemas:
|
|
LoginRequest:
|
|
type: object
|
|
required:
|
|
- username
|
|
- password
|
|
properties:
|
|
username:
|
|
type: string
|
|
password:
|
|
type: string
|
|
format: password
|
|
LoginResponse:
|
|
type: object
|
|
properties:
|
|
message:
|
|
type: string
|
|
token:
|
|
type: string
|
|
Agent:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
implant_id:
|
|
type: string
|
|
ip:
|
|
type: string
|
|
os:
|
|
type: string
|
|
commands:
|
|
type: array
|
|
items:
|
|
type: object
|
|
tags:
|
|
type: array
|
|
items:
|
|
type: string
|
|
last_checkin:
|
|
type: string
|
|
format: date-time
|
|
AgentsResponse:
|
|
type: object
|
|
properties:
|
|
agents:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Agent'
|
|
Task:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
agent:
|
|
type: string
|
|
format: uuid
|
|
task:
|
|
type: string
|
|
args:
|
|
type: array
|
|
items:
|
|
type: string
|
|
sent:
|
|
type: boolean
|
|
completed:
|
|
type: boolean
|
|
TasksResponse:
|
|
type: object
|
|
properties:
|
|
tasks:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Task'
|
|
TaskResult:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
task:
|
|
type: string
|
|
format: uuid
|
|
result:
|
|
type: string
|
|
nullable: true
|
|
timestamp:
|
|
type: string
|
|
description: Timestamp formatted by the server as `YYYY-MM-DD HH:MM:SS.ffffff`
|
|
example: '2026-09-26 12:34:56.789012'
|
|
TaskResultsResponse:
|
|
type: object
|
|
properties:
|
|
results:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/TaskResult'
|
|
Key:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
key:
|
|
type: string
|
|
KeysResponse:
|
|
type: object
|
|
properties:
|
|
keys:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Key'
|
|
KeyRequest:
|
|
type: object
|
|
required:
|
|
- name
|
|
- key
|
|
properties:
|
|
name:
|
|
type: string
|
|
key:
|
|
type: string
|
|
KeyDeleteRequest:
|
|
type: object
|
|
required:
|
|
- id
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
User:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
username:
|
|
type: string
|
|
admin:
|
|
type: boolean
|
|
UsersResponse:
|
|
type: object
|
|
properties:
|
|
users:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/User'
|
|
UserRequest:
|
|
type: object
|
|
required:
|
|
- username
|
|
- password
|
|
- admin
|
|
properties:
|
|
username:
|
|
type: string
|
|
password:
|
|
type: string
|
|
format: password
|
|
admin:
|
|
type: boolean
|
|
UserPatchRequest:
|
|
type: object
|
|
required:
|
|
- id
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
username:
|
|
type: string
|
|
password:
|
|
type: string
|
|
format: password
|
|
admin:
|
|
type: boolean
|
|
TaskCreateRequest:
|
|
type: object
|
|
required:
|
|
- agent_id
|
|
- command
|
|
- args
|
|
properties:
|
|
agent_id:
|
|
type: string
|
|
format: uuid
|
|
command:
|
|
type: string
|
|
args:
|
|
type: array
|
|
items:
|
|
type: string
|
|
TagAgentRequest:
|
|
type: object
|
|
required:
|
|
- tags
|
|
description: Tags to add to the agent (POST) or remove from the agent (DELETE)
|
|
properties:
|
|
tags:
|
|
type: array
|
|
items:
|
|
type: string
|
|
UserDeleteRequest:
|
|
type: object
|
|
required:
|
|
- id
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
ErrorResponse:
|
|
type: object
|
|
properties:
|
|
message:
|
|
type: string
|
|
MessageResponse:
|
|
type: object
|
|
properties:
|
|
message:
|
|
type: string
|
|
paths:
|
|
/api/login:
|
|
post:
|
|
summary: User login
|
|
description: Authenticate a user and return a JWT token
|
|
operationId: login
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LoginRequest'
|
|
responses:
|
|
'200':
|
|
description: Successful login
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LoginResponse'
|
|
'400':
|
|
description: Invalid request - missing username or password
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Invalid username or password
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
/api/agents:
|
|
get:
|
|
summary: List all agents
|
|
description: >
|
|
Retrieve a list of all registered agents.
|
|
|
|
Note: the `ip`, `implant` and `os` filters are accepted but are not applied
|
|
to the returned list - the full agent list is always returned.
|
|
operationId: getAgents
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: ip
|
|
in: query
|
|
schema:
|
|
type: string
|
|
description: Filter agents by IP address (currently not applied)
|
|
- name: implant
|
|
in: query
|
|
schema:
|
|
type: string
|
|
description: Filter agents by implant ID (currently not applied)
|
|
- name: os
|
|
in: query
|
|
schema:
|
|
type: string
|
|
description: Filter agents by operating system (currently not applied)
|
|
responses:
|
|
'200':
|
|
description: List of agents
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AgentsResponse'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
/api/agents/{agent_id}:
|
|
get:
|
|
summary: Get agent by ID
|
|
description: Retrieve details of a specific agent
|
|
operationId: getAgent
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: agent_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Agent UUID
|
|
responses:
|
|
'200':
|
|
description: Agent details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Agent'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Agent not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
delete:
|
|
summary: Delete an agent
|
|
description: Delete a specific agent
|
|
operationId: deleteAgent
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: agent_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Agent UUID
|
|
responses:
|
|
'204':
|
|
description: The agent was deleted
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Agent not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
/api/agents/{agent_id}/tags:
|
|
post:
|
|
summary: Add tags to an agent
|
|
description: Append tags to an existing agent. Tags already present are ignored.
|
|
operationId: tagAgent
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: agent_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Agent UUID
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TagAgentRequest'
|
|
responses:
|
|
'201':
|
|
description: Agent updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/MessageResponse'
|
|
'400':
|
|
description: Invalid request - missing tags or tags is not an array
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Agent not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
delete:
|
|
summary: Remove tags from an agent
|
|
description: >
|
|
Remove tags from an existing agent. Tags that are not present are ignored.
|
|
|
|
Note: the current implementation does not modify the stored tags - it
|
|
responds with 201 and the agent's tag list is left unchanged.
|
|
operationId: untagAgent
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: agent_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Agent UUID
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TagAgentRequest'
|
|
responses:
|
|
'201':
|
|
description: Agent updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/MessageResponse'
|
|
'400':
|
|
description: Invalid request - missing tags or tags is not an array
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Agent not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
/api/tasks:
|
|
get:
|
|
summary: List tasks
|
|
description: Retrieve tasks with optional filtering
|
|
operationId: getTasks
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: id
|
|
in: query
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Filter by agent ID
|
|
- name: agent
|
|
in: query
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Filter by agent ID (alias for id)
|
|
- name: sent
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum: ['true', 'false']
|
|
description: Filter by sent status
|
|
- name: completed
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum: ['true', 'false']
|
|
description: Filter by completed status
|
|
responses:
|
|
'200':
|
|
description: List of tasks
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TasksResponse'
|
|
'400':
|
|
description: Invalid query parameter - sent or completed is not 'true' or 'false'
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
post:
|
|
summary: Create a new task
|
|
description: >
|
|
Create a task for a specific agent with a command and arguments. The number of
|
|
arguments must exactly match the number of required arguments declared by the
|
|
agent for that command.
|
|
operationId: createTask
|
|
security:
|
|
- BearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TaskCreateRequest'
|
|
responses:
|
|
'201':
|
|
description: Task sent
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/MessageResponse'
|
|
'400':
|
|
description: Invalid request - missing body fields, or the command is not supported by the agent
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Agent not found, or the number of supplied arguments does not match the required arguments
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
/api/task-results:
|
|
get:
|
|
summary: List task results
|
|
description: >
|
|
Retrieve task results with optional filtering.
|
|
|
|
Note: filtering by `task_id` only takes effect when the undocumented `agent`
|
|
query parameter is supplied as well; sending `task_id` on its own results in
|
|
a server error.
|
|
operationId: getTaskResults
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: id
|
|
in: query
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Filter by result ID
|
|
- name: task_id
|
|
in: query
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Filter by task ID (requires the `agent` query parameter)
|
|
- name: agent
|
|
in: query
|
|
schema:
|
|
type: string
|
|
description: >
|
|
Has no effect on its own; it must be supplied together with `task_id`
|
|
for the task ID filter to be applied
|
|
responses:
|
|
'200':
|
|
description: List of task results
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TaskResultsResponse'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'500':
|
|
description: Server error - `task_id` was supplied without the `agent` query parameter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
/api/keys:
|
|
get:
|
|
summary: List all keys
|
|
description: Retrieve a list of all stored keys
|
|
operationId: getKeys
|
|
security:
|
|
- BearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: List of keys
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/KeysResponse'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
post:
|
|
summary: Create a new key
|
|
description: Store a new key with a name
|
|
operationId: createKey
|
|
security:
|
|
- BearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/KeyRequest'
|
|
responses:
|
|
'200':
|
|
description: A key with that name already exists - no key was created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'201':
|
|
description: Key created successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/MessageResponse'
|
|
'400':
|
|
description: Invalid request - missing key or name
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
delete:
|
|
summary: Delete a key
|
|
description: Delete a key by its ID
|
|
operationId: deleteKey
|
|
security:
|
|
- BearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/KeyDeleteRequest'
|
|
responses:
|
|
'204':
|
|
description: The key was deleted
|
|
'400':
|
|
description: Invalid request - missing id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Unauthorized - missing, expired or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Key not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
/api/users:
|
|
get:
|
|
summary: List all users
|
|
description: Retrieve a list of all users (unauthenticated endpoint)
|
|
operationId: getUsers
|
|
security: []
|
|
responses:
|
|
'200':
|
|
description: List of users
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UsersResponse'
|
|
post:
|
|
summary: Create a new user
|
|
description: Register a new user account (unauthenticated endpoint)
|
|
operationId: createUser
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UserRequest'
|
|
responses:
|
|
'200':
|
|
description: Success - user created successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/MessageResponse'
|
|
'400':
|
|
description: Invalid request - missing fields, or admin is not a boolean
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: A user with that username already exists
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
patch:
|
|
summary: Update a user
|
|
description: >
|
|
Update user details (username, password, or admin status) by ID
|
|
(unauthenticated endpoint).
|
|
|
|
Note: only one field is applied per request, in the order username, then
|
|
password, then admin - whichever comes first in the body.
|
|
operationId: editUser
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UserPatchRequest'
|
|
responses:
|
|
'200':
|
|
description: User updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/MessageResponse'
|
|
'400':
|
|
description: Invalid request - missing id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: User not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'500':
|
|
description: Server error - currently returned whenever `admin` is supplied
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
delete:
|
|
summary: Delete a user
|
|
description: Delete a user by ID (unauthenticated endpoint)
|
|
operationId: deleteUser
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UserDeleteRequest'
|
|
responses:
|
|
'204':
|
|
description: User deleted
|
|
'400':
|
|
description: Invalid request - missing id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: User not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|