feat(deploy): systemd units for identity + campaign clusterers
decnet-clusterer.service.j2 ships the identity clusterer that landed last session (was overlooked) — bus-woken on attacker.>, publishes identity.> events. decnet-campaign-clusterer.service.j2 ships the campaign clusterer from this session — bus-woken on identity.>, publishes campaign.> events plus the cross-family identity.campaign.assigned. After= decnet-clusterer.service so the identity layer is up before the campaign layer reads its rows. decnet.target Wants both new units. Both follow the same security hardening profile as enrich + reuse-correlator.
This commit is contained in:
52
deploy/decnet-campaign-clusterer.service.j2
Normal file
52
deploy/decnet-campaign-clusterer.service.j2
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=DECNET Campaign Clusterer (identities → campaigns / operations)
|
||||||
|
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#campaign-clusterer
|
||||||
|
After=network-online.target decnet-bus.service decnet-clusterer.service
|
||||||
|
Wants=network-online.target decnet-bus.service decnet-clusterer.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User={{ user }}
|
||||||
|
Group={{ group }}
|
||||||
|
WorkingDirectory={{ install_dir }}
|
||||||
|
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||||
|
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.campaign-clusterer.log
|
||||||
|
# Subscribes to identity.>; falls back to a 60s slow-tick poll when
|
||||||
|
# the bus is idle or unavailable. Reads AttackerIdentity rows,
|
||||||
|
# projects them into the campaign-level similarity graph
|
||||||
|
# (phase-handoff / shared-infra / temporal overlap / cohort), runs
|
||||||
|
# union-find, writes campaigns rows + sets
|
||||||
|
# attacker_identities.campaign_id, and publishes campaign.formed /
|
||||||
|
# campaign.identity.assigned / campaign.merged / campaign.unmerged
|
||||||
|
# plus the cross-family identity.campaign.assigned for identity-side
|
||||||
|
# subscribers.
|
||||||
|
#
|
||||||
|
# Master-only: gated via MASTER_ONLY_COMMANDS in decnet/cli/gating.py.
|
||||||
|
# Sits one layer above decnet-clusterer (the After=/Wants= ensures the
|
||||||
|
# identity layer is up first; the campaign clusterer then wakes on
|
||||||
|
# identity.> events fired by it).
|
||||||
|
ExecStart={{ venv_dir }}/bin/decnet campaign-clusterer
|
||||||
|
StandardOutput=append:/var/log/decnet/decnet.campaign-clusterer.log
|
||||||
|
StandardError=append:/var/log/decnet/decnet.campaign-clusterer.log
|
||||||
|
|
||||||
|
CapabilityBoundingSet=
|
||||||
|
AmbientCapabilities=
|
||||||
|
|
||||||
|
# Security Hardening
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=full
|
||||||
|
ProtectHome=read-only
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
RestrictSUIDSGID=yes
|
||||||
|
LockPersonality=yes
|
||||||
|
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||||
|
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
TimeoutStopSec=15
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
47
deploy/decnet-clusterer.service.j2
Normal file
47
deploy/decnet-clusterer.service.j2
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=DECNET Identity Clusterer (per-IP observations → identities)
|
||||||
|
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#identity-clusterer
|
||||||
|
After=network-online.target decnet-bus.service
|
||||||
|
Wants=network-online.target decnet-bus.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User={{ user }}
|
||||||
|
Group={{ group }}
|
||||||
|
WorkingDirectory={{ install_dir }}
|
||||||
|
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||||
|
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.clusterer.log
|
||||||
|
# Subscribes to attacker.observed and attacker.scored; falls back to a
|
||||||
|
# 60s slow-tick poll when the bus is idle or unavailable. Reads
|
||||||
|
# Attacker rows, projects per-IP observations into the similarity
|
||||||
|
# graph (JA3 / HASSH / payload-hash / C2-endpoint), runs union-find,
|
||||||
|
# writes attacker_identities rows + sets attackers.identity_id, and
|
||||||
|
# publishes identity.formed / identity.observation.linked /
|
||||||
|
# identity.merged / identity.unmerged.
|
||||||
|
#
|
||||||
|
# Master-only: gated via MASTER_ONLY_COMMANDS in decnet/cli/gating.py.
|
||||||
|
ExecStart={{ venv_dir }}/bin/decnet clusterer
|
||||||
|
StandardOutput=append:/var/log/decnet/decnet.clusterer.log
|
||||||
|
StandardError=append:/var/log/decnet/decnet.clusterer.log
|
||||||
|
|
||||||
|
CapabilityBoundingSet=
|
||||||
|
AmbientCapabilities=
|
||||||
|
|
||||||
|
# Security Hardening
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=full
|
||||||
|
ProtectHome=read-only
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
RestrictSUIDSGID=yes
|
||||||
|
LockPersonality=yes
|
||||||
|
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||||
|
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
TimeoutStopSec=15
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -15,6 +15,8 @@ Wants=decnet-bus.service \
|
|||||||
decnet-mutator.service \
|
decnet-mutator.service \
|
||||||
decnet-reuse-correlator.service \
|
decnet-reuse-correlator.service \
|
||||||
decnet-enrich.service \
|
decnet-enrich.service \
|
||||||
|
decnet-clusterer.service \
|
||||||
|
decnet-campaign-clusterer.service \
|
||||||
decnet-webhook.service
|
decnet-webhook.service
|
||||||
After=decnet-bus.service
|
After=decnet-bus.service
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user