Compare commits
14 Commits
92e06cb193
...
897f498bcd
| Author | SHA1 | Date | |
|---|---|---|---|
| 897f498bcd | |||
| ea409650fa | |||
| d92aa99b81 | |||
| fc7fca998f | |||
| ed749a8c31 | |||
| cf36ebcd84 | |||
| 6a5c6f098e | |||
| 988732f4f9 | |||
| 4acfa3f779 | |||
| 35c67ec34d | |||
| fe7354554f | |||
| b3b3597011 | |||
| 38b1efa8c0 | |||
| d7a6aeff86 |
86
.gitea/workflows/ci.yml
Normal file
86
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,86 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev, testing]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint (ruff)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- run: pip install ruff
|
||||
- run: ruff check .
|
||||
|
||||
test:
|
||||
name: Test (pytest)
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.11", "3.12"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- run: pip install -e .
|
||||
- run: pytest tests/ -v --tb=short
|
||||
|
||||
bandit:
|
||||
name: SAST (bandit)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- run: pip install bandit
|
||||
- run: bandit -r decnet/ -ll -x decnet/services/registry.py
|
||||
|
||||
pip-audit:
|
||||
name: Dependency audit (pip-audit)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- run: pip install pip-audit
|
||||
- run: pip install -e .
|
||||
- run: pip-audit --skip-editable
|
||||
|
||||
open-pr:
|
||||
name: Open PR to main
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test, bandit, pip-audit]
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
steps:
|
||||
- name: Open PR via Gitea API
|
||||
run: |
|
||||
echo "--- Checking for existing open PRs ---"
|
||||
LIST_RESPONSE=$(curl -s \
|
||||
-H "Authorization: token ${{ secrets.DECNET_PR_TOKEN }}" \
|
||||
"https://git.resacachile.cl/api/v1/repos/anti/DECNET/pulls?state=open&head=anti:dev&base=main&limit=5")
|
||||
echo "$LIST_RESPONSE"
|
||||
EXISTING=$(echo "$LIST_RESPONSE" | python3 -c "import sys, json; print(len(json.load(sys.stdin)))")
|
||||
echo "Open PRs found: $EXISTING"
|
||||
if [ "$EXISTING" -gt "0" ]; then
|
||||
echo "PR already open, skipping."
|
||||
exit 0
|
||||
fi
|
||||
echo "--- Creating PR ---"
|
||||
CREATE_RESPONSE=$(curl -s -X POST \
|
||||
-H "Authorization: token ${{ secrets.DECNET_PR_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"title": "Auto PR: dev → main",
|
||||
"head": "dev",
|
||||
"base": "main",
|
||||
"body": "All CI and security checks passed. Review and merge when ready."
|
||||
}' \
|
||||
"https://git.resacachile.cl/api/v1/repos/anti/DECNET/pulls")
|
||||
echo "$CREATE_RESPONSE"
|
||||
31
.gitea/workflows/pr.yml
Normal file
31
.gitea/workflows/pr.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
name: PR Gate
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint (ruff)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- run: pip install ruff
|
||||
- run: ruff check .
|
||||
|
||||
test:
|
||||
name: Test (pytest)
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.11", "3.12"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- run: pip install -e .
|
||||
- run: pytest tests/ -v --tb=short
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
fi
|
||||
|
||||
docker:
|
||||
name: Build & push ${{ matrix.service }}
|
||||
name: Build, scan & push ${{ matrix.service }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: tag
|
||||
strategy:
|
||||
@@ -76,6 +76,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Gitea container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -83,7 +86,26 @@ jobs:
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
- name: Build image locally
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: templates/${{ matrix.service }}
|
||||
load: true
|
||||
push: false
|
||||
tags: decnet-${{ matrix.service }}:scan
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Scan with Trivy
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: decnet-${{ matrix.service }}:scan
|
||||
exit-code: "1"
|
||||
severity: CRITICAL
|
||||
ignore-unfixed: true
|
||||
|
||||
- name: Push image
|
||||
if: success()
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: templates/${{ matrix.service }}
|
||||
@@ -91,3 +113,4 @@ jobs:
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.OWNER }}/decnet-${{ matrix.service }}:latest
|
||||
${{ env.REGISTRY }}/${{ env.OWNER }}/decnet-${{ matrix.service }}:v${{ needs.tag.outputs.version }}
|
||||
cache-from: type=gha
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -9,3 +9,10 @@ decnet-compose.yml
|
||||
decnet-state.json
|
||||
*.ini
|
||||
.env
|
||||
decnet.log*
|
||||
*.loggy
|
||||
*.nmap
|
||||
linterfails.log
|
||||
test-scan
|
||||
webmail
|
||||
windows1
|
||||
|
||||
@@ -54,3 +54,4 @@ DECNET is a honeypot/deception network framework. It deploys fake machines (call
|
||||
- NEVER pass broken code to the user.
|
||||
- Broken means: not running, not passing 100% tests, etc.
|
||||
- After tests pass with 100%, always git commit your changes.
|
||||
- NEVER add "Co-Authored-By" or any Claude attribution lines to git commit messages.
|
||||
|
||||
1
DEVELOPMENT.md
Normal file
1
DEVELOPMENT.md
Normal file
@@ -0,0 +1 @@
|
||||
CI/CD TEST 2
|
||||
60
NOTES.md
60
NOTES.md
@@ -51,3 +51,63 @@ docker network create -d macvlan \
|
||||
#### Issues
|
||||
|
||||
This initial test doesn't seem to be working. Might be that I'm using WSL, so I downloaded a Ubuntu 22.04 Server ISO. I'll try the MACVLAN network on it. Now, if that doesn't work, I don't see how the 802.1q would work, at least on _my network_. Perhaps if I had a switch I could make it work, but currently I don't have one :c
|
||||
|
||||
---
|
||||
|
||||
# TODO
|
||||
|
||||
## Core / Hardening
|
||||
|
||||
- [ ] **Attacker fingerprinting** — Beyond IP logging: capture TLS JA3/JA4 hashes, TCP window sizes, User-Agent strings, SSH client banners, and tool signatures (nmap, masscan, Metasploit, Cobalt Strike). Build attacker profiles across sessions.
|
||||
- [ ] **Canary tokens** — Embed canary URLs, fake AWS keys, fake API tokens, and honeydocs (PDF/DOCX with phone-home URLs) into decky filesystems. Fire an alert the moment one is used.
|
||||
- [ ] **Tarpit mode** — Slow down attackers by making services respond extremely slowly (e.g., SSH that takes 60s to reject, HTTP that drip-feeds bytes). Wastes attacker time and resources.
|
||||
- [ ] **Dynamic decky mutation** — Deckies that change their exposed services or OS fingerprint over time to confuse port-scan caching and appear more "alive."
|
||||
- [ ] **Credential harvesting DB** — Every username/password attempt across all services lands in a queryable database. Expose via CLI (`decnet creds`) and flag reuse across deckies.
|
||||
- [ ] **Session recording** — Full session capture for SSH/Telnet (keystroke logs, commands run, files downloaded). Cowrie already does this — surface it better in the CLI and correlation engine.
|
||||
- [ ] **Payload capture** — Store every file uploaded or command executed by an attacker. Hash and auto-submit to VirusTotal or a local sandbox.
|
||||
|
||||
## Detection & Intelligence
|
||||
|
||||
- [ ] **Real-time alerting** — Webhook/Slack/Telegram notifications when an attacker hits a decky for the first time, crosses N deckies (lateral movement), or uses a known bad IP.
|
||||
- [ ] **Threat intel enrichment** — Auto-lookup attacker IPs against AbuseIPDB, Shodan, GreyNoise, and AlienVault OTX. Tag known scanners vs. targeted attackers.
|
||||
- [ ] **Attack campaign clustering** — Group attacker sessions by tooling signatures, timing patterns, and credential sets. Identify coordinated campaigns hitting multiple deckies.
|
||||
- [ ] **GeoIP mapping** — Attacker origin on a world map. Correlate with ASN data to identify cloud exit nodes, VPNs, and Tor exits.
|
||||
- [ ] **TTPs tagging** — Map observed attacker behaviors to MITRE ATT&CK techniques automatically. Tag events in the correlation engine.
|
||||
- [ ] **Honeypot interaction scoring** — Score attackers on a scale: casual scanner vs. persistent targeted attacker, based on depth of interaction and commands run.
|
||||
|
||||
## Dashboard & Visibility
|
||||
|
||||
- [ ] **Web dashboard** — Real-time web UI showing live decky status, attacker activity, traversal graphs, and credential stats. Could be a simple FastAPI + HTMX or a full React app.
|
||||
- [ ] **Pre-built Kibana/Grafana dashboards** — Ship dashboard JSON exports out of the box so ELK/Grafana deployments are plug-and-play.
|
||||
- [ ] **CLI live feed** — `decnet watch` command: tail all decky logs in a unified, colored terminal stream (like `docker-compose logs -f` but prettier).
|
||||
- [ ] **Traversal graph export** — Export attacker traversal graphs as DOT/Graphviz or JSON for visualization in external tools.
|
||||
- [ ] **Daily digest** — Automated daily summary email/report: new attackers, top credentials tried, most-hit services.
|
||||
|
||||
## Deployment & Infrastructure
|
||||
|
||||
- [ ] **SWARM / multihost mode** — Full Ansible-based orchestration for deploying deckies across N real hosts.
|
||||
- [ ] **Terraform/Pulumi provider** — Spin up cloud-hosted deckies on AWS/GCP/Azure with one command. Useful for internet-facing honeynets.
|
||||
- [ ] **Auto-scaling** — When attack traffic increases, automatically spawn more deckies to absorb and log more activity.
|
||||
- [ ] **Kubernetes deployment mode** — Run deckies as Kubernetes pods for environments already running k8s.
|
||||
- [ ] **Proxmox/libvirt backend** — Full VM-based deckies instead of containers, for even more realistic OS fingerprints and behavior. Docker for speed; VMs for realism.
|
||||
- [ ] **Raspberry Pi / ARM support** — Low-cost physical honeynets using RPis. Validate ARM image builds.
|
||||
- [ ] **Decky health monitoring** — Watchdog that auto-restarts crashed deckies and alerts if a service goes dark.
|
||||
|
||||
## Services & Realism
|
||||
|
||||
- [ ] **HTTPS/TLS support** — HTTP honeypot with a self-signed or Let's Encrypt cert. Many real-world services use HTTPS; plain HTTP stands out.
|
||||
- [ ] **Fake Active Directory** — A convincing fake AD/LDAP with fake users, groups, and GPOs. Attacker tools like BloodHound should get juicy (fake) data.
|
||||
- [ ] **Fake file shares** — SMB/NFS shares pre-populated with enticing but fake files: "passwords.xlsx", "vpn_config.ovpn", "backup_keys.tar.gz". All instrumented to detect access.
|
||||
- [ ] **Realistic web apps** — HTTP honeypot serving convincing fake apps: a fake WordPress, a fake phpMyAdmin, a fake Grafana login — all logging every interaction.
|
||||
- [ ] **OT/ICS profiles** — Expand Conpot support: Modbus, DNP3, BACnet, EtherNet/IP. Convincing industrial control system decoys.
|
||||
- [ ] **Printer/IoT archetypes** — Expand existing printer/camera archetypes with actual service emulation (IPP, ONVIF, WS-Discovery).
|
||||
- [ ] **Service interaction depth** — Some services currently just log the connection. Deepen interaction: fake MySQL that accepts queries and returns realistic fake data, fake Redis that stores and retrieves dummy keys.
|
||||
|
||||
## Developer Experience
|
||||
|
||||
- [ ] **Plugin SDK docs** — Full documentation and an example plugin for adding custom services. Lower the barrier for community contributions.
|
||||
- [ ] **Integration tests** — Full deploy/teardown cycle tests against a real Docker daemon (not just unit tests).
|
||||
- [ ] **Per-service tests** — Each of the 29 service implementations deserves its own test coverage.
|
||||
- [ ] **CI/CD pipeline** — GitHub/Gitea Actions: run tests on push, lint, build Docker images, publish releases.
|
||||
- [ ] **Config validation CLI** — `decnet validate my.ini` to dry-check an INI config before deploying.
|
||||
- [ ] **Config generator wizard** — `decnet wizard` interactive prompt to generate an INI config without writing one by hand.
|
||||
|
||||
@@ -22,7 +22,6 @@ Usage
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ The attacker IP may appear under several field names depending on service:
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
# RFC 5424 line structure
|
||||
|
||||
@@ -14,11 +14,8 @@ from decnet.config import DecnetConfig, clear_state, load_state, save_state
|
||||
from decnet.composer import write_compose
|
||||
from decnet.network import (
|
||||
MACVLAN_NETWORK_NAME,
|
||||
allocate_ips,
|
||||
create_ipvlan_network,
|
||||
create_macvlan_network,
|
||||
detect_interface,
|
||||
detect_subnet,
|
||||
get_host_ip,
|
||||
ips_to_range,
|
||||
remove_macvlan_network,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Rotating file handler for DECNET syslog output.
|
||||
|
||||
@@ -7,6 +6,8 @@ Path is controlled by the DECNET_LOG_FILE environment variable
|
||||
(default: /var/log/decnet/decnet.log).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from __future__ import annotations
|
||||
"""
|
||||
RFC 5424 syslog formatter for DECNET.
|
||||
|
||||
@@ -9,6 +8,8 @@ Facility: local0 (16)
|
||||
PEN for structured data: decnet@55555
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
|
||||
@@ -8,10 +8,7 @@ Handles:
|
||||
- IP allocation (sequential, skipping reserved addresses)
|
||||
"""
|
||||
|
||||
import ipaddress
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
from ipaddress import IPv4Address, IPv4Interface, IPv4Network
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ def _load_plugins() -> None:
|
||||
continue
|
||||
importlib.import_module(f"decnet.services.{module_info.name}")
|
||||
for cls in BaseService.__subclasses__():
|
||||
if not cls.__module__.startswith("decnet.services."):
|
||||
continue
|
||||
instance = cls()
|
||||
_registry[instance.name] = instance
|
||||
_loaded = True
|
||||
|
||||
@@ -13,6 +13,10 @@ dependencies = [
|
||||
"docker>=7.0",
|
||||
"pyyaml>=6.0",
|
||||
"jinja2>=3.1",
|
||||
"pytest>=8.0",
|
||||
"ruff>=0.4",
|
||||
"bandit>=1.7",
|
||||
"pip-audit>=2.0",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ but fake data. Logs all requests as JSON.
|
||||
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from flask import Flask, request
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@ as JSON. Designed to attract automated scanners and credential stuffers.
|
||||
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -5,13 +5,10 @@ Accepts any credentials, logs all commands and file requests,
|
||||
forwards events as JSON to LOG_TARGET if set.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from twisted.internet import defer, protocol, reactor
|
||||
from twisted.internet import defer, reactor
|
||||
from twisted.protocols.ftp import FTP, FTPFactory
|
||||
from twisted.python import log as twisted_log
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@ and responds with configurable pages. Forwards events as JSON to LOG_TARGET if s
|
||||
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from flask import Flask, request, send_from_directory
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -6,10 +6,7 @@ AUTHENTICATE), then returns a NO response. Logs all commands as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "mailserver")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ Responds to recon endpoints (/version, /api, /apis, /api/v1/namespaces,
|
||||
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from flask import Flask, request
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -6,10 +6,7 @@ invalidCredentials error. Logs all interactions as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "ldapserver")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,11 +7,8 @@ Logs every packet with source IP and decoded query name where possible.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "lan-host")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,11 +7,8 @@ received messages as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "mongodb")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,11 +7,8 @@ interactions as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "mqtt-broker")
|
||||
@@ -48,11 +45,13 @@ def _parse_connect(payload: bytes):
|
||||
# Protocol level (1 byte)
|
||||
if pos >= len(payload):
|
||||
return {}, pos
|
||||
_proto_level = payload[pos]; pos += 1
|
||||
_proto_level = payload[pos]
|
||||
pos += 1
|
||||
# Connect flags (1 byte)
|
||||
if pos >= len(payload):
|
||||
return {}, pos
|
||||
flags = payload[pos]; pos += 1
|
||||
flags = payload[pos]
|
||||
pos += 1
|
||||
# Keep alive (2 bytes)
|
||||
pos += 2
|
||||
# Client ID
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -6,11 +6,8 @@ a login failed error. Logs auth attempts as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "dbserver")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,11 +7,8 @@ attempts as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "dbserver")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,10 +7,7 @@ to LOG_TARGET if set.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "mailserver")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,11 +7,8 @@ returns an error. Logs all interactions as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "pgserver")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -6,11 +6,8 @@ in the initial RDP negotiation request. Forwards events as JSON to
|
||||
LOG_TARGET if set.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from twisted.internet import protocol, reactor
|
||||
from twisted.python import log as twisted_log
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -6,10 +6,7 @@ KEYS, and arbitrary commands. Logs every command and argument as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "cache-server")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -6,11 +6,8 @@ Authorization header and call metadata, then responds with 401 Unauthorized.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "pbx")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -4,10 +4,7 @@ Minimal SMB server using Impacket's SimpleSMBServer.
|
||||
Logs all connection attempts, optionally forwarding them as JSON to LOG_TARGET.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from impacket import smbserver
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -5,10 +5,7 @@ Logs EHLO/AUTH/MAIL FROM/RCPT TO attempts as JSON, then denies auth.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "mailserver")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,11 +7,8 @@ Logs all requests as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "switch")
|
||||
@@ -94,35 +91,46 @@ def _ber_tlv(tag: int, value: bytes) -> bytes:
|
||||
def _parse_snmp(data: bytes):
|
||||
"""Return (version, community, request_id, oids) or raise."""
|
||||
pos = 0
|
||||
assert data[pos] == 0x30; pos += 1
|
||||
assert data[pos] == 0x30
|
||||
pos += 1
|
||||
_, pos = _read_ber_length(data, pos)
|
||||
# version
|
||||
assert data[pos] == 0x02; pos += 1
|
||||
assert data[pos] == 0x02
|
||||
pos += 1
|
||||
v_len, pos = _read_ber_length(data, pos)
|
||||
version = int.from_bytes(data[pos:pos + v_len], "big"); pos += v_len
|
||||
version = int.from_bytes(data[pos:pos + v_len], "big")
|
||||
pos += v_len
|
||||
# community
|
||||
assert data[pos] == 0x04; pos += 1
|
||||
assert data[pos] == 0x04
|
||||
pos += 1
|
||||
c_len, pos = _read_ber_length(data, pos)
|
||||
community = data[pos:pos + c_len].decode(errors="replace"); pos += c_len
|
||||
community = data[pos:pos + c_len].decode(errors="replace")
|
||||
pos += c_len
|
||||
# PDU type (0xa0 = GetRequest, 0xa1 = GetNextRequest)
|
||||
pdu_type = data[pos]; pos += 1
|
||||
pos += 1
|
||||
_, pos = _read_ber_length(data, pos)
|
||||
# request-id
|
||||
assert data[pos] == 0x02; pos += 1
|
||||
assert data[pos] == 0x02
|
||||
pos += 1
|
||||
r_len, pos = _read_ber_length(data, pos)
|
||||
request_id = int.from_bytes(data[pos:pos + r_len], "big"); pos += r_len
|
||||
request_id = int.from_bytes(data[pos:pos + r_len], "big")
|
||||
pos += r_len
|
||||
pos += 4 # skip error-status and error-index
|
||||
# varbind list
|
||||
assert data[pos] == 0x30; pos += 1
|
||||
assert data[pos] == 0x30
|
||||
pos += 1
|
||||
vbl_len, pos = _read_ber_length(data, pos)
|
||||
end = pos + vbl_len
|
||||
oids = []
|
||||
while pos < end:
|
||||
assert data[pos] == 0x30; pos += 1
|
||||
assert data[pos] == 0x30
|
||||
pos += 1
|
||||
vb_len, pos = _read_ber_length(data, pos)
|
||||
assert data[pos] == 0x06; pos += 1
|
||||
assert data[pos] == 0x06
|
||||
pos += 1
|
||||
oid_len, pos = _read_ber_length(data, pos)
|
||||
oid = _decode_oid(data[pos:pos + oid_len]); pos += oid_len
|
||||
oid = _decode_oid(data[pos:pos + oid_len])
|
||||
pos += oid_len
|
||||
oids.append(oid)
|
||||
pos += vb_len - oid_len - 2 # skip value
|
||||
return version, community, request_id, oids
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -6,11 +6,8 @@ then responds with an error packet. Logs all requests as JSON.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "tftpserver")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Shared RFC 5424 syslog helper for DECNET service templates.
|
||||
|
||||
|
||||
@@ -7,10 +7,7 @@ failed". Logs the raw response for offline cracking.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime, timezone
|
||||
from decnet_logging import syslog_line, write_syslog_file, forward_syslog
|
||||
|
||||
NODE_NAME = os.environ.get("NODE_NAME", "desktop")
|
||||
|
||||
98
test-scan
98
test-scan
@@ -1,98 +0,0 @@
|
||||
# Nmap 7.92 scan initiated Sat Apr 4 04:41:05 2026 as: nmap -sS -sV -O -A -oN test-scan 192.168.1.110-119
|
||||
Nmap scan report for 192.168.1.110
|
||||
Host is up (0.000035s latency).
|
||||
Not shown: 996 closed tcp ports (reset)
|
||||
PORT STATE SERVICE VERSION
|
||||
25/tcp open smtp Postfix smtpd
|
||||
|_smtp-commands: decky-webmail, PIPELINING, SIZE 10240000, VRFY, ETRN, AUTH PLAIN LOGIN, ENHANCEDSTATUSCODES, 8BITMIME, DSN
|
||||
80/tcp open http Apache httpd 2.4.54 ((Debian))
|
||||
|_http-title: WordPress › Error
|
||||
|_http-server-header: Werkzeug/3.1.8 Python/3.11.2
|
||||
110/tcp open pop3
|
||||
| fingerprint-strings:
|
||||
| GenericLines, HTTPOptions:
|
||||
| +OK decky-webmail POP3 server ready
|
||||
| -ERR Unknown command
|
||||
| -ERR Unknown command
|
||||
| NULL:
|
||||
|_ +OK decky-webmail POP3 server ready
|
||||
|_pop3-capabilities: USER
|
||||
143/tcp open imap
|
||||
| fingerprint-strings:
|
||||
| GenericLines, NULL:
|
||||
| * OK [decky-webmail] IMAP4rev1 Service Ready
|
||||
| GetRequest:
|
||||
| * OK [decky-webmail] IMAP4rev1 Service Ready
|
||||
|_ Command not recognized
|
||||
|_imap-capabilities: OK CAPABILITY AUTH=LOGINA0001 IMAP4rev1 completed AUTH=PLAIN
|
||||
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
|
||||
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
|
||||
SF-Port110-TCP:V=7.92%I=7%D=4/4%Time=69D0C099%P=x86_64-redhat-linux-gnu%r(
|
||||
SF:NULL,25,"\+OK\x20decky-webmail\x20POP3\x20server\x20ready\r\n")%r(Gener
|
||||
SF:icLines,51,"\+OK\x20decky-webmail\x20POP3\x20server\x20ready\r\n-ERR\x2
|
||||
SF:0Unknown\x20command\r\n-ERR\x20Unknown\x20command\r\n")%r(HTTPOptions,5
|
||||
SF:1,"\+OK\x20decky-webmail\x20POP3\x20server\x20ready\r\n-ERR\x20Unknown\
|
||||
SF:x20command\r\n-ERR\x20Unknown\x20command\r\n");
|
||||
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
|
||||
SF-Port143-TCP:V=7.92%I=7%D=4/4%Time=69D0C099%P=x86_64-redhat-linux-gnu%r(
|
||||
SF:NULL,2E,"\*\x20OK\x20\[decky-webmail\]\x20IMAP4rev1\x20Service\x20Ready
|
||||
SF:\r\n")%r(GetRequest,4E,"\*\x20OK\x20\[decky-webmail\]\x20IMAP4rev1\x20S
|
||||
SF:ervice\x20Ready\r\nGET\x20BAD\x20Command\x20not\x20recognized\r\n")%r(G
|
||||
SF:enericLines,2E,"\*\x20OK\x20\[decky-webmail\]\x20IMAP4rev1\x20Service\x
|
||||
SF:20Ready\r\n");
|
||||
MAC Address: D2:55:C7:0D:B4:FC (Unknown)
|
||||
Device type: general purpose
|
||||
Running: Linux 5.X
|
||||
OS CPE: cpe:/o:linux:linux_kernel:5
|
||||
OS details: Linux 5.3 - 5.4
|
||||
Network Distance: 1 hop
|
||||
Service Info: Host: decky-webmail
|
||||
|
||||
TRACEROUTE
|
||||
HOP RTT ADDRESS
|
||||
1 0.04 ms 192.168.1.110
|
||||
|
||||
Nmap scan report for 192.168.1.111
|
||||
Host is up (0.000016s latency).
|
||||
Not shown: 998 closed tcp ports (reset)
|
||||
PORT STATE SERVICE VERSION
|
||||
21/tcp open ftp vsftpd (before 2.0.8) or WU-FTPD
|
||||
445/tcp open microsoft-ds
|
||||
| fingerprint-strings:
|
||||
| SMBProgNeg:
|
||||
| SMBr
|
||||
|_ "3DUfw
|
||||
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
|
||||
SF-Port445-TCP:V=7.92%I=7%D=4/4%Time=69D0C09E%P=x86_64-redhat-linux-gnu%r(
|
||||
SF:SMBProgNeg,51,"\0\0\0M\xffSMBr\0\0\0\0\x80\0\xc0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
SF:0\0\0@\x06\0\0\x01\0\x11\x07\0\x03\x01\0\x01\0\0\xfa\0\0\0\0\x01\0\0\0\
|
||||
SF:0\0p\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\x08\0\x11\"3DUfw\x88");
|
||||
MAC Address: 92:44:B7:6C:F6:D0 (Unknown)
|
||||
Device type: general purpose
|
||||
Running: Linux 5.X
|
||||
OS CPE: cpe:/o:linux:linux_kernel:5
|
||||
OS details: Linux 5.3 - 5.4
|
||||
Network Distance: 1 hop
|
||||
Service Info: Host: Twisted
|
||||
|
||||
Host script results:
|
||||
| smb-security-mode:
|
||||
| account_used: guest
|
||||
| authentication_level: user
|
||||
| challenge_response: supported
|
||||
|_ message_signing: disabled (dangerous, but default)
|
||||
| smb2-time:
|
||||
| date: 2026-04-04T07:41:24
|
||||
|_ start_date: 2026-04-04T07:41:24
|
||||
| smb2-security-mode:
|
||||
| 2.0.2:
|
||||
|_ Message signing enabled but not required
|
||||
|_ms-sql-info: ERROR: Script execution failed (use -d to debug)
|
||||
|_clock-skew: mean: -77660d15h50m42s, deviation: 109828d18h12m51s, median: -155321d07h41m24s
|
||||
|
||||
TRACEROUTE
|
||||
HOP RTT ADDRESS
|
||||
1 0.02 ms 192.168.1.111
|
||||
|
||||
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
|
||||
# Nmap done at Sat Apr 4 04:41:33 2026 -- 10 IP addresses (2 hosts up) scanned in 28.27 seconds
|
||||
61
tests/test_build.py
Normal file
61
tests/test_build.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""Smoke test: verify the package and all submodules import cleanly."""
|
||||
import importlib
|
||||
import pytest
|
||||
|
||||
|
||||
MODULES = [
|
||||
"decnet",
|
||||
"decnet.cli",
|
||||
"decnet.config",
|
||||
"decnet.composer",
|
||||
"decnet.deployer",
|
||||
"decnet.network",
|
||||
"decnet.archetypes",
|
||||
"decnet.distros",
|
||||
"decnet.os_fingerprint",
|
||||
"decnet.ini_loader",
|
||||
"decnet.custom_service",
|
||||
"decnet.correlation",
|
||||
"decnet.correlation.engine",
|
||||
"decnet.correlation.graph",
|
||||
"decnet.correlation.parser",
|
||||
"decnet.logging",
|
||||
"decnet.logging.file_handler",
|
||||
"decnet.logging.forwarder",
|
||||
"decnet.logging.syslog_formatter",
|
||||
"decnet.services",
|
||||
"decnet.services.registry",
|
||||
"decnet.services.base",
|
||||
"decnet.services.ssh",
|
||||
"decnet.services.ftp",
|
||||
"decnet.services.http",
|
||||
"decnet.services.smb",
|
||||
"decnet.services.rdp",
|
||||
"decnet.services.smtp",
|
||||
"decnet.services.mysql",
|
||||
"decnet.services.postgres",
|
||||
"decnet.services.redis",
|
||||
"decnet.services.mongodb",
|
||||
"decnet.services.mssql",
|
||||
"decnet.services.elasticsearch",
|
||||
"decnet.services.ldap",
|
||||
"decnet.services.k8s",
|
||||
"decnet.services.docker_api",
|
||||
"decnet.services.vnc",
|
||||
"decnet.services.telnet",
|
||||
"decnet.services.tftp",
|
||||
"decnet.services.snmp",
|
||||
"decnet.services.sip",
|
||||
"decnet.services.mqtt",
|
||||
"decnet.services.llmnr",
|
||||
"decnet.services.imap",
|
||||
"decnet.services.pop3",
|
||||
"decnet.services.conpot",
|
||||
"decnet.services.real_ssh",
|
||||
"decnet.services.registry",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("module", MODULES)
|
||||
def test_module_imports(module):
|
||||
importlib.import_module(module)
|
||||
Reference in New Issue
Block a user