mirror of
https://github.com/opnsense/plugins.git
synced 2025-12-12 00:30:29 -06:00
crowdsec: migrate bootgrid -> UIBootGrid (#4816)
* bootgrid -> UIBootGrid * server-side filtering, pagination etc. * version bump * add some field defaults; lint
This commit is contained in:
parent
259fb1ebb8
commit
3b95aa598f
@ -1,5 +1,5 @@
|
|||||||
PLUGIN_NAME= crowdsec
|
PLUGIN_NAME= crowdsec
|
||||||
PLUGIN_VERSION= 1.0.10
|
PLUGIN_VERSION= 1.0.11
|
||||||
PLUGIN_DEPENDS= crowdsec
|
PLUGIN_DEPENDS= crowdsec
|
||||||
PLUGIN_COMMENT= Lightweight and collaborative security engine
|
PLUGIN_COMMENT= Lightweight and collaborative security engine
|
||||||
PLUGIN_MAINTAINER= marco@crowdsec.net
|
PLUGIN_MAINTAINER= marco@crowdsec.net
|
||||||
|
|||||||
@ -8,6 +8,11 @@ WWW: https://crowdsec.net/
|
|||||||
Plugin Changelog
|
Plugin Changelog
|
||||||
================
|
================
|
||||||
|
|
||||||
|
1.0.11
|
||||||
|
|
||||||
|
* convert tables to UIBootGrid (required for opnsense 25.7)
|
||||||
|
* separate page for each table
|
||||||
|
|
||||||
1.0.10
|
1.0.10
|
||||||
|
|
||||||
* changed alias names crowdsec*blacklists -> crowdsec*blocklists
|
* changed alias names crowdsec*blacklists -> crowdsec*blocklists
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AlertsController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class AlertsController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction(): void
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/alerts');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,7 +6,6 @@
|
|||||||
namespace OPNsense\CrowdSec\Api;
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
use OPNsense\Base\ApiControllerBase;
|
use OPNsense\Base\ApiControllerBase;
|
||||||
use OPNsense\CrowdSec\CrowdSec;
|
|
||||||
use OPNsense\Core\Backend;
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,6 +13,48 @@ use OPNsense\Core\Backend;
|
|||||||
*/
|
*/
|
||||||
class AlertsController extends ApiControllerBase
|
class AlertsController extends ApiControllerBase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Format scope and value as "scope:value"
|
||||||
|
*
|
||||||
|
* @param array $source Array with 'scope' and 'value' keys (can be a decision)
|
||||||
|
* @return string Formatted string
|
||||||
|
*/
|
||||||
|
private function formatScopeValue(array $source): string
|
||||||
|
{
|
||||||
|
$scope = $source['scope'] ?? '';
|
||||||
|
if ($source['value'] !== '') {
|
||||||
|
$scope = $scope . ':' . $source['value'];
|
||||||
|
}
|
||||||
|
return $scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Summarize decision types as "type1:count1 type2:count2 ..."
|
||||||
|
*
|
||||||
|
* @param array $decisions List of decision arrays
|
||||||
|
* @return string Summary string
|
||||||
|
*/
|
||||||
|
private function formatDecisions(array $decisions): string
|
||||||
|
{
|
||||||
|
$counts = [];
|
||||||
|
|
||||||
|
foreach ($decisions as $decision) {
|
||||||
|
if (!isset($decision['type'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $decision['type'];
|
||||||
|
$counts[$type] = ($counts[$type] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = [];
|
||||||
|
foreach ($counts as $type => $count) {
|
||||||
|
$parts[] = "{$type}:{$count}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' ', $parts);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve list of alerts
|
* Retrieve list of alerts
|
||||||
*
|
*
|
||||||
@ -21,13 +62,27 @@ class AlertsController extends ApiControllerBase
|
|||||||
* @throws \OPNsense\Base\ModelException
|
* @throws \OPNsense\Base\ModelException
|
||||||
* @throws \ReflectionException
|
* @throws \ReflectionException
|
||||||
*/
|
*/
|
||||||
public function getAction()
|
public function searchAction(): array
|
||||||
{
|
{
|
||||||
$result = json_decode(trim((new Backend())->configdRun("crowdsec alerts-list")), true);
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec alerts-list")), true);
|
||||||
if ($result !== null) {
|
if ($result === null) {
|
||||||
// only return valid json type responses
|
return ["message" => "unable to retrieve data"];
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
return ["message" => "unable to list alerts"];
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($result as $alert) {
|
||||||
|
$source = $alert['source'] ?? [];
|
||||||
|
$rows[] = [
|
||||||
|
'id' => $alert['id'],
|
||||||
|
'value' => $this->formatScopeValue($source ?? []),
|
||||||
|
'reason' => $alert['scenario'] ?? '',
|
||||||
|
'country' => $source['cn'] ?? '',
|
||||||
|
'as' => $source['as_name'] ?? '',
|
||||||
|
'decisions' => $this->formatDecisions($alert['decisions'] ?? []),
|
||||||
|
'created' => $alert['created_at'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
|
use OPNsense\Base\ApiControllerBase;
|
||||||
|
use OPNsense\CrowdSec\Util;
|
||||||
|
|
||||||
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class AppsecconfigsController extends ApiControllerBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve the installed appsec-configs
|
||||||
|
*
|
||||||
|
* @return dictionary of items, by type
|
||||||
|
* @throws \OPNsense\Base\ModelException
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
|
public function searchAction(): array
|
||||||
|
{
|
||||||
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec appsec-configs-list")), true);
|
||||||
|
if ($result === null) {
|
||||||
|
return ["message" => "unable to retrieve data"];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $result["appsec-configs"];
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$rows[] = [
|
||||||
|
'name' => $item['name'],
|
||||||
|
'status' => $item['status'] ?? '',
|
||||||
|
'local_version' => $item['local_version'] ?? '',
|
||||||
|
'local_path' => Util::trimLocalPath($item['local_path'] ?? ''),
|
||||||
|
'description' => $item['description'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
|
use OPNsense\Base\ApiControllerBase;
|
||||||
|
use OPNsense\CrowdSec\Util;
|
||||||
|
|
||||||
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class AppsecrulesController extends ApiControllerBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve the installed appsec-rules
|
||||||
|
*
|
||||||
|
* @return dictionary of items, by type
|
||||||
|
* @throws \OPNsense\Base\ModelException
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
|
public function searchAction(): array
|
||||||
|
{
|
||||||
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec appsec-rules-list")), true);
|
||||||
|
if ($result === null) {
|
||||||
|
return ["message" => "unable to retrieve data"];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $result["appsec-rules"];
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$rows[] = [
|
||||||
|
'name' => $item['name'],
|
||||||
|
'status' => $item['status'] ?? '',
|
||||||
|
'local_version' => $item['local_version'] ?? '',
|
||||||
|
'local_path' => Util::trimLocalPath($item['local_path'] ?? ''),
|
||||||
|
'description' => $item['description'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,7 +6,6 @@
|
|||||||
namespace OPNsense\CrowdSec\Api;
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
use OPNsense\Base\ApiControllerBase;
|
use OPNsense\Base\ApiControllerBase;
|
||||||
use OPNsense\CrowdSec\CrowdSec;
|
|
||||||
use OPNsense\Core\Backend;
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,13 +20,27 @@ class BouncersController extends ApiControllerBase
|
|||||||
* @throws \OPNsense\Base\ModelException
|
* @throws \OPNsense\Base\ModelException
|
||||||
* @throws \ReflectionException
|
* @throws \ReflectionException
|
||||||
*/
|
*/
|
||||||
public function getAction()
|
public function searchAction(): array
|
||||||
{
|
{
|
||||||
$result = json_decode(trim((new Backend())->configdRun("crowdsec bouncers-list")), true);
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec bouncers-list")), true);
|
||||||
if ($result !== null) {
|
if ($result === null) {
|
||||||
// only return valid json type responses
|
return ["message" => "unable to retrieve data"];
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
return ["message" => "unable to list bouncers"];
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($result as $bouncer) {
|
||||||
|
$rows[] = [
|
||||||
|
'name' => $bouncer['name'],
|
||||||
|
'type' => $bouncer['type'] ?? '',
|
||||||
|
'version' => $bouncer['version'] ?? '',
|
||||||
|
'created' => $bouncer['created_at'] ?? '',
|
||||||
|
'valid' => ($bouncer['revoked'] ?? false) !== true,
|
||||||
|
'ip_address' => $bouncer['ip_address'] ?? '',
|
||||||
|
'last_seen' => $bouncer['last_pull'] ?? '',
|
||||||
|
'os' => $bouncer['os'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
|
use OPNsense\Base\ApiControllerBase;
|
||||||
|
use OPNsense\CrowdSec\Util;
|
||||||
|
|
||||||
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class CollectionsController extends ApiControllerBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve the installed collections
|
||||||
|
*
|
||||||
|
* @return dictionary of items, by type
|
||||||
|
* @throws \OPNsense\Base\ModelException
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
|
public function searchAction(): array
|
||||||
|
{
|
||||||
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec collections-list")), true);
|
||||||
|
if ($result === null) {
|
||||||
|
return ["message" => "unable to retrieve data"];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $result["collections"];
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$rows[] = [
|
||||||
|
'name' => $item['name'],
|
||||||
|
'status' => $item['status'] ?? '',
|
||||||
|
'local_version' => $item['local_version'] ?? '',
|
||||||
|
'local_path' => Util::trimLocalPath($item['local_path'] ?? ''),
|
||||||
|
'description' => $item['description'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,14 +6,62 @@
|
|||||||
namespace OPNsense\CrowdSec\Api;
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
use OPNsense\Base\ApiControllerBase;
|
use OPNsense\Base\ApiControllerBase;
|
||||||
use OPNsense\CrowdSec\CrowdSec;
|
|
||||||
use OPNsense\Core\Backend;
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
|
|
||||||
|
function unrollDecisions(array $alerts): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($alerts as $alert) {
|
||||||
|
if (!isset($alert['decisions']) || !is_array($alert['decisions'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($alert['decisions'] as $decision) {
|
||||||
|
// ignore deleted decisions
|
||||||
|
if (isset($decision['duration']) && str_starts_with($decision['duration'], '-')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = $decision;
|
||||||
|
|
||||||
|
// Add parent alert fields with prefix
|
||||||
|
foreach ($alert as $key => $value) {
|
||||||
|
if ($key === 'decisions') {
|
||||||
|
continue; // skip nested array
|
||||||
|
}
|
||||||
|
$row["alert_" . $key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @package OPNsense\CrowdSec
|
* @package OPNsense\CrowdSec
|
||||||
*/
|
*/
|
||||||
class DecisionsController extends ApiControllerBase
|
class DecisionsController extends ApiControllerBase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Format scope and value as "scope:value"
|
||||||
|
*
|
||||||
|
* @param array $source Array with 'scope' and 'value' keys
|
||||||
|
* @return string Formatted string
|
||||||
|
*/
|
||||||
|
private function formatScopeValue(array $source): string
|
||||||
|
{
|
||||||
|
$scope = $source['scope'] ?? '';
|
||||||
|
if ($source['value'] !== '') {
|
||||||
|
$scope = $scope . ':' . $source['value'];
|
||||||
|
}
|
||||||
|
return $scope;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve list of decisions
|
* Retrieve list of decisions
|
||||||
*
|
*
|
||||||
@ -21,29 +69,50 @@ class DecisionsController extends ApiControllerBase
|
|||||||
* @throws \OPNsense\Base\ModelException
|
* @throws \OPNsense\Base\ModelException
|
||||||
* @throws \ReflectionException
|
* @throws \ReflectionException
|
||||||
*/
|
*/
|
||||||
public function getAction()
|
public function searchAction(): array
|
||||||
{
|
{
|
||||||
$result = json_decode(trim((new Backend())->configdRun("crowdsec decisions-list")), true);
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec decisions-list")), true);
|
||||||
if ($result !== null) {
|
if ($result === null) {
|
||||||
// only return valid json type responses
|
return ["message" => "unable to retrieve data"];
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
return ["message" => "unable to list decisions"];
|
|
||||||
|
$decisions = unrollDecisions($result);
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($decisions as $dec) {
|
||||||
|
$alert_source = $dec['alert_source'] ?? [];
|
||||||
|
|
||||||
|
$rows[] = [
|
||||||
|
'id' => $dec['id'],
|
||||||
|
'source' => $dec['origin'] ?? '',
|
||||||
|
'scope_value' => $this->formatScopeValue($dec),
|
||||||
|
'reason' => $dec['scenario'] ?? '',
|
||||||
|
'action' => $dec['type'] ?? '',
|
||||||
|
'country' => $alert_source['cn'] ?? '',
|
||||||
|
'as' => $alert_source['as_name'] ?? '',
|
||||||
|
'events_count' => $dec['alert_events_count'] ?? '',
|
||||||
|
'expiration' => $dec['duration'] ?? '',
|
||||||
|
'alert_id' => $dec['alert_id'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteAction($decision_id)
|
public function delAction($decision_id): array
|
||||||
{
|
{
|
||||||
if ($this->request->isDelete()) {
|
if ($this->request->isPost()) {
|
||||||
$result = (new Backend())->configdRun("crowdsec decisions-delete ${decision_id}");
|
$result = (new Backend())->configdRun("crowdsec decisions-delete {$decision_id}");
|
||||||
if ($result !== null) {
|
if ($result === null) {
|
||||||
// why does the action return \n\n for empty output?
|
return ["result" => "deleted"];
|
||||||
if (trim($result) === '') {
|
|
||||||
return ["message" => "OK"];
|
|
||||||
}
|
|
||||||
// TODO handle error
|
|
||||||
return ["message" => result];
|
|
||||||
}
|
}
|
||||||
return ["message" => "OK"];
|
|
||||||
|
// why does the action return \n\n for empty output?
|
||||||
|
if (trim($result) === '') {
|
||||||
|
return ["result" => "deleted"];
|
||||||
|
}
|
||||||
|
// TODO assume not found, should handle other errors
|
||||||
|
return ["result" => "not found"];
|
||||||
} else {
|
} else {
|
||||||
$this->response->setStatusCode(405, "Method Not Allowed");
|
$this->response->setStatusCode(405, "Method Not Allowed");
|
||||||
$this->response->setHeader("Allow", "DELETE");
|
$this->response->setHeader("Allow", "DELETE");
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
|
||||||
|
|
||||||
namespace OPNsense\CrowdSec\Api;
|
|
||||||
|
|
||||||
use OPNsense\Base\ApiControllerBase;
|
|
||||||
use OPNsense\CrowdSec\CrowdSec;
|
|
||||||
use OPNsense\Core\Backend;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @package OPNsense\CrowdSec
|
|
||||||
*/
|
|
||||||
class HubController extends ApiControllerBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Retrieve the registered hub items
|
|
||||||
*
|
|
||||||
* @return dictionary of items, by type
|
|
||||||
* @throws \OPNsense\Base\ModelException
|
|
||||||
* @throws \ReflectionException
|
|
||||||
*/
|
|
||||||
public function getAction()
|
|
||||||
{
|
|
||||||
$result = json_decode(trim((new Backend())->configdRun("crowdsec hub-items")), true);
|
|
||||||
if ($result !== null) {
|
|
||||||
// only return valid json type responses
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
return ["message" => "unable to list hub items"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,7 +6,6 @@
|
|||||||
namespace OPNsense\CrowdSec\Api;
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
use OPNsense\Base\ApiControllerBase;
|
use OPNsense\Base\ApiControllerBase;
|
||||||
use OPNsense\CrowdSec\CrowdSec;
|
|
||||||
use OPNsense\Core\Backend;
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,19 +14,32 @@ use OPNsense\Core\Backend;
|
|||||||
class MachinesController extends ApiControllerBase
|
class MachinesController extends ApiControllerBase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Retrieve list of registered machines
|
* Retrieve list of machines
|
||||||
*
|
*
|
||||||
* @return array of machines
|
* @return array of machines
|
||||||
* @throws \OPNsense\Base\ModelException
|
* @throws \OPNsense\Base\ModelException
|
||||||
* @throws \ReflectionException
|
* @throws \ReflectionException
|
||||||
*/
|
*/
|
||||||
public function getAction()
|
public function searchAction(): array
|
||||||
{
|
{
|
||||||
$result = json_decode(trim((new Backend())->configdRun("crowdsec machines-list")), true);
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec machines-list")), true);
|
||||||
if ($result !== null) {
|
if ($result === null) {
|
||||||
// only return valid json type responses
|
return ["message" => "unable to retrieve data"];
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
return ["message" => "unable to list machines"];
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($result as $machine) {
|
||||||
|
$rows[] = [
|
||||||
|
'name' => $machine['machineId'],
|
||||||
|
'ip_address' => $machine['ipAddress'] ?? '',
|
||||||
|
'version' => $machine['version'] ?? '',
|
||||||
|
'validated' => $machine['isValidated'] ?? false,
|
||||||
|
'created' => $machine['created_at'] ?? '',
|
||||||
|
'last_seen' => $machine['last_heartbeat'] ?? '',
|
||||||
|
'os' => $machine['os'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
|
use OPNsense\Base\ApiControllerBase;
|
||||||
|
use OPNsense\CrowdSec\Util;
|
||||||
|
|
||||||
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class ParsersController extends ApiControllerBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve the installed parsers
|
||||||
|
*
|
||||||
|
* @return dictionary of items, by type
|
||||||
|
* @throws \OPNsense\Base\ModelException
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
|
public function searchAction(): array
|
||||||
|
{
|
||||||
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec parsers-list")), true);
|
||||||
|
if ($result === null) {
|
||||||
|
return ["message" => "unable to retrieve data"];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $result["parsers"];
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$rows[] = [
|
||||||
|
'name' => $item['name'],
|
||||||
|
'status' => $item['status'] ?? '',
|
||||||
|
'local_version' => $item['local_version'] ?? '',
|
||||||
|
'local_path' => Util::trimLocalPath($item['local_path'] ?? ''),
|
||||||
|
'description' => $item['description'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
|
use OPNsense\Base\ApiControllerBase;
|
||||||
|
use OPNsense\CrowdSec\Util;
|
||||||
|
|
||||||
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class PostoverflowsController extends ApiControllerBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve the installed postoverflows
|
||||||
|
*
|
||||||
|
* @return dictionary of items, by type
|
||||||
|
* @throws \OPNsense\Base\ModelException
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
|
public function searchAction(): array
|
||||||
|
{
|
||||||
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec postoverflows-list")), true);
|
||||||
|
if ($result === null) {
|
||||||
|
return ["message" => "unable to retrieve data"];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $result["postoverflows"];
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$rows[] = [
|
||||||
|
'name' => $item['name'],
|
||||||
|
'status' => $item['status'] ?? '',
|
||||||
|
'local_version' => $item['local_version'] ?? '',
|
||||||
|
'local_path' => Util::trimLocalPath($item['local_path'] ?? ''),
|
||||||
|
'description' => $item['description'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
|
use OPNsense\Base\ApiControllerBase;
|
||||||
|
use OPNsense\CrowdSec\Util;
|
||||||
|
|
||||||
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class ScenariosController extends ApiControllerBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve the installed scenarios
|
||||||
|
*
|
||||||
|
* @return dictionary of items, by type
|
||||||
|
* @throws \OPNsense\Base\ModelException
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
|
public function searchAction(): array
|
||||||
|
{
|
||||||
|
$result = json_decode(trim((new Backend())->configdRun("crowdsec scenarios-list")), true);
|
||||||
|
if ($result === null) {
|
||||||
|
return ["message" => "unable to retrieve data"];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $result["scenarios"];
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$rows[] = [
|
||||||
|
'name' => $item['name'],
|
||||||
|
'status' => $item['status'] ?? '',
|
||||||
|
'local_version' => $item['local_version'] ?? '',
|
||||||
|
'local_path' => Util::trimLocalPath($item['local_path'] ?? ''),
|
||||||
|
'description' => $item['description'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchRecordsetBase($rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,8 +16,10 @@ class ServiceController extends ApiControllerBase
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* reconfigure CrowdSec
|
* reconfigure CrowdSec
|
||||||
|
*
|
||||||
|
* @return array Status result
|
||||||
*/
|
*/
|
||||||
public function reloadAction()
|
public function reloadAction(): array
|
||||||
{
|
{
|
||||||
$status = "failed";
|
$status = "failed";
|
||||||
if ($this->request->isPost()) {
|
if ($this->request->isPost()) {
|
||||||
@ -36,7 +38,11 @@ class ServiceController extends ApiControllerBase
|
|||||||
/**
|
/**
|
||||||
* Retrieve status of crowdsec
|
* Retrieve status of crowdsec
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array{
|
||||||
|
* status: string,
|
||||||
|
* crowdsec-status: string,
|
||||||
|
* crowdsec-firewall-status: string
|
||||||
|
* }
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function statusAction()
|
public function statusAction()
|
||||||
@ -44,24 +50,30 @@ class ServiceController extends ApiControllerBase
|
|||||||
$backend = new Backend();
|
$backend = new Backend();
|
||||||
$response = $backend->configdRun("crowdsec crowdsec-status");
|
$response = $backend->configdRun("crowdsec crowdsec-status");
|
||||||
|
|
||||||
$status = "unknown";
|
$crowdsec_status = "unknown";
|
||||||
if (strpos($response, "not running") > 0) {
|
if (strpos($response, "not running") !== false) {
|
||||||
$status = "stopped";
|
$crowdsec_status = "stopped";
|
||||||
} elseif (strpos($response, "is running") > 0) {
|
} elseif (strpos($response, "is running") !== false) {
|
||||||
$status = "running";
|
$crowdsec_status = "running";
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = $backend->configdRun("crowdsec crowdsec-firewall-status");
|
$response = $backend->configdRun("crowdsec crowdsec-firewall-status");
|
||||||
|
|
||||||
$firewall_status = "unknown";
|
$firewall_status = "unknown";
|
||||||
if (strpos($response, "not running") > 0) {
|
if (strpos($response, "not running") !== false) {
|
||||||
$firewall_status = "stopped";
|
$firewall_status = "stopped";
|
||||||
} elseif (strpos($response, "is running") > 0) {
|
} elseif (strpos($response, "is running") !== false) {
|
||||||
$firewall_status = "running";
|
$firewall_status = "running";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$status = "unknown";
|
||||||
|
if ($crowdsec_status == $firewall_status) {
|
||||||
|
$status = $crowdsec_status;
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
"crowdsec-status" => $status,
|
"status" => $status,
|
||||||
|
"crowdsec-status" => $crowdsec_status,
|
||||||
"crowdsec-firewall-status" => $firewall_status,
|
"crowdsec-firewall-status" => $firewall_status,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
namespace OPNsense\CrowdSec\Api;
|
namespace OPNsense\CrowdSec\Api;
|
||||||
|
|
||||||
use OPNsense\Base\ApiControllerBase;
|
use OPNsense\Base\ApiControllerBase;
|
||||||
use OPNsense\CrowdSec\CrowdSec;
|
|
||||||
use OPNsense\Core\Backend;
|
use OPNsense\Core\Backend;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,7 +20,7 @@ class VersionController extends ApiControllerBase
|
|||||||
* @throws \OPNsense\Base\ModelException
|
* @throws \OPNsense\Base\ModelException
|
||||||
* @throws \ReflectionException
|
* @throws \ReflectionException
|
||||||
*/
|
*/
|
||||||
public function getAction()
|
public function getAction(): string
|
||||||
{
|
{
|
||||||
return (new Backend())->configdRun("crowdsec version");
|
return (new Backend())->configdRun("crowdsec version");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AppsecconfigsController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class AppsecconfigsController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction(): void
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/appsecconfigs');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AppsecrulesController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class AppsecrulesController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction(): void
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/appsecrules');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BouncersController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class BouncersController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction(): void
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/bouncers');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CollectionsController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class CollectionsController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction(): void
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/collections');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DecisionsController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class DecisionsController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction(): void
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/decisions');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,7 @@ namespace OPNsense\CrowdSec;
|
|||||||
*/
|
*/
|
||||||
class GeneralController extends \OPNsense\Base\IndexController
|
class GeneralController extends \OPNsense\Base\IndexController
|
||||||
{
|
{
|
||||||
public function indexAction()
|
public function indexAction(): void
|
||||||
{
|
{
|
||||||
$this->view->pick('OPNsense/CrowdSec/general');
|
$this->view->pick('OPNsense/CrowdSec/general');
|
||||||
$this->view->generalForm = $this->getForm("general");
|
$this->view->generalForm = $this->getForm("general");
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class MachinesController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class MachinesController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction(): void
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/machines');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,7 @@ namespace OPNsense\CrowdSec;
|
|||||||
*/
|
*/
|
||||||
class OverviewController extends \OPNsense\Base\IndexController
|
class OverviewController extends \OPNsense\Base\IndexController
|
||||||
{
|
{
|
||||||
public function indexAction()
|
public function indexAction(): void
|
||||||
{
|
{
|
||||||
$this->view->pick('OPNsense/CrowdSec/overview');
|
$this->view->pick('OPNsense/CrowdSec/overview');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ParsersController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class ParsersController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction(): void
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/parsers');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PostoverflowsController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class PostoverflowsController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction()
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/postoverflows');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net>
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ScenariosController
|
||||||
|
* @package OPNsense\CrowdSec
|
||||||
|
*/
|
||||||
|
class ScenariosController extends \OPNsense\Base\IndexController
|
||||||
|
{
|
||||||
|
public function indexAction(): void
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/CrowdSec/scenarios');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OPNsense\CrowdSec;
|
||||||
|
|
||||||
|
class Util
|
||||||
|
{
|
||||||
|
public static function trimLocalPath($local_path): string
|
||||||
|
{
|
||||||
|
$prefix = '/usr/local/etc/crowdsec/';
|
||||||
|
if (str_starts_with($local_path, $prefix)) {
|
||||||
|
return substr($local_path, strlen($prefix));
|
||||||
|
}
|
||||||
|
return $local_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<model>
|
<model>
|
||||||
<mount>//OPNsense/crowdsec/general</mount>
|
<mount>//OPNsense/crowdsec/general</mount>
|
||||||
<description>CrowdSec general configuration</description>
|
<description>CrowdSec general configuration</description>
|
||||||
<version>1.0.10</version>
|
<version>1.0.11</version>
|
||||||
<items>
|
<items>
|
||||||
|
|
||||||
<agent_enabled type="BooleanField">
|
<agent_enabled type="BooleanField">
|
||||||
|
|||||||
@ -2,7 +2,16 @@
|
|||||||
<Services>
|
<Services>
|
||||||
<CrowdSec cssClass="fa fa-globe fa-fw">
|
<CrowdSec cssClass="fa fa-globe fa-fw">
|
||||||
<Settings order="1" url="/ui/crowdsec/general/index"/>
|
<Settings order="1" url="/ui/crowdsec/general/index"/>
|
||||||
<Overview order="2" url="/ui/crowdsec/overview"/>
|
<Machines order="2" url="/ui/crowdsec/machines/index"/>
|
||||||
|
<Bouncers order="3" url="/ui/crowdsec/bouncers/index"/>
|
||||||
|
<Alerts order="5" url="/ui/crowdsec/alerts/index"/>
|
||||||
|
<Decisions order="6" url="/ui/crowdsec/decisions/index"/>
|
||||||
|
<Collections order="7" url="/ui/crowdsec/collections/index"/>
|
||||||
|
<Scenarios order="8" url="/ui/crowdsec/scenarios/index"/>
|
||||||
|
<Parsers order="9" url="/ui/crowdsec/parsers/index"/>
|
||||||
|
<Postoverflows order="10" url="/ui/crowdsec/postoverflows/index"/>
|
||||||
|
<Appsecrules order="11" url="/ui/crowdsec/appsecrules/index"/>
|
||||||
|
<Appsecconfigs order="12" url="/ui/crowdsec/appsecconfigs/index"/>
|
||||||
</CrowdSec>
|
</CrowdSec>
|
||||||
</Services>
|
</Services>
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@ -0,0 +1,62 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
const decisionsByType = function(decisions) {
|
||||||
|
const dectypes = {};
|
||||||
|
if (!decisions) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
decisions.map(function (decision) {
|
||||||
|
// TODO ignore negative expiration?
|
||||||
|
dectypes[decision.type] = dectypes[decision.type]
|
||||||
|
? dectypes[decision.type] + 1
|
||||||
|
: 1;
|
||||||
|
});
|
||||||
|
let ret = '';
|
||||||
|
for (const type in dectypes) {
|
||||||
|
if (ret !== '') {
|
||||||
|
ret += ' ';
|
||||||
|
}
|
||||||
|
ret += type + ':' + dectypes[type];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
$("#cscli_alerts").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/alerts/search/',
|
||||||
|
options: {
|
||||||
|
selection: false,
|
||||||
|
multiSelect: false,
|
||||||
|
formatters: {
|
||||||
|
"created": CrowdSec.formatters.datetime,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table id="cscli_alerts" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="id" data-type="numeric" data-order="asc">ID</th>
|
||||||
|
<th data-column-id="value">Value</th>
|
||||||
|
<th data-column-id="reason">Reason</th>
|
||||||
|
<th data-column-id="country">Country</th>
|
||||||
|
<th data-column-id="as">AS</th>
|
||||||
|
<th data-column-id="decisions">Decisions</th>
|
||||||
|
<th data-column-id="created_at" data-formatter="created">Created</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#cscli_appsecconfigs").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/appsecconfigs/search/',
|
||||||
|
options: {
|
||||||
|
selection: false,
|
||||||
|
multiSelect: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table id="cscli_appsecconfigs" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="name">Name</th>
|
||||||
|
<th data-column-id="status">Status</th>
|
||||||
|
<th data-column-id="local_version">Version</th>
|
||||||
|
<th data-column-id="local_path" data-formatter="localpath" data-visible="false">Path</th>
|
||||||
|
<th data-column-id="description">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#cscli_appsecrules").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/appsecrules/search/',
|
||||||
|
options: {
|
||||||
|
selection: false,
|
||||||
|
multiSelect: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table id="cscli_appsecrules" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="name">Name</th>
|
||||||
|
<th data-column-id="status">Status</th>
|
||||||
|
<th data-column-id="local_version">Version</th>
|
||||||
|
<th data-column-id="local_path" data-formatter="localpath" data-visible="false">Path</th>
|
||||||
|
<th data-column-id="description">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#cscli_bouncers").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/bouncers/search/',
|
||||||
|
options: {
|
||||||
|
selection: false,
|
||||||
|
multiSelect: false,
|
||||||
|
formatters: {
|
||||||
|
"created": CrowdSec.formatters.datetime,
|
||||||
|
"last_seen": CrowdSec.formatters.datetime,
|
||||||
|
"valid": CrowdSec.formatters.yesno,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table id="cscli_bouncers" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="name">Name</th>
|
||||||
|
<th data-column-id="type">Type</th>
|
||||||
|
<th data-column-id="version">Version</th>
|
||||||
|
<th data-column-id="created" data-formatter="created" data-visible="false">Created</th>
|
||||||
|
<th data-column-id="valid" data-formatter="valid">Valid</th>
|
||||||
|
<th data-column-id="ip_address">IP Address</th>
|
||||||
|
<th data-column-id="last_seen" data-formatter="last_seen">Last Seen</th>
|
||||||
|
<th data-column-id="os" data-visible="false">OS</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#cscli_collections").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/collections/search/',
|
||||||
|
options: {
|
||||||
|
selection: false,
|
||||||
|
multiSelect: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table id="cscli_collections" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="name">Name</th>
|
||||||
|
<th data-column-id="status">Status</th>
|
||||||
|
<th data-column-id="local_version">Version</th>
|
||||||
|
<th data-column-id="local_path" data-formatter="localpath" data-visible="false">Path</th>
|
||||||
|
<th data-column-id="description">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#cscli_decisions").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/decisions/search/',
|
||||||
|
del: '/api/crowdsec/decisions/del/',
|
||||||
|
datakey: "id",
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
Note: the decisions coming from the CAPI (signals collected by the CrowdSec users) do not appear here.
|
||||||
|
To show them, use <code>cscli decisions list -a</code> in a shell.
|
||||||
|
|
||||||
|
<table id="cscli_decisions" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="id" data-type="numeric" data-visible="false" data-order="asc">ID</th>
|
||||||
|
<th data-column-id="source" data-visible="false">Source</th>
|
||||||
|
<th data-column-id="scope_value">Scope:Value</th>
|
||||||
|
<th data-column-id="reason">Reason</th>
|
||||||
|
<th data-column-id="action" data-visible="false">Action</th>
|
||||||
|
<th data-column-id="country">Country</th>
|
||||||
|
<th data-column-id="as">AS</th>
|
||||||
|
<th data-column-id="events_count" data-type="numeric">Events</th>
|
||||||
|
<th data-column-id="expiration">Expiration</th>
|
||||||
|
<th data-column-id="alert_id" data-type="numeric" data-visible="false">Alert ID</th>
|
||||||
|
<th data-column-id="commands" data-formatter="commands" data-sortable="false">Commands</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td/>
|
||||||
|
<td>
|
||||||
|
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default">
|
||||||
|
<span class="fa fa-trash-o fa-fw"></span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#cscli_machines").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/machines/search/',
|
||||||
|
options: {
|
||||||
|
selection: false,
|
||||||
|
multiSelect: false,
|
||||||
|
formatters: {
|
||||||
|
"created": CrowdSec.formatters.datetime,
|
||||||
|
"last_seen": CrowdSec.formatters.datetime,
|
||||||
|
"validated": CrowdSec.formatters.yesno,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table id="cscli_machines" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="name">Name</th>
|
||||||
|
<th data-column-id="version">Version</th>
|
||||||
|
<th data-column-id="validated" data-formatter="validated">Validated?</th>
|
||||||
|
<th data-column-id="ip_address">IP Address</th>
|
||||||
|
<th data-column-id="created" data-formatter="created" data-visible="false">Created</th>
|
||||||
|
<th data-column-id="last_seen" data-formatter="last_seen">Last Seen</th>
|
||||||
|
<th data-column-id="os" data-visible="false">OS</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -1,246 +0,0 @@
|
|||||||
{# SPDX-License-Identifier: MIT #}
|
|
||||||
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
|
||||||
|
|
||||||
<script src="/ui/js/moment-with-locales.min.js"></script>
|
|
||||||
<script src="/ui/js/CrowdSec/crowdsec.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$(function() {
|
|
||||||
CrowdSec.init();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
.content-box table {
|
|
||||||
table-layout: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.bootgrid-table tr .btn-sm {
|
|
||||||
padding: 2px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.bootgrid-table tr > td {
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.spaced {
|
|
||||||
margin-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.nav>li>a {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Service status: crowdsec <span id="crowdsec-status">...</span> - firewall bouncer <span id="crowdsec-firewall-status">...</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
|
||||||
<li><a data-toggle="tab" id="machines_tab" href="#machines">Machines</a></li>
|
|
||||||
<li><a data-toggle="tab" id="bouncers_tab" href="#bouncers">Bouncers</a></li>
|
|
||||||
<li class="spaced"><a data-toggle="tab" id="collections_tab" href="#collections">Collections</a></li>
|
|
||||||
<li><a data-toggle="tab" id="scenarios_tab" href="#scenarios">Scenarios</a></li>
|
|
||||||
<li><a data-toggle="tab" id="parsers_tab" href="#parsers">Parsers</a></li>
|
|
||||||
<li><a data-toggle="tab" id="postoverflows_tab" href="#postoverflows">Postoverflows</a></li>
|
|
||||||
<li class="spaced"><a data-toggle="tab" id="alerts_tab" href="#alerts">Alerts</a></li>
|
|
||||||
<li><a data-toggle="tab" id="decisions_tab" href="#decisions">Decisions</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="tab-content content-box">
|
|
||||||
|
|
||||||
<div id="machines" class="tab-pane fade in">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-column-id="name" data-order="asc">Name</th>
|
|
||||||
<th data-column-id="ip_address">IP Address</th>
|
|
||||||
<th data-column-id="last_update" data-formatter="datetime">Last Update</th>
|
|
||||||
<th data-column-id="validated" data-formatter="yesno" data-searchable="false">Validated?</th>
|
|
||||||
<th data-column-id="version">Version</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="bouncers" class="tab-pane fade in">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-column-id="name" data-order="asc">Name</th>
|
|
||||||
<th data-column-id="ip_address">IP Address</th>
|
|
||||||
<th data-column-id="valid" data-formatter="yesno" data-searchable="false">Valid</th>
|
|
||||||
<th data-column-id="last_pull" data-formatter="datetime">Last API Pull</th>
|
|
||||||
<th data-column-id="type">Type</th>
|
|
||||||
<th data-column-id="version">Version</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="collections" class="tab-pane fade in">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-column-id="name" data-order="asc">Collection</th>
|
|
||||||
<th data-column-id="status">Status</th>
|
|
||||||
<th data-column-id="local_version">Version</th>
|
|
||||||
<th data-visible="false" data-column-id="local_path">Path</th>
|
|
||||||
<th data-column-id="description">Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="scenarios" class="tab-pane fade in">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-column-id="name" data-order="asc">Scenario</th>
|
|
||||||
<th data-column-id="status">Status</th>
|
|
||||||
<th data-column-id="local_version">Version</th>
|
|
||||||
<th data-visible="false" data-column-id="local_path">Path</th>
|
|
||||||
<th data-column-id="description">Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="parsers" class="tab-pane fade in">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-column-id="name" data-order="asc">Parser</th>
|
|
||||||
<th data-column-id="status">Status</th>
|
|
||||||
<th data-column-id="local_version">Version</th>
|
|
||||||
<th data-visible="false" data-column-id="local_path">Path</th>
|
|
||||||
<th data-column-id="description">Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="postoverflows" class="tab-pane fade in">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-column-id="name" data-order="asc">Postoverflow</th>
|
|
||||||
<th data-column-id="status">Status</th>
|
|
||||||
<th data-column-id="local_version">Version</th>
|
|
||||||
<th data-visible="false" data-column-id="local_path">Path</th>
|
|
||||||
<th data-column-id="description">Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="alerts" class="tab-pane fade in">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-column-id="id" data-type="numeric" data-order="asc">ID</th>
|
|
||||||
<th data-column-id="value">Value</th>
|
|
||||||
<th data-column-id="reason">Reason</th>
|
|
||||||
<th data-column-id="country">Country</th>
|
|
||||||
<th data-column-id="as">AS</th>
|
|
||||||
<th data-column-id="decisions">Decisions</th>
|
|
||||||
<th data-column-id="created_at" data-formatter="datetime">Created At</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="decisions" class="tab-pane fade in">
|
|
||||||
Note: the decisions coming from the CAPI (signals collected by the CrowdSec users) do not appear here.
|
|
||||||
To show them, use <code>cscli decisions list -a</code> in a shell.
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-column-id="delete" data-formatter="delete"
|
|
||||||
data-visible-in-selection="false"></th>
|
|
||||||
<th data-column-id="id" data-visible="false" data-identifier="true" data-type="numeric"
|
|
||||||
data-order="asc">ID</th>
|
|
||||||
<th data-visible="false" data-column-id="source">Source</th>
|
|
||||||
<th data-column-id="scope_value">Scope:Value</th>
|
|
||||||
<th data-column-id="reason">Reason</th>
|
|
||||||
<th data-visible="false" data-column-id="action">Action</th>
|
|
||||||
<th data-column-id="country">Country</th>
|
|
||||||
<th data-column-id="as">AS</th>
|
|
||||||
<th data-column-id="events_count" data-type="numeric">Events</th>
|
|
||||||
<th data-column-id="expiration" data-formatter="duration">Expiration</th>
|
|
||||||
<th data-visible="false" data-column-id="alert_id" data-type="numeric">Alert ID</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Modal popup to confirm decision deletion -->
|
|
||||||
<div class="modal fade" id="remove-decision-modal" tabindex="-1" role="dialog" aria-labelledby="modalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
<h4 class="modal-title" id="modalLabel">Modal Title</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
Modal content...
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">No, cancel</button>
|
|
||||||
<button type="button" class="btn btn-danger" data-dismiss="modal" id="remove-decision-confirm">Yes, delete</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#cscli_parsers").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/parsers/search/',
|
||||||
|
options: {
|
||||||
|
selection: false,
|
||||||
|
multiSelect: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table id="cscli_parsers" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="name">Name</th>
|
||||||
|
<th data-column-id="status">Status</th>
|
||||||
|
<th data-column-id="local_version">Version</th>
|
||||||
|
<th data-column-id="local_path" data-formatter="localpath" data-visible="false">Path</th>
|
||||||
|
<th data-column-id="description">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#cscli_postoverflows").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/postoverflows/search/',
|
||||||
|
options: {
|
||||||
|
selection: false,
|
||||||
|
multiSelect: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table id="cscli_postoverflows" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="name">Name</th>
|
||||||
|
<th data-column-id="status">Status</th>
|
||||||
|
<th data-column-id="local_version">Version</th>
|
||||||
|
<th data-column-id="local_path" data-formatter="localpath" data-visible="false">Path</th>
|
||||||
|
<th data-column-id="description">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
{# SPDX-License-Identifier: MIT #}
|
||||||
|
{# SPDX-FileCopyrightText: © 2021 CrowdSec <info@crowdsec.net> #}
|
||||||
|
|
||||||
|
<script src="/ui/js/moment-with-locales.min.js"></script>
|
||||||
|
<script src="/ui/js/CrowdSec/crowdsec-misc.js"></script>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#cscli_scenarios").UIBootgrid({
|
||||||
|
search: '/api/crowdsec/scenarios/search/',
|
||||||
|
options: {
|
||||||
|
selection: false,
|
||||||
|
multiSelect: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateServiceControlUI('crowdsec');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table id="cscli_scenarios" class="table table-condensed table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-id="name">Name</th>
|
||||||
|
<th data-column-id="status">Status</th>
|
||||||
|
<th data-column-id="local_version">Version</th>
|
||||||
|
<th data-column-id="local_path" data-formatter="localpath" data-visible="false">Path</th>
|
||||||
|
<th data-column-id="description">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
@ -57,10 +57,35 @@ parameters:--id %s
|
|||||||
type:script_output
|
type:script_output
|
||||||
message:crowdsec decisions delete
|
message:crowdsec decisions delete
|
||||||
|
|
||||||
[hub-items]
|
[collections-list]
|
||||||
command:/usr/local/bin/cscli hub list -o json
|
command:/usr/local/bin/cscli collections list -o json
|
||||||
type:script_output
|
type:script_output
|
||||||
message:crowdsec hub list
|
message:crowdsec collections list
|
||||||
|
|
||||||
|
[scenarios-list]
|
||||||
|
command:/usr/local/bin/cscli scenarios list -o json
|
||||||
|
type:script_output
|
||||||
|
message:crowdsec scenarios list
|
||||||
|
|
||||||
|
[parsers-list]
|
||||||
|
command:/usr/local/bin/cscli parsers list -o json
|
||||||
|
type:script_output
|
||||||
|
message:crowdsec parsers list
|
||||||
|
|
||||||
|
[postoverflows-list]
|
||||||
|
command:/usr/local/bin/cscli postoverflows list -o json
|
||||||
|
type:script_output
|
||||||
|
message:crowdsec postoverflows list
|
||||||
|
|
||||||
|
[appsec-rules-list]
|
||||||
|
command:/usr/local/bin/cscli appsec-rules list -o json
|
||||||
|
type:script_output
|
||||||
|
message:crowdsec appsec-rules list
|
||||||
|
|
||||||
|
[appsec-configs-list]
|
||||||
|
command:/usr/local/bin/cscli appsec-configs list -o json
|
||||||
|
type:script_output
|
||||||
|
message:crowdsec appsec-configs list
|
||||||
|
|
||||||
[machines-list]
|
[machines-list]
|
||||||
command:/usr/local/bin/cscli machines list -o json
|
command:/usr/local/bin/cscli machines list -o json
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
/* global moment, $ */
|
||||||
|
/* exported CrowdSec */
|
||||||
|
/* eslint no-undef: "error" */
|
||||||
|
/* eslint semi: "error" */
|
||||||
|
|
||||||
|
const CrowdSec = (function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function _humanizeDate(text) {
|
||||||
|
return moment(text).fromNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatters = {
|
||||||
|
yesno: function(column, row) {
|
||||||
|
const val = row[column.id];
|
||||||
|
if (val) {
|
||||||
|
return '<i class="fa fa-check text-success"></i>';
|
||||||
|
} else {
|
||||||
|
return '<i class="fa fa-times text-danger"></i>';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
datetime: function (column, row) {
|
||||||
|
const val = row[column.id];
|
||||||
|
const parsed = moment(val);
|
||||||
|
if (!val) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (!parsed.isValid()) {
|
||||||
|
console.error('Cannot parse timestamp: %s', val);
|
||||||
|
return '???';
|
||||||
|
}
|
||||||
|
return $('<div>')
|
||||||
|
.attr({
|
||||||
|
'data-toggle': 'tooltip',
|
||||||
|
'data-placement': 'left',
|
||||||
|
title: parsed.format(),
|
||||||
|
})
|
||||||
|
.text(_humanizeDate(val))
|
||||||
|
.prop('outerHTML');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
formatters: formatters,
|
||||||
|
};
|
||||||
|
})();
|
||||||
@ -1,473 +0,0 @@
|
|||||||
/* global moment, $ */
|
|
||||||
/* exported CrowdSec */
|
|
||||||
/* eslint no-undef: "error" */
|
|
||||||
/* eslint semi: "error" */
|
|
||||||
|
|
||||||
const CrowdSec = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const crowdsec_path = '/usr/local/etc/crowdsec/';
|
|
||||||
const _refreshTemplate =
|
|
||||||
'<button class="btn btn-default" type="button" title="Refresh"><span class="icon fa fa-refresh"></span></button>';
|
|
||||||
|
|
||||||
const _dataFormatters = {
|
|
||||||
yesno: function (column, row) {
|
|
||||||
return _yesno2html(row[column.id]);
|
|
||||||
},
|
|
||||||
|
|
||||||
delete: function (column, row) {
|
|
||||||
const val = row.id;
|
|
||||||
if (isNaN(val)) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
'<button type="button" class="btn btn-secondary btn-sm" value="' +
|
|
||||||
val +
|
|
||||||
'" onclick="CrowdSec.deleteDecision(' +
|
|
||||||
val +
|
|
||||||
')"><i class="fa fa-trash" /></button>'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
duration: function (column, row) {
|
|
||||||
const duration = row[column.id];
|
|
||||||
if (!duration) {
|
|
||||||
return 'n/a';
|
|
||||||
}
|
|
||||||
return $('<div>')
|
|
||||||
.attr({
|
|
||||||
'data-toggle': 'tooltip',
|
|
||||||
'data-placement': 'left',
|
|
||||||
title: duration,
|
|
||||||
})
|
|
||||||
.text(_humanizeDuration(duration))
|
|
||||||
.prop('outerHTML');
|
|
||||||
},
|
|
||||||
|
|
||||||
datetime: function (column, row) {
|
|
||||||
const dt = row[column.id];
|
|
||||||
const parsed = moment(dt);
|
|
||||||
if (!dt) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if (!parsed.isValid()) {
|
|
||||||
console.error('Cannot parse timestamp: %s', dt);
|
|
||||||
return '???';
|
|
||||||
}
|
|
||||||
return $('<div>')
|
|
||||||
.attr({
|
|
||||||
'data-toggle': 'tooltip',
|
|
||||||
'data-placement': 'left',
|
|
||||||
title: parsed.format(),
|
|
||||||
})
|
|
||||||
.text(_humanizeDate(dt))
|
|
||||||
.prop('outerHTML');
|
|
||||||
},
|
|
||||||
};
|
|
||||||
function _decisionsByType(decisions) {
|
|
||||||
const dectypes = {};
|
|
||||||
if (!decisions) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
decisions.map(function (decision) {
|
|
||||||
// TODO ignore negative expiration?
|
|
||||||
dectypes[decision.type] = dectypes[decision.type]
|
|
||||||
? dectypes[decision.type] + 1
|
|
||||||
: 1;
|
|
||||||
});
|
|
||||||
let ret = '';
|
|
||||||
for (const type in dectypes) {
|
|
||||||
if (ret !== '') {
|
|
||||||
ret += ' ';
|
|
||||||
}
|
|
||||||
ret += type + ':' + dectypes[type];
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _updateFreshness(selector, timestamp) {
|
|
||||||
const $freshness = $(selector).find('.actionBar .freshness');
|
|
||||||
if (timestamp) {
|
|
||||||
$freshness.data('refresh_timestamp', timestamp);
|
|
||||||
} else {
|
|
||||||
timestamp = $freshness.data('refresh_timestamp');
|
|
||||||
}
|
|
||||||
const howlongHuman = '???';
|
|
||||||
if (timestamp) {
|
|
||||||
const howlongms = moment() - moment(timestamp);
|
|
||||||
howlongHuman = moment.duration(howlongms).humanize();
|
|
||||||
}
|
|
||||||
$freshness.text(howlongHuman + ' ago');
|
|
||||||
}
|
|
||||||
|
|
||||||
function _addFreshness(selector) {
|
|
||||||
// this creates one timer per tab
|
|
||||||
const freshnessTemplate =
|
|
||||||
'<span style="float:left">Last refresh: <span class="freshness"></span></span>';
|
|
||||||
$(selector).find('.actionBar').prepend(freshnessTemplate);
|
|
||||||
setInterval(function () {
|
|
||||||
_updateFreshness(selector);
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _refreshTab(selector, url, dataCallback) {
|
|
||||||
$.ajax({
|
|
||||||
url: url,
|
|
||||||
cache: false,
|
|
||||||
success: dataCallback,
|
|
||||||
complete: function () {
|
|
||||||
_updateFreshness(selector, moment());
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _parseDuration(duration) {
|
|
||||||
const re = /(-?)(?:(?:(\d+)h)?(\d+)m)?(\d+).\d+(m?)s/m;
|
|
||||||
const matches = duration.match(re);
|
|
||||||
let seconds = 0;
|
|
||||||
|
|
||||||
if (!matches.length) {
|
|
||||||
throw new Error(
|
|
||||||
'Unable to parse the following duration: ' + duration + '.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (typeof matches[2] !== 'undefined') {
|
|
||||||
seconds += parseInt(matches[2], 10) * 3600; // hours
|
|
||||||
}
|
|
||||||
if (typeof matches[3] !== 'undefined') {
|
|
||||||
seconds += parseInt(matches[3], 10) * 60; // minutes
|
|
||||||
}
|
|
||||||
if (typeof matches[4] !== 'undefined') {
|
|
||||||
seconds += parseInt(matches[4], 10); // seconds
|
|
||||||
}
|
|
||||||
if (parseInt(matches[5], 10) === 'm') {
|
|
||||||
// units in milliseconds
|
|
||||||
seconds *= 0.001;
|
|
||||||
}
|
|
||||||
if (parseInt(matches[1], 10) === '-') {
|
|
||||||
// negative
|
|
||||||
seconds = -seconds;
|
|
||||||
}
|
|
||||||
return seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _humanizeDate(text) {
|
|
||||||
return moment(text).fromNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _humanizeDuration(text) {
|
|
||||||
return moment.duration(_parseDuration(text), 'seconds').humanize();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _yesno2html(val) {
|
|
||||||
if (val) {
|
|
||||||
return '<i class="fa fa-check text-success"></i>';
|
|
||||||
} else {
|
|
||||||
return '<i class="fa fa-times text-danger"></i>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initTab(selector, url, dataCallback) {
|
|
||||||
const $tab = $(selector);
|
|
||||||
if ($tab.find('table.bootgrid-table').length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$tab
|
|
||||||
.find('table')
|
|
||||||
.on('initialized.rs.jquery.bootgrid', function () {
|
|
||||||
$(_refreshTemplate)
|
|
||||||
.on('click', function () {
|
|
||||||
_refreshTab(selector, url, dataCallback);
|
|
||||||
})
|
|
||||||
.insertBefore($tab.find('.actionBar .actions .dropdown:first'));
|
|
||||||
_addFreshness(selector);
|
|
||||||
_refreshTab(selector, url, dataCallback);
|
|
||||||
})
|
|
||||||
.bootgrid({
|
|
||||||
caseSensitive: false,
|
|
||||||
formatters: _dataFormatters,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initStatusMachines() {
|
|
||||||
const url = '/api/crowdsec/machines/get';
|
|
||||||
const id = '#machines';
|
|
||||||
const dataCallback = function (data) {
|
|
||||||
const rows = [];
|
|
||||||
data.map(function (row) {
|
|
||||||
rows.push({
|
|
||||||
name: row.machineId,
|
|
||||||
ip_address: row.ipAddress || ' ',
|
|
||||||
last_update: row.updated_at || ' ',
|
|
||||||
validated: row.isValidated,
|
|
||||||
version: row.version || ' ',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$(id + ' table')
|
|
||||||
.bootgrid('clear')
|
|
||||||
.bootgrid('append', rows);
|
|
||||||
};
|
|
||||||
_initTab(id, url, dataCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initStatusCollections() {
|
|
||||||
const url = '/api/crowdsec/hub/get';
|
|
||||||
const id = '#collections';
|
|
||||||
const dataCallback = function (data) {
|
|
||||||
const rows = [];
|
|
||||||
data.collections.map(function (row) {
|
|
||||||
rows.push({
|
|
||||||
name: row.name,
|
|
||||||
status: row.status,
|
|
||||||
local_version: row.local_version || ' ',
|
|
||||||
local_path: row.local_path
|
|
||||||
? row.local_path.replace(crowdsec_path, '')
|
|
||||||
: ' ',
|
|
||||||
description: row.description || ' ',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$(id + ' table')
|
|
||||||
.bootgrid('clear')
|
|
||||||
.bootgrid('append', rows);
|
|
||||||
};
|
|
||||||
_initTab(id, url, dataCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initStatusScenarios() {
|
|
||||||
const url = '/api/crowdsec/hub/get';
|
|
||||||
const id = '#scenarios';
|
|
||||||
const dataCallback = function (data) {
|
|
||||||
const rows = [];
|
|
||||||
data.scenarios.map(function (row) {
|
|
||||||
rows.push({
|
|
||||||
name: row.name,
|
|
||||||
status: row.status,
|
|
||||||
local_version: row.local_version || ' ',
|
|
||||||
local_path: row.local_path
|
|
||||||
? row.local_path.replace(crowdsec_path, '')
|
|
||||||
: ' ',
|
|
||||||
description: row.description || ' ',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$(id + ' table')
|
|
||||||
.bootgrid('clear')
|
|
||||||
.bootgrid('append', rows);
|
|
||||||
};
|
|
||||||
_initTab(id, url, dataCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initStatusParsers() {
|
|
||||||
const url = '/api/crowdsec/hub/get';
|
|
||||||
const id = '#parsers';
|
|
||||||
const dataCallback = function (data) {
|
|
||||||
const rows = [];
|
|
||||||
data.parsers.map(function (row) {
|
|
||||||
rows.push({
|
|
||||||
name: row.name,
|
|
||||||
status: row.status,
|
|
||||||
local_version: row.local_version || ' ',
|
|
||||||
local_path: row.local_path
|
|
||||||
? row.local_path.replace(crowdsec_path, '')
|
|
||||||
: ' ',
|
|
||||||
description: row.description || ' ',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$(id + ' table')
|
|
||||||
.bootgrid('clear')
|
|
||||||
.bootgrid('append', rows);
|
|
||||||
};
|
|
||||||
_initTab(id, url, dataCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initStatusPostoverflows() {
|
|
||||||
const url = '/api/crowdsec/hub/get';
|
|
||||||
const id = '#postoverflows';
|
|
||||||
const dataCallback = function (data) {
|
|
||||||
const rows = [];
|
|
||||||
data.postoverflows.map(function (row) {
|
|
||||||
rows.push({
|
|
||||||
name: row.name,
|
|
||||||
status: row.status,
|
|
||||||
local_version: row.local_version || ' ',
|
|
||||||
local_path: row.local_path
|
|
||||||
? row.local_path.replace(crowdsec_path, '')
|
|
||||||
: ' ',
|
|
||||||
description: row.description || ' ',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$(id + ' table')
|
|
||||||
.bootgrid('clear')
|
|
||||||
.bootgrid('append', rows);
|
|
||||||
};
|
|
||||||
_initTab(id, url, dataCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initStatusBouncers() {
|
|
||||||
const url = '/api/crowdsec/bouncers/get';
|
|
||||||
const id = '#bouncers';
|
|
||||||
const dataCallback = function (data) {
|
|
||||||
const rows = [];
|
|
||||||
data.map(function (row) {
|
|
||||||
// TODO - remove || ' ' later, it was fixed for 1.3.3
|
|
||||||
rows.push({
|
|
||||||
name: row.name,
|
|
||||||
ip_address: row.ip_address || ' ',
|
|
||||||
valid: row.revoked ? false : true,
|
|
||||||
last_pull: row.last_pull,
|
|
||||||
type: row.type || ' ',
|
|
||||||
version: row.version || ' ',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$(id + ' table')
|
|
||||||
.bootgrid('clear')
|
|
||||||
.bootgrid('append', rows);
|
|
||||||
};
|
|
||||||
_initTab(id, url, dataCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initStatusAlerts() {
|
|
||||||
const url = '/api/crowdsec/alerts/get';
|
|
||||||
const id = '#alerts';
|
|
||||||
const dataCallback = function (data) {
|
|
||||||
const rows = [];
|
|
||||||
data.map(function (row) {
|
|
||||||
rows.push({
|
|
||||||
id: row.id,
|
|
||||||
value:
|
|
||||||
row.source.scope + (row.source.value ? ':' + row.source.value : ''),
|
|
||||||
reason: row.scenario || ' ',
|
|
||||||
country: row.source.cn || ' ',
|
|
||||||
as: row.source.as_name || ' ',
|
|
||||||
decisions: _decisionsByType(row.decisions) || ' ',
|
|
||||||
created_at: row.created_at,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$(id + ' table')
|
|
||||||
.bootgrid('clear')
|
|
||||||
.bootgrid('append', rows);
|
|
||||||
};
|
|
||||||
_initTab(id, url, dataCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initStatusDecisions() {
|
|
||||||
const url = '/api/crowdsec/decisions/get';
|
|
||||||
const id = '#decisions';
|
|
||||||
const dataCallback = function (data) {
|
|
||||||
const rows = [];
|
|
||||||
data.map(function (row) {
|
|
||||||
row.decisions.map(function (decision) {
|
|
||||||
// ignore deleted decisions
|
|
||||||
if (decision.duration.startsWith('-')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rows.push({
|
|
||||||
// search will break on empty values when using .append(). so we use spaces
|
|
||||||
delete: '',
|
|
||||||
id: decision.id,
|
|
||||||
source: decision.origin || ' ',
|
|
||||||
scope_value:
|
|
||||||
decision.scope + (decision.value ? ':' + decision.value : ''),
|
|
||||||
reason: decision.scenario || ' ',
|
|
||||||
action: decision.type || ' ',
|
|
||||||
country: row.source.cn || ' ',
|
|
||||||
as: row.source.as_name || ' ',
|
|
||||||
events_count: row.events_count,
|
|
||||||
// XXX pre-parse duration to seconds, and integer type, for sorting
|
|
||||||
expiration: decision.duration || ' ',
|
|
||||||
alert_id: row.id || ' ',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$(id + ' table')
|
|
||||||
.bootgrid('clear')
|
|
||||||
.bootgrid('append', rows);
|
|
||||||
};
|
|
||||||
_initTab(id, url, dataCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function initService() {
|
|
||||||
$.ajax({
|
|
||||||
url: '/api/crowdsec/service/status',
|
|
||||||
cache: false,
|
|
||||||
success: function (data) {
|
|
||||||
let crowdsecStatus = data['crowdsec-status'];
|
|
||||||
if (crowdsecStatus === 'unknown') {
|
|
||||||
crowdsecStatus = '<span class="text-danger">Unknown</span>';
|
|
||||||
} else {
|
|
||||||
crowdsecStatus = _yesno2html(crowdsecStatus === 'running');
|
|
||||||
}
|
|
||||||
$('#crowdsec-status').html(crowdsecStatus);
|
|
||||||
|
|
||||||
let crowdsecFirewallStatus = data['crowdsec-firewall-status'];
|
|
||||||
if (crowdsecFirewallStatus === 'unknown') {
|
|
||||||
crowdsecFirewallStatus = '<span class="text-danger">Unknown</span>';
|
|
||||||
} else {
|
|
||||||
crowdsecFirewallStatus = _yesno2html(
|
|
||||||
crowdsecFirewallStatus === 'running',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$('#crowdsec-firewall-status').html(crowdsecFirewallStatus);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteDecision(decisionId) {
|
|
||||||
const $modal = $('#remove-decision-modal');
|
|
||||||
$modal.find('.modal-title').text('Delete decision #' + decisionId);
|
|
||||||
$modal.find('.modal-body').text('Are you sure?');
|
|
||||||
$modal.find('#remove-decision-confirm').on('click', function () {
|
|
||||||
$.ajax({
|
|
||||||
// XXX handle errors
|
|
||||||
url: '/api/crowdsec/decisions/delete/' + decisionId,
|
|
||||||
method: 'DELETE',
|
|
||||||
success: function (result) {
|
|
||||||
if (result && result.message === 'OK') {
|
|
||||||
$('#decisions table').bootgrid('remove', [decisionId]);
|
|
||||||
$modal.modal('hide');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$modal.modal('show');
|
|
||||||
}
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
initService();
|
|
||||||
|
|
||||||
$('#machines_tab').on('click', _initStatusMachines);
|
|
||||||
$('#collections_tab').on('click', _initStatusCollections);
|
|
||||||
$('#scenarios_tab').on('click', _initStatusScenarios);
|
|
||||||
$('#parsers_tab').on('click', _initStatusParsers);
|
|
||||||
$('#postoverflows_tab').on('click', _initStatusPostoverflows);
|
|
||||||
$('#bouncers_tab').on('click', _initStatusBouncers);
|
|
||||||
$('#alerts_tab').on('click', _initStatusAlerts);
|
|
||||||
$('#decisions_tab').on('click', _initStatusDecisions);
|
|
||||||
|
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
|
||||||
|
|
||||||
if (window.location.hash) {
|
|
||||||
// activate a tab from the hash, if it exists
|
|
||||||
$(window.location.hash + '_tab').click();
|
|
||||||
} else {
|
|
||||||
// otherwise, machines
|
|
||||||
$('#machines_tab').click();
|
|
||||||
}
|
|
||||||
|
|
||||||
$(window).on('hashchange', function (e) {
|
|
||||||
$(window.location.hash + '_tab').click();
|
|
||||||
});
|
|
||||||
|
|
||||||
// navigation
|
|
||||||
if (window.location.hash !== '') {
|
|
||||||
$('a[href="' + window.location.hash + '"]').click();
|
|
||||||
}
|
|
||||||
$('.nav-tabs a').on('shown.bs.tab', function (e) {
|
|
||||||
history.pushState(null, null, e.target.hash);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
deleteDecision: deleteDecision,
|
|
||||||
init: init,
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
Loading…
x
Reference in New Issue
Block a user