{"id":31618058,"url":"https://github.com/alaub81/portwatcher","last_synced_at":"2026-05-08T09:31:38.814Z","repository":{"id":316947348,"uuid":"1065407461","full_name":"alaub81/portwatcher","owner":"alaub81","description":"Portwatcher Honeypot","archived":false,"fork":false,"pushed_at":"2026-03-16T09:35:02.000Z","size":57,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-16T22:22:53.788Z","etag":null,"topics":["docker","docker-compose","geoip","honeypot","monitoring","ntfy","python","tcp"],"latest_commit_sha":null,"homepage":"https://lhlab.wiki/wiki/Portwatcher_–_TCP-Verbindungen_überwachen,_Scans_erkennen,_sofort_alarmieren","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alaub81.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-27T17:01:26.000Z","updated_at":"2026-03-16T09:34:59.000Z","dependencies_parsed_at":"2025-09-27T20:38:11.402Z","dependency_job_id":null,"html_url":"https://github.com/alaub81/portwatcher","commit_stats":null,"previous_names":["alaub81/portwatcher"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/alaub81/portwatcher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alaub81%2Fportwatcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alaub81%2Fportwatcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alaub81%2Fportwatcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alaub81%2Fportwatcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alaub81","download_url":"https://codeload.github.com/alaub81/portwatcher/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alaub81%2Fportwatcher/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32774704,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"ssl_error","status_checked_at":"2026-05-08T08:22:45.650Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","docker-compose","geoip","honeypot","monitoring","ntfy","python","tcp"],"created_at":"2025-10-06T13:45:17.706Z","updated_at":"2026-05-08T09:31:38.782Z","avatar_url":"https://github.com/alaub81.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Portwatcher\n\nA lightweight TCP watcher (Python/asyncio) that logs inbound connections and notifies you via **email** and **ntfy push**.  \nIncludes **offline geolocation (MaxMind)** with an optional online fallback, **rDNS**, **per‑IP cooldown**, **healthcheck/CIDR ignore**, **payload snippet \u0026 protocol detection** (HTTP/TLS/SSH/…).\n\n## Features\n\n- Listens on a configurable TCP port (IPv4/IPv6)\n- Notifications via **email** (SMTP) and **ntfy** (toggle per channel)\n- Subject/title like `DE Heidelberg 78.43.247.28` (ISO country, city/region, IP)\n- **Offline geo** using MaxMind **GeoLite2-City/ASN** (sidecar keeps DBs fresh)\n- **rDNS** (short timeout, optional)\n- **Per-IP cooldown** to prevent mail/push storms\n- **Ignore** healthchecks and local/CIDR ranges (optionally also remove from logs)\n- **Payload snippet** (text/hex/base64, length-limited) + **protocol detection**: HTTP (method/host/UA), TLS (SNI/ALPN/optional JA3), SSH banner, VNC/RDP/Redis…\n- Timezone‑aware timestamps (default: `Europe/Berlin`)\n- Multi-arch container image on **GHCR** (`linux/amd64`, `linux/arm64`)\n\n---\n\n## Architecture\n\n- **portwatcher** (app): `/app/portwatcher.py` listens on the port and sends notifications\n- **geoipupdate** (sidecar): downloads/refreshes MaxMind DBs into a **shared volume** `/usr/share/GeoIP`\n\n```txt\n[geoipupdate] ──writes──\u003e  [volume: /usr/share/GeoIP]  \u003c──reads── [portwatcher]\n```\n\n---\n\n## Quick start\n\n1) **Clone** and enter the repo\n\n```bash\ngit clone https://github.com/alaub81/portwatcher.git\ncd portwatcher\n```\n\n2) **Create configs** from the examples\n\n- Copy `.env.example` → `.env` and adjust (port, timezone …)\n- Copy `portwatcher.env.example` → `portwatcher.env` and adjust (SMTP, ntfy, …)\n- Copy `geoipupdate.env.example` → `geoipupdate.env` and fill in your **MaxMind** Account/Key\n\n3) **Start with Docker Compose**\n\n```bash\ndocker compose pull\ndocker compose up -d\ndocker compose logs -f portwatcher\n```\n\n---\n\n## Docker Compose (excerpt)\n\n```yaml\nservices:\n  geoipupdate:\n    image: ghcr.io/maxmind/geoipupdate:latest\n    restart: unless-stopped\n    env_file:\n      - geoipupdate.env\n    environment:\n      TZ: ${PW_TZ}  \n    volumes:\n      - geoip-db:/usr/share/GeoIP\n    healthcheck:\n      test: [\"CMD\", \"sh\", \"-c\", \"test -f /usr/share/GeoIP/GeoLite2-City.mmdb -a -f /usr/share/GeoIP/GeoLite2-ASN.mmdb\"]\n      interval: 1m\n      timeout: 5s\n      retries: 3\n      start_period: 30s\n\n  portwatcher:\n    image: ghcr.io/alaub81/portwatcher:latest\n    restart: unless-stopped\n    env_file:\n      - portwatcher.env\n      - .env\n    environment:\n      TZ: ${PW_TZ}\n      # vermeidet __pycache__-Writes auf read-only FS\n      PYTHONDONTWRITEBYTECODE: \"1\"\n    ports:\n      - \"${PW_PORT:-5555}:${PW_PORT:-5555}/tcp\"\n    volumes:\n      - geoip-db:/usr/share/GeoIP\n    user: \"10001:10001\"\n    read_only: true\n    tmpfs:\n      - /tmp\n    cap_drop:\n      - ALL\n    security_opt:\n      - no-new-privileges:true\n    ulimits:\n      nofile: 16384\n    depends_on:\n      geoipupdate:\n        #condition: service_started  # oder 'service_healthy' falls HC gesetzt\n        condition: service_healthy\n    healthcheck:\n      test: [\"CMD\", \"/bin/sh\", \"/app/healthcheck.sh\"]\n      interval: 30s\n      timeout: 5s\n      retries: 3\n    logging:\n      options:\n        max-size: \"10m\"\n        max-file: \"3\"\n\nvolumes:\n  geoip-db:\n```\n\n**Local dev / own build (optional):**  \nYou can also build locally (Dockerfile included) or add a `docker-compose.dev.yml` override.\n\n---\n\n## Configuration\n\nAll variables are documented in **`*.env.example`** files. Key groups:\n\n### Basics\n\n- `PW_HOST` (`0.0.0.0`) – bind address\n- `PW_LOG_LEVEL` (`INFO`/`DEBUG`/…)\n- `PW_BANNER` – optional banner text sent to clients\n\n### Notifications\n\n- `PW_ENABLE_EMAIL`, `PW_ENABLE_PUSH`\n- **SMTP:** `PW_SMTP_SERVER`, `PW_SMTP_PORT`, `PW_SMTP_STARTTLS`, `PW_SMTP_USER`, `PW_SMTP_PASS` **or** `PW_SMTP_PASS_FILE`, `PW_MAIL_FROM`, `PW_MAIL_TO`\n- **ntfy:** `PW_NTFY_SERVER`, `PW_NTFY_TOPIC`, `PW_NTFY_PRIORITY`, `PW_NTFY_TAGS`\n\n### Geo / rDNS\n\n- **Offline:** `PW_GEOIP_CITY_DB=/usr/share/GeoIP/GeoLite2-City.mmdb`, `PW_GEOIP_ASN_DB=/usr/share/GeoIP/GeoLite2-ASN.mmdb`\n- **Online (optional):** `PW_IPAPI_ENABLE=0|1`, `PW_IPAPI_BUDGET_PER_MIN`\n- `PW_RDNS_ENABLE`, `PW_RDNS_TIMEOUT`\n\n### Ignore / Healthchecks\n\n- `PW_NOTIFY_IGNORE_LOOPBACK=1` – mute 127.0.0.0/8, ::1\n- `PW_NOTIFY_IGNORE_PRIVATES=0|1` – mute RFC1918/link-local\n- `PW_NOTIFY_IGNORE_CIDRS=\"172.24.0.0/16,::1/128\"` – custom ranges\n- `PW_LOG_IGNORE_SUPPRESSED=1` – also remove suppressed events from logs\n\n### Payload \u0026 detection\n\n- `PW_CAPTURE_PAYLOAD=1`, `PW_PROBE_MAX_BYTES` (default 2048), `PW_PROBE_TIMEOUT_MS` (default 800)\n- `PW_PAYLOAD_IN_NOTIF=1`, `PW_PAYLOAD_MAX_CHARS` (default 600), `PW_PAYLOAD_MODE=auto|text|hex|base64`, `PW_PAYLOAD_STRIP_CONTROL=1`\n- `PW_TLS_JA3=0|1` – optional TLS JA3 fingerprint\n\n### Rate-limit, time, detail fields\n\n- `PW_RL_COOLDOWN_S` (default 1800), `PW_RL_FORGET_S` (default 86400)\n- `PW_TZ=Europe/Berlin`, `PW_TS_INCLUDE_UTC=0|1`\n- `PW_INCLUDE_CITY=1`, `PW_INCLUDE_COORDS=0`, `PW_LATLON_PRECISION=2`\n\n\u003e **Note:** `.env` and `geoipupdate.env` are **gitignored**. Examples are tracked: `.env.example`, `geoipupdate.env.example`.\n\n---\n\n## Local build (optional)\n\nThe simplified Dockerfile uses `python:3.13-slim` and installs `python3-geoip2` via APT.\n\n```bash\ndocker build -t portwatcher:dev .\ndocker run --rm -it -p 1417:1417 --env-file .env -v geoip-db:/usr/share/GeoIP portwatcher:dev\n```\n\n---\n\n## CI/CD (GitHub Actions)\n\n- **CI:** Python syntax check \u0026 Docker build (no push)\n- **Release:** Buildx (driver `docker-container`) + push to **GHCR** (`ghcr.io/alaub81/portwatcher:{latest,SHA}`), optional multi-arch\n\n\u003e Tip: In repo **Settings → Actions → Workflow permissions**, set **Read and write** so `GITHUB_TOKEN` can push to GHCR.\n\n---\n\n## Security\n\n- Never commit secrets. Use `*.env` (gitignored), `PW_SMTP_PASS_FILE` (Docker Secrets), or Actions secrets.\n- Container runs **non-root**, filesystem **read-only**, `cap_drop: [ALL]`, `no-new-privileges`.\n- Payload snippets are length‑limited and control chars are sanitized; consider header redaction if needed.\n\n---\n\n## Troubleshooting\n\n- **Geo fields empty:** ensure DBs exist in the app container (`/usr/share/GeoIP`); check sidecar health.\n- **`ModuleNotFoundError: geoip2`:** ensure the image contains `python3-geoip2` (APT) or `geoip2` (pip).\n- **Healthcheck messages/logs:** set `PW_NOTIFY_IGNORE_LOOPBACK=1` and optionally `PW_LOG_IGNORE_SUPPRESSED=1`.\n- **Timezone/UTC suffix:** set `PW_TZ`, optionally enable `PW_TS_INCLUDE_UTC`.\n- **GHA cache error (“docker driver”):** set up Buildx with `driver: docker-container`.\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falaub81%2Fportwatcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falaub81%2Fportwatcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falaub81%2Fportwatcher/lists"}