forked from gethomepage/homepage
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: crowdsec widget (gethomepage#3197)
- Loading branch information
Showing
8 changed files
with
169 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
--- | ||
title: Crowdsec | ||
description: Crowdsec Widget Configuration | ||
--- | ||
|
||
Learn more about [Crowdsec](https://crowdsec.net). | ||
|
||
See the [crowdsec docs](https://docs.crowdsec.net/docs/local_api/intro/#machines) for information about registering a machine, | ||
in most instances you can use the default credentials (`/etc/crowdsec/local_api_credentials.yaml`). | ||
|
||
Allowed fields: ["alerts", "bans"] | ||
|
||
```yaml | ||
widget: | ||
type: crowdsec | ||
url: http://crowdsechostorip:port | ||
username: localhost # machine_id in crowdsec | ||
passowrd: password | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { useTranslation } from "next-i18next"; | ||
|
||
import Container from "components/services/widget/container"; | ||
import Block from "components/services/widget/block"; | ||
import useWidgetAPI from "utils/proxy/use-widget-api"; | ||
|
||
export default function Component({ service }) { | ||
const { t } = useTranslation(); | ||
|
||
const { widget } = service; | ||
|
||
const { data: alerts, error: alertsError } = useWidgetAPI(widget, "alerts"); | ||
const { data: bans, error: bansError } = useWidgetAPI(widget, "bans"); | ||
|
||
if (alertsError || bansError) { | ||
return <Container service={service} error={alertsError ?? bansError} />; | ||
} | ||
|
||
if (!alerts || !bans) { | ||
return ( | ||
<Container service={service}> | ||
<Block label="crowdsec.alerts" /> | ||
<Block label="crowdsec.bans" /> | ||
</Container> | ||
); | ||
} | ||
|
||
return ( | ||
<Container service={service}> | ||
<Block label="crowdsec.alerts" value={t("common.number", { value: alerts.length })} /> | ||
<Block label="crowdsec.bans" value={t("common.number", { value: bans.length })} /> | ||
</Container> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import cache from "memory-cache"; | ||
|
||
import { httpProxy } from "utils/proxy/http"; | ||
import { formatApiCall } from "utils/proxy/api-helpers"; | ||
import getServiceWidget from "utils/config/service-helpers"; | ||
import createLogger from "utils/logger"; | ||
import widgets from "widgets/widgets"; | ||
|
||
const proxyName = "crowdsecProxyHandler"; | ||
const logger = createLogger(proxyName); | ||
const sessionTokenCacheKey = `${proxyName}__sessionToken`; | ||
|
||
async function login(widget, service) { | ||
const url = formatApiCall(widgets[widget.type].loginURL, widget); | ||
const [status, , data] = await httpProxy(url, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
"User-Agent": "Mozilla/5.0", // Crowdsec requires a user-agent | ||
}, | ||
body: JSON.stringify({ | ||
machine_id: widget.username, | ||
password: widget.password, | ||
scenarios: [], | ||
}), | ||
}); | ||
|
||
const dataParsed = JSON.parse(data); | ||
|
||
if (!(status === 200) || !dataParsed.token) { | ||
logger.error("Failed to login to Crowdsec API, status: %d", status); | ||
cache.del(`${sessionTokenCacheKey}.${service}`); | ||
} | ||
cache.put(`${sessionTokenCacheKey}.${service}`, dataParsed.token, new Date(dataParsed.expire) - new Date()); | ||
} | ||
|
||
export default async function crowdsecProxyHandler(req, res) { | ||
const { group, service, endpoint } = req.query; | ||
|
||
if (!group || !service) { | ||
logger.error("Invalid or missing service '%s' or group '%s'", service, group); | ||
return res.status(400).json({ error: "Invalid proxy service type" }); | ||
} | ||
|
||
const widget = await getServiceWidget(group, service); | ||
if (!widget || !widgets[widget.type].api) { | ||
logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||
return res.status(400).json({ error: "Invalid widget configuration" }); | ||
} | ||
|
||
if (!cache.get(`${sessionTokenCacheKey}.${service}`)) { | ||
await login(widget, service); | ||
} | ||
|
||
const token = cache.get(`${sessionTokenCacheKey}.${service}`); | ||
if (!token) { | ||
return res.status(500).json({ error: "Failed to authenticate with Crowdsec" }); | ||
} | ||
|
||
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget })); | ||
|
||
try { | ||
const params = { | ||
method: "GET", | ||
headers: { | ||
"User-Agent": "Mozilla/5.0", // Crowdsec requires a user-agent | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${token}`, | ||
}, | ||
}; | ||
|
||
logger.debug("Calling Crowdsec API endpoint: %s", endpoint); | ||
|
||
if (endpoint.indexOf("decisions") === 0) { | ||
delete params.headers.Authorization; | ||
} | ||
|
||
const [status, , data] = await httpProxy(url, params); | ||
|
||
if (status !== 200) { | ||
logger.error("Error calling Crowdsec API: %d. Data: %s", status, data); | ||
return res.status(status).json({ error: "Crowdsec API Error", data }); | ||
} | ||
|
||
return res.status(status).send(data); | ||
} catch (error) { | ||
logger.error("Exception calling Crowdsec API: %s", error.message); | ||
return res.status(500).json({ error: "Crowdsec API Error", message: error.message }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import crowdsecProxyHandler from "./proxy"; | ||
|
||
const widget = { | ||
api: "{url}/v1/{endpoint}", | ||
loginURL: "{url}/v1/watchers/login", | ||
proxyHandler: crowdsecProxyHandler, | ||
|
||
mappings: { | ||
alerts: { | ||
endpoint: "alerts", | ||
}, | ||
bans: { | ||
endpoint: "alerts?decision_type=ban&origin=crowdsec&has_active_decision=1", | ||
}, | ||
}, | ||
}; | ||
|
||
export default widget; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters