Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/banip/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk

PKG_NAME:=banip
PKG_VERSION:=1.8.10
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_LICENSE:=GPL-3.0-or-later
PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>

Expand Down
13 changes: 13 additions & 0 deletions packages/banip/files/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ The `report` sub-command accepts an output mode: `text` (default, human-readable
| ban_resolver | option | - | external resolver used for DNS lookups, by default the local resolver/forwarder will be used |
| ban_remotelog | option | 0 | enable the cgi interface to receive remote logging events |
| ban_remotetoken | option | - | unique token to communicate with the cgi interface |
| ban_blockhook | option | - | full path of an external script, called once for every IP newly blocked by the log service |

<a id="examples"></a>
## Examples
Expand Down Expand Up @@ -545,6 +546,18 @@ Examples to transfer remote logging events from an internal server to banIP via

Please note: for security reasons use this cgi interface only internally and only encrypted via https transfer protocol.

**External hook for newly blocked IPs**
banIP can notify an external program whenever the log service adds a new IP to a blocklist Set (disabled by default). Set `ban_blockhook` to the full path of an executable script, e.g. `/usr/libexec/my-block-hook`. The hook is called once per newly blocked IP, right after the nftables element has been added, with four positional arguments:

```
$1: the blocked IP address, e.g. '198.51.100.44'
$2: the protocol family, either 'v4' or 'v6'
$3: the log count that triggered the block (ban_logcount)
$4: the Set expiry time (ban_nftexpiry), '0s' if the block is permanent
```

Please note: the hook runs synchronously inside the single-threaded log service, so it has to be fast and non-blocking - queue the event and process it elsewhere, never do network I/O in the hook itself. Its exit code and output are ignored.

**Download options**
By default banIP uses the following pre-configured download options:

Expand Down
9 changes: 8 additions & 1 deletion packages/banip/files/banip-functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ ban_mailprofile="ban_notify"
ban_mailnotification="0"
ban_remotelog="0"
ban_remotetoken=""
ban_blockhook=""
ban_nftloglevel="warn"
ban_nftpriority="-100"
ban_nftpolicy="memory"
Expand Down Expand Up @@ -2695,7 +2696,7 @@ f_monitor() {
case "${log_type}" in
tail)
"${ban_logreadcmd}" -qf "${ban_logreadfile}" 2>/dev/null |
"${ban_grepcmd}" -e "${ban_logterm}" 2>/dev/null
"${ban_grepcmd}" --line-buffered -e "${ban_logterm}" 2>/dev/null
;;
logread)
"${ban_logreadcmd}" -fe "${ban_logterm}" 2>/dev/null
Expand Down Expand Up @@ -2835,6 +2836,12 @@ f_monitor() {
fi
block_cache="${block_cache} ${ip} "
f_log "info" "add IP '${ip}' (cnt: ${ban_logcount}, expiry: ${ban_nftexpiry:-"0"}) to blocklist${proto} Set"

# optional external hook, called once for every newly blocked IP
#
if [ -n "${ban_blockhook}" ] && [ -x "${ban_blockhook}" ]; then
"${ban_blockhook}" "${ip}" "${proto#.}" "${ban_logcount}" "${ban_nftexpiry:-0s}" >/dev/null 2>&1
fi
else
f_log "info" "failed to add IP '${ip}' to blocklist${proto} Set with rc '${?}'"
continue
Expand Down
2 changes: 1 addition & 1 deletion packages/ns-api/files/ns.threatshield
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ def list_blocklist(e_uci):
feed = feeds[f]
enabled = f in enabled_feeds

if 'nethesis-blacklists' in feed.get('url_4'):
if 'nethesis' in feed.get('url_4'):
type = 'enterprise'
else:
type = 'community'
Expand Down
6 changes: 5 additions & 1 deletion packages/ns-threat_shield/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk

PKG_NAME:=ns-threat_shield
PKG_VERSION:=1.0.1
PKG_VERSION:=1.1.0
PKG_RELEASE:=1

PKG_BUILD_DIR:=$(BUILD_DIR)/ns-threat_shield-$(PKG_VERSION)
Expand Down Expand Up @@ -46,6 +46,8 @@ define Package/ns-threat_shield/install
$(INSTALL_DIR) $(1)/usr/libexec
$(INSTALL_BIN) ./files/ts-dns $(1)/usr/sbin/ts-dns
$(INSTALL_BIN) ./files/ts-ip $(1)/usr/sbin/ts-ip
$(INSTALL_BIN) ./files/ts-insights-report.py $(1)/usr/sbin/ts-insights-report
$(INSTALL_BIN) ./files/ts-insights-hook $(1)/usr/libexec/ts-insights-hook
$(INSTALL_BIN) ./files/20_threat_shield $(1)/etc/uci-defaults
$(INSTALL_BIN) ./files/ts-dns.hook $(1)/usr/share/ns-plug/hooks/register/90ts-dns
$(INSTALL_BIN) ./files/ts-dns.hook $(1)/usr/share/ns-plug/hooks/unregister/90ts-dns
Expand All @@ -62,6 +64,7 @@ define Package/ns-threat_shield/install
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_BIN) ./files/banip-defaults $(1)/etc/uci-defaults/99-nethsec-banip
$(INSTALL_BIN) ./files/banip-extra-defaults $(1)/etc/uci-defaults/96-nethsec-banip-extra
$(INSTALL_BIN) ./files/banip-insights-defaults $(1)/etc/uci-defaults/97-nethsec-banip-insights
$(INSTALL_BIN) ./files/35_ns-threat_shield $(1)/etc/uci-defaults/35_ns-threat_shield
$(INSTALL_BIN) ./files/96_ns-threat_shield $(1)/etc/uci-defaults/96_ns-threat_shield
gzip -9n $(1)/usr/share/threat_shield/nethesis-dns.sources
Expand All @@ -84,6 +87,7 @@ define Package/ns-threat_shield/prerm
if [ -z "$${IPKG_INSTROOT}" ]; then
crontab -l | grep -v "/etc/init.d/banip reload" | sort | uniq | crontab -
crontab -l | grep -v "/etc/init.d/adblock" | sort | uniq | crontab -
crontab -l | grep -v "/usr/sbin/ts-insights-report" | sort | uniq | crontab -
fi
exit 0
endef
Expand Down
37 changes: 37 additions & 0 deletions packages/ns-threat_shield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The following categories require a valid entitlement:
- `yoroisusplvl1` (was `yoroi_souspicious_level1` on NS7)
- `yoroisusplvl2` (was `yoroi_souspicious_level2` on NS7)
- `nethesislvl3` (was `nethesis_level3` on NS7)
- `nethesisinsights` (attackers reported by the other Nethesis firewalls, see [Nethesis Insights](#nethesis-insights))

After machine registration, above categories will be automatically added to existing banip categories (`/etc/banip/banip.custom.feeds`).

Expand All @@ -47,6 +48,42 @@ ts-ip
/etc/init.d/banip restart
```

### Nethesis Insights

If the machine is registered, `ts-ip` also joins the [Nethesis Insights](https://github.com/nethesis/nethesis-insights)
threat shield: every IP blocked by the banip log service is reported to the Insights server, and the
list aggregated from the reports of all registered firewalls is blocked locally.
Both directions authenticate with the `system_id` and `secret` of the subscription: no additional
configuration is required and nothing is sent from a machine which is not registered.

Reporting side:

- `ts-ip` sets `banip.global.ban_blockhook` to `/usr/libexec/ts-insights-hook`; banip calls it once
for every IP added to a blocklist Set by the log service
- the hook only appends a JSON line to `/var/run/ns-insights/threat-events.jsonl`, so that a burst
of blocked IPs never slows down the banip log service
- `/usr/sbin/ts-insights-report` is executed every 5 minutes by cron: it sends the spooled events to
`POST /v1/threat-events` in batches of at most 500, then writes the outcome to
`/var/run/ns-insights/last_push.json`
- only globally routable addresses are reported: private, CGNAT, link-local, reserved and
documentation ranges are dropped locally, along with the events older than 2 hours
- on a failed push the events are kept in the spool and sent again at the next run, duplicated
reports are discarded by the server

Blocking side:

- the `nethesisinsights` feed points to `GET /v1/blocklist`, it is added to `ban_feed` on
registration and it is reloaded with all the other feeds every 4 hours
- an IP is published by the server only after it has been reported by several distinct firewalls,
and it expires when nobody reports it any more

On unregistration the hook and the feed are both removed.

Check the last report, example:
```
cat /var/run/ns-insights/last_push.json
```

## ts-dns

Threat shield DNS (`ts-dns`) is a special configuration for [adblock](https://github.com/openwrt/packages/tree/master/net/adblock).
Expand Down
1 change: 1 addition & 0 deletions packages/ns-threat_shield/files/20_threat_shield
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

crontab -l | grep -q '/etc/init.d/banip' || echo '0 */4 * * * sleep $(( RANDOM % 3600 )); /etc/init.d/banip reload' >> /etc/crontabs/root
crontab -l | grep -q '/etc/init.d/adblock' || echo '1 */12 * * * sleep $(( RANDOM % 3600 )); /etc/init.d/adblock reload' >> /etc/crontabs/root
crontab -l | grep -q '/usr/sbin/ts-insights-report' || echo '*/5 * * * * sleep $(( RANDOM % 60 )); /usr/sbin/ts-insights-report' >> /etc/crontabs/root
15 changes: 15 additions & 0 deletions packages/ns-threat_shield/files/banip-insights-defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/sh

#
# Copyright (C) 2026 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-2.0-only
#

# An already registered unit must join the Nethesis Insights threat shield right
# after an image update, without waiting for the next machine registration:
# ts-ip sets the banip block hook and adds the nethesisinsights feed.

[ -n "$(uci -q get ns-plug.config.secret)" ] || exit 0
[ -n "$(uci -q get banip.global.ban_blockhook)" ] && exit 0

/usr/sbin/ts-ip
7 changes: 7 additions & 0 deletions packages/ns-threat_shield/files/banip.nethesis.feeds
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
"chain": "in",
"descr": "Yoroi suspicious - Level 2"
},
"nethesisinsightslvl2": {
"url_4": "https://__USER__:__PASSWORD__@insights.nethesis.it/v1/blocklist",
"url_6": "https://__USER__:__PASSWORD__@insights.nethesis.it/v1/blocklist",
"rule": "feed 1",
"chain": "in",
"descr": "Nethesis Insights - Level 2"
},
"nethesislvl3": {
"url_4": "https://__USER__:__PASSWORD__@bl.nethesis.it/plain/__TYPE__/nethesis-blacklists/nethesis_level3.netset",
"rule": "feed 1",
Expand Down
26 changes: 26 additions & 0 deletions packages/ns-threat_shield/files/ts-insights-hook
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/sh

#
# Copyright (C) 2026 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-2.0-only
#

#
# banip ban_blockhook script: spool a threat event for nethesis-insights.
#
# Called by the banip log service for every newly blocked IP, arguments:
# $1 IP address, $2 protocol family (v4|v6), $3 log count, $4 Set expiry
#
# It must stay fast and non-blocking: the only job here is to append one JSON
# line to the spool file, /usr/sbin/ts-insights-report sends it later on.
#

SPOOL_DIR="/var/run/ns-insights"
SPOOL="${SPOOL_DIR}/threat-events.jsonl"

[ -n "$1" ] || exit 0

[ -d "${SPOOL_DIR}" ] || mkdir -p "${SPOOL_DIR}"

printf '{"value":"%s","scope":"Ip","type":"ban","scenario":"nethsecurity/banip-log","origin":"banip","duration":"%s","created_at":"%s"}\n' \
"$1" "${4:-0s}" "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >>"${SPOOL}"
Loading
Loading