From 60a8a265e00cf3d3cad51da3a831cd436aff1a5e Mon Sep 17 00:00:00 2001 From: Thijs Heijligenberg Date: Thu, 14 Aug 2025 11:21:00 +0200 Subject: [PATCH 1/2] Update swagger definitions Also fixes 06b0473: this commit did indeed fix the issue at hand, but also inadvertently broke the swagger generation. This also adds the entries for the manual api, which were apparently missing. --- docs/static/openapi/swagger.json | 381 ++++++++++++++++++++++++++++++- docs/static/openapi/swagger.yaml | 261 ++++++++++++++++++++- makefile | 5 +- 3 files changed, 644 insertions(+), 3 deletions(-) diff --git a/docs/static/openapi/swagger.json b/docs/static/openapi/swagger.json index ee7eee092..f14e4f60c 100644 --- a/docs/static/openapi/swagger.json +++ b/docs/static/openapi/swagger.json @@ -6,6 +6,256 @@ "version": "1.0.0" }, "paths": { + "/keymanagement/": { + "get": { + "description": "return all keys in the KMS", + "produces": [ + "application/json" + ], + "tags": [ + "keymanagement" + ], + "summary": "gets all keys from the KMS", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/keymanagement/:keyname/": { + "put": { + "description": "adds a key to the KMS; load key into cache and write file", + "produces": [ + "application/json" + ], + "tags": [ + "keymanagement" + ], + "summary": "add key to KMS", + "parameters": [ + { + "description": "key", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.KeyManagementKey" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "json" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "delete": { + "description": "revokes the key by moving it to .revoked and renaming it", + "produces": [ + "application/json" + ], + "tags": [ + "keymanagement" + ], + "summary": "remove key from KMS", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "json" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/keymanagement/refresh/": { + "post": { + "description": "refresh the KMS and re-parse the underlying directory", + "produces": [ + "application/json" + ], + "tags": [ + "keymanagement" + ], + "summary": "refresh the KMS system, which allows the user to include manually added files in the KMS", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "json" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/manual/": { + "get": { + "description": "get all pending manual commands that still needs values to be returned", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "manual" + ], + "summary": "get all pending manual commands that still needs values to be returned", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/api.InteractionCommandData" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/api.InteractionCommandData" + } + } + } + } + } + }, + "/manual/continue": { + "post": { + "description": "updates the value of a variable according to the manual interaction", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "manual" + ], + "summary": "updates the value of a variable according to the manual interaction", + "parameters": [ + { + "type": "string", + "description": "execution ID", + "name": "exec_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "step ID", + "name": "step_id", + "in": "path", + "required": true + }, + { + "description": "playbook", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.ManualOutArgsUpdatePayload" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.Execution" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/manual/{exec_id}/{step_id}": { + "get": { + "description": "get a specific manual command that still needs a value to be returned", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "manual" + ], + "summary": "get a specific manual command that still needs a value to be returned", + "parameters": [ + { + "type": "string", + "description": "execution ID", + "name": "exec_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "step ID", + "name": "step_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.InteractionCommandData" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/playbook/": { "get": { "description": "return all stored playbooks default limit:100", @@ -460,6 +710,123 @@ } } }, + "api.InteractionCommandData": { + "type": "object", + "required": [ + "command", + "description", + "execution_id", + "out_args", + "playbook_id", + "step_id", + "target", + "type" + ], + "properties": { + "command": { + "description": "The command for the agent either command", + "type": "string" + }, + "commandb64": { + "description": "Indicates if the command is in b64", + "type": "boolean" + }, + "description": { + "description": "The description from the workflow step", + "type": "string" + }, + "execution_id": { + "description": "The id of the execution", + "type": "string" + }, + "out_args": { + "description": "Map of cacao variables handled in the step out args with current values and definitions", + "allOf": [ + { + "$ref": "#/definitions/cacao.Variables" + } + ] + }, + "playbook_id": { + "description": "The id of the CACAO playbook executed by the execution", + "type": "string" + }, + "step_id": { + "description": "The id of the step executed by the execution", + "type": "string" + }, + "target": { + "description": "Map of cacao agent-target with the target(s) of this command", + "allOf": [ + { + "$ref": "#/definitions/cacao.AgentTarget" + } + ] + }, + "type": { + "description": "The type of this content", + "type": "string", + "example": "execution-status" + } + } + }, + "api.KeyManagementKey": { + "type": "object", + "properties": { + "private": { + "type": "string" + }, + "public": { + "type": "string" + } + } + }, + "api.ManualOutArgsUpdatePayload": { + "type": "object", + "required": [ + "execution_id", + "playbook_id", + "response_out_args", + "response_status", + "step_id", + "type" + ], + "properties": { + "execution_id": { + "description": "The id of the execution", + "type": "string" + }, + "playbook_id": { + "description": "The id of the CACAO playbook executed by the execution", + "type": "string" + }, + "response_out_args": { + "description": "Map of cacao variables storing the out args value, handled in the step out args, with current values and definitions", + "allOf": [ + { + "$ref": "#/definitions/cacao.Variables" + } + ] + }, + "response_status": { + "description": "Indicates status of command", + "allOf": [ + { + "$ref": "#/definitions/manual.ManualResponseStatus" + } + ] + }, + "step_id": { + "description": "The id of the step executed by the execution", + "type": "string" + }, + "type": { + "description": "The type of this content", + "type": "string", + "example": "string" + } + } + }, "api.PlaybookExecutionReport": { "type": "object", "properties": { @@ -1307,6 +1674,7 @@ "type": "integer" }, "type": { + "description": "The uuid of the step (not part of CACAO spec, but included in object for utility)", "type": "string" } } @@ -1333,7 +1701,7 @@ "example": false }, "name": { - "description": "The name of the variable in the style __variable_name__", + "description": "The name of the variable in the style __variable_name__ (not part of CACAO spec, but included in object for utility)", "type": "string", "example": "__example_string__" }, @@ -1360,6 +1728,17 @@ "additionalProperties": { "$ref": "#/definitions/cacao.Step" } + }, + "manual.ManualResponseStatus": { + "type": "string", + "enum": [ + "success", + "failure" + ], + "x-enum-varnames": [ + "ManualResponseSuccessStatus", + "ManualResponseFailureStatus" + ] } } } \ No newline at end of file diff --git a/docs/static/openapi/swagger.yaml b/docs/static/openapi/swagger.yaml index 1e707e574..b590e4536 100644 --- a/docs/static/openapi/swagger.yaml +++ b/docs/static/openapi/swagger.yaml @@ -30,6 +30,88 @@ definitions: - execution_id - payload type: object + api.InteractionCommandData: + properties: + command: + description: The command for the agent either command + type: string + commandb64: + description: Indicates if the command is in b64 + type: boolean + description: + description: The description from the workflow step + type: string + execution_id: + description: The id of the execution + type: string + out_args: + allOf: + - $ref: '#/definitions/cacao.Variables' + description: Map of cacao variables handled in the step out args with current + values and definitions + playbook_id: + description: The id of the CACAO playbook executed by the execution + type: string + step_id: + description: The id of the step executed by the execution + type: string + target: + allOf: + - $ref: '#/definitions/cacao.AgentTarget' + description: Map of cacao agent-target with the target(s) of this command + type: + description: The type of this content + example: execution-status + type: string + required: + - command + - description + - execution_id + - out_args + - playbook_id + - step_id + - target + - type + type: object + api.KeyManagementKey: + properties: + private: + type: string + public: + type: string + type: object + api.ManualOutArgsUpdatePayload: + properties: + execution_id: + description: The id of the execution + type: string + playbook_id: + description: The id of the CACAO playbook executed by the execution + type: string + response_out_args: + allOf: + - $ref: '#/definitions/cacao.Variables' + description: Map of cacao variables storing the out args value, handled in + the step out args, with current values and definitions + response_status: + allOf: + - $ref: '#/definitions/manual.ManualResponseStatus' + description: Indicates status of command + step_id: + description: The id of the step executed by the execution + type: string + type: + description: The type of this content + example: string + type: string + required: + - execution_id + - playbook_id + - response_out_args + - response_status + - step_id + - type + type: object api.PlaybookExecutionReport: properties: description: @@ -598,6 +680,8 @@ definitions: timeout: type: integer type: + description: The uuid of the step (not part of CACAO spec, but included in + object for utility) type: string required: - type @@ -617,7 +701,8 @@ definitions: example: false type: boolean name: - description: The name of the variable in the style __variable_name__ + description: The name of the variable in the style __variable_name__ (not + part of CACAO spec, but included in object for utility) example: __example_string__ type: string type: @@ -639,11 +724,185 @@ definitions: additionalProperties: $ref: '#/definitions/cacao.Step' type: object + manual.ManualResponseStatus: + enum: + - success + - failure + type: string + x-enum-varnames: + - ManualResponseSuccessStatus + - ManualResponseFailureStatus info: contact: {} title: SOARCA API version: 1.0.0 paths: + /keymanagement/: + get: + description: return all keys in the KMS + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: gets all keys from the KMS + tags: + - keymanagement + /keymanagement/:keyname/: + delete: + description: revokes the key by moving it to .revoked and renaming it + produces: + - application/json + responses: + "200": + description: OK + schema: + type: json + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: remove key from KMS + tags: + - keymanagement + put: + description: adds a key to the KMS; load key into cache and write file + parameters: + - description: key + in: body + name: data + required: true + schema: + $ref: '#/definitions/api.KeyManagementKey' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: json + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: add key to KMS + tags: + - keymanagement + /keymanagement/refresh/: + post: + description: refresh the KMS and re-parse the underlying directory + produces: + - application/json + responses: + "200": + description: OK + schema: + type: json + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: refresh the KMS system, which allows the user to include manually added + files in the KMS + tags: + - keymanagement + /manual/: + get: + consumes: + - application/json + description: get all pending manual commands that still needs values to be returned + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/api.InteractionCommandData' + type: array + "400": + description: Bad Request + schema: + items: + $ref: '#/definitions/api.InteractionCommandData' + type: array + summary: get all pending manual commands that still needs values to be returned + tags: + - manual + /manual/{exec_id}/{step_id}: + get: + consumes: + - application/json + description: get a specific manual command that still needs a value to be returned + parameters: + - description: execution ID + in: path + name: exec_id + required: true + type: string + - description: step ID + in: path + name: step_id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.InteractionCommandData' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: get a specific manual command that still needs a value to be returned + tags: + - manual + /manual/continue: + post: + consumes: + - application/json + description: updates the value of a variable according to the manual interaction + parameters: + - description: execution ID + in: path + name: exec_id + required: true + type: string + - description: step ID + in: path + name: step_id + required: true + type: string + - description: playbook + in: body + name: data + required: true + schema: + $ref: '#/definitions/api.ManualOutArgsUpdatePayload' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.Execution' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: updates the value of a variable according to the manual interaction + tags: + - manual /playbook/: get: description: return all stored playbooks default limit:100 diff --git a/makefile b/makefile index 3d1f16842..fb619cf0f 100644 --- a/makefile +++ b/makefile @@ -9,9 +9,12 @@ GOLDFLAGS += -X main.Version=$(VERSION) GOLDFLAGS += -X main.Buildtime=$(BUILDTIME) GOFLAGS = -ldflags "$(GOLDFLAGS)" +# This creates the swagger.json and swagger.yaml files in api/. \ +# These can be copied to docs/static/openapi to provide convenient api documentation +# The same overview of the build can be viewed at /swagger/index.html, including for local build. swagger: mkdir -p api - swag init -g cmd/soarca/main.go -o api + swag init -o api -d cmd/soarca/,pkg/models/api,pkg/models/cacao,pkg/models/manual,pkg/api -g main.go lint: swagger From 13f586318c2b263e30c56c27d31f2f96b206b7d5 Mon Sep 17 00:00:00 2001 From: Thijs Heijligenberg Date: Thu, 14 Aug 2025 11:32:29 +0200 Subject: [PATCH 2/2] Introduce documentation for the KMS This is currently listed under extensions & capabilities. The kms is a cacao-native feature in that the json entries are from the cacao spec, but the actual implementation beyond this is not specified. It therefore makes sense to put it here and not as a core component. --- .../docs/installation-configuration/_index.md | 12 ++++++ .../key-management-system.md | 40 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 docs/content/en/docs/soarca-extensions/key-management-system.md diff --git a/docs/content/en/docs/installation-configuration/_index.md b/docs/content/en/docs/installation-configuration/_index.md index 2ccfe091e..fc789b1bd 100644 --- a/docs/content/en/docs/installation-configuration/_index.md +++ b/docs/content/en/docs/installation-configuration/_index.md @@ -67,3 +67,15 @@ More information on setting up authentication can be found [here](/docs/installa | COOKIE_SECRET_KEY | `SOME_COOKIE_SECRET` | Optional: Secret key for cookies. Generate using `openssl rand -base64 32` or `head -c 32 /dev/urandom | base64`. | | OIDC_SKIP_TLS_VERIFY | `true` | Set whether to skip TLS verification. Default is `true`. | | AUTH_GROUP | `soarca_admin` | Specify the group users must belong to for authentication against SOARCA. | + +----- + +#### Key management system +{{% alert title="Note" color="primary" %}} +More information on the key management system can be found [here](/docs/soarca-extensions/key-management-system). +{{% /alert %}} +| Variable | Content | Description | +|------------------------|---------------------------------------------|---------------------------------------------------------------------------------------------| +| ENABLE_SSH_KMS | `false` | Enable the key management system for SSH| +| SSH_KMS_DIR | `deployments/docker/testing/ssh-kms-test/ssh-keystore/`| Set the folder where SSH keys for the key management system are stored| + diff --git a/docs/content/en/docs/soarca-extensions/key-management-system.md b/docs/content/en/docs/soarca-extensions/key-management-system.md new file mode 100644 index 000000000..dc072e39f --- /dev/null +++ b/docs/content/en/docs/soarca-extensions/key-management-system.md @@ -0,0 +1,40 @@ +--- +title: Key management system +description: > + Organizing keys used in SSH connections +categories: [capabilities] +tags: [native] +weight: 2 +date: 2023-01-05 +--- + +This page details the key management system that is built into SOARCA. +It currently works only in conjunction with the [SSH capability](./native-capabilities/#SSH). + +## Activation + +The KMS feature of SOARCA is enabled by setting [environment variables](../installation-configuration/#key-management-system). + +## Use in SSH commands + +The KMS can be referenced inside a playbook inside a [user-auth](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256508) element. +This element is referenced by the [ssh command](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256500). +To use the KMS the value kms must be set to true, and the value kms_key_identifier must be set to the name of the key. + +The use of the KMS overrides a specified password. + +## Underlying structure + +The key management system relies on an underlying folder with public and private keys. +The name of the key referenced by the user in the kms_key_identifier field is the name of the private key file. +SOARCA caches the keys, and the API allows the user to refresh the system, which loads any potential new key files found in the underlying directory. +Adding a key through the API also creates new files for storing the keys. + +The system also allows the user to revoke keys through the API. +This moves the keys to a directory called .revoked inside the key storage, and appends the time and date of the revocation to the key name. +It is up to the user to actually delete these keys or to recover keys. + +## API + +The API endpoints for the KMS are documented [here](/docs/soarca-api/#key-management-system) +