Sign inSign up

onesystems/tools

By onesystems

•Updated 8 days ago

This project packages SMTP ingress, an optional web test UI, and IT tools.

Image
Networking
Security
API management
0

4.1K

onesystems/tools repository overview

⁠OneSystems Tools (Docker)

SMTP ingress (Echo Mail with SPF, DKIM, DMARC analysis), an optional web test UI for one-shot recipient addresses, and IT tools (DNS, GeoIP, TLS/certs, mail generators, and more) in a single Alpine-based Python image.

  • Image: onesystems/tools⁠ (latest, version tags e.g. 1.3.0)
  • Entrypoint: python -m onesystems_tools (legacy: python echo_maild.py)
  • Compose examples: docker-compose.yml (profiles bridge and host-tls)

Designed for public exposure: SSRF-filtered outbound targets, HTTP rate limits, CSP with per-request nonces, trusted-proxy checks, non-root process, no open SMTP relay by default.


⁠Features

  • SMTP listener (aiosmtpd) for configured addresses — classic Echo reply and/or test-<token>@… web flow
  • SPF, DKIM, DMARC evaluation (DNS-based; optional fixed resolvers via TOOLS_DNS_SERVERS)
  • Optional HTTP UI (Uvicorn + FastAPI) — dashboard, tool forms, JSON API under /api/tools/…
  • IT tools: DNS, PTR, subdomains, ping/traceroute, WHOIS/RDAP, GeoIP (local MaxMind MMDB), TLS/cert helpers, mail auth (SPF/DMARC/DKIM), record generators (SPF / DMARC / MTA-STS / TLS-RPT under /generators), client Autoconfig (SRV + Mozilla XML), Exchange Server Check, Base64/password/hashes, optional LibreSpeed under /speedtest (gated), and more
  • Static assets under files/ (Bootstrap 5.3.3, no CDN required)
  • Optional Matomo (TOOLS_MATOMO_*) and ad slots (TOOLS_WEB_ADS_*) for trusted HTML only
  • Hardening: SSRF filter, HTTP rate limits, CSP nonces, HSTS, no-new-privileges, non-root runtime

⁠Quick start

⁠Pull from Docker Hub
docker pull onesystems/tools:latest

Minimal Compose (bridge / port mapping):

services:
  tools:
    image: onesystems/tools:latest
    restart: unless-stopped
    ports:
      - "25:8025"
      # With TOOLS_WEB_ENABLED=true:
      # - "8080:8080"
    environment:
      TOOLS_MAIL_ADDRESSES: [email protected]
      TOOLS_MAIL_HOSTNAME: tools.example.com
      # TOOLS_WEB_ENABLED: "true"
      # WEB_LISTEN_PORT: "8080"
⁠Compose profiles (repo docker-compose.yml)

Pick exactly one profile:

  • bridge — host 25 → container 8025 (classic mapping)
  • host-tls — host network + Caddy (HTTPS) + web on 127.0.0.1:8080 + optional LibreSpeed
docker compose --profile bridge up -d
# or
cp .env.example .env   # set secrets if using LibreSpeed
docker compose --profile host-tls up -d

Do not run both profiles on the same host if they compete for port 25.


⁠Layout (source / self-build)

src/onesystems_tools/
  __main__.py          # python -m onesystems_tools
  config.py / envutil.py
  smtp/                # server, security, analysis, outbound
  web/                 # FastAPI UI, store, middleware
  api/                 # /api/tools JSON router
  services/            # net_guard, rate_limit, geoip, toolkits
echo_maild.py          # thin legacy wrapper → same main()
files/                 # static assets (css/js/vendor/logo)
speedtest/             # LibreSpeed sidecar Dockerfile + branding
caddy/                 # Caddyfile for host-tls profile
tests/                 # pytest

Env vars use the TOOLS_* / SMTP_* / WEB_* prefixes (ECHO_* / ECHO_TOOLS_* are no longer read).


⁠Build (optional)

Multi-arch example (set VERSION so the image labels / TOOLS_VERSION match the release):

docker buildx create --name multiarch --driver docker-container --use
docker buildx inspect --bootstrap
docker run --privileged --rm tonistiigi/binfmt --install all

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --push \
  --build-arg VERSION="1.3.0" \
  --build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --tag onesystems/tools:1.3.0 \
  --tag onesystems/tools:latest \
  .

If VERSION is omitted, the image resolves the version from pyproject.toml at build time (never leaves a bare dev placeholder).


⁠Configuration overview

⁠HTTP / Web UI (onesystems_tools.web)
VariablePurpose
TOOLS_WEB_SUBPATHBase path for the web test UI (default /echo).
TOOLS_WEB_ENABLEDtrue/1: start the HTTP server (with WEB_LISTEN_*).
TOOLS_WEB_FILES_DIRStatic files directory (default /app/files in the image).
TOOLS_WEB_LOGO / TOOLS_WEB_FAVICONFilenames inside the files dir.
TOOLS_WEB_BRANDNavbar brand text (default Tools).
TOOLS_WEB_APP_NAMEapplication-name / og:site_name. Empty → TOOLS_WEB_BRAND, else Tools.
TOOLS_WEB_OG_LOCALEOpen Graph og:locale (default en_US).
TOOLS_WEB_SUBTITLEOptional subtitle in the compact top bar.
TOOLS_WEB_FOOTER_COPYRIGHTFooter text; {YEAR} → current year. Empty → built-in default.
TOOLS_WEB_PRIVACY_URL / TOOLS_WEB_IMPRINT_URLOptional footer links.
TOOLS_WEB_ADS_*Raw HTML slots: AFTER_NAV, MAIN_TOP, MAIN_BOTTOM, FOOTER (trusted admins only).
TOOLS_WEB_ADS_ORIGINSExtra ad-server origins for CSP if auto-detection is not enough.
TOOLS_CSP_EXTRA_ORIGINSOrigins added to connect-src / frame-src.
TOOLS_CSP_SCRIPT_SRC / TOOLS_CSP_CONNECT_SRC / TOOLS_CSP_FRAME_SRCOptional origin allowlists. HTML CSP script-src uses nonce + strict-dynamic.
TOOLS_IT_TOOLS_ENABLEDtrue/1: IT tools (HTML + /api/tools/…). false: mail/web test only.
TOOLS_WEB_CURL_ROOT_IPDefault true: GET / with curl/… UA returns client IP as plain text.
TOOLS_WEB_META_*SEO / Open Graph (DESCRIPTION, KEYWORDS, AUTHOR, SITE_URL, …).
TOOLS_MATOMO_URL / TOOLS_MATOMO_SITE_IDOptional Matomo.
TOOLS_MATOMO_COOKIE_DOMAINe.g. *.tools.example.com; empty → omit.

Static files are served under /web-assets/…. Mounting ./files:/app/files replaces the entire directory — include css/, js/, vendor/ or mount single files only.

⁠HTTP server (Uvicorn)
VariablePurpose
WEB_LISTEN_HOST / WEB_LISTEN_PORTBind (default 0.0.0.0 / 8080). Behind a reverse proxy prefer 127.0.0.1.
WEB_FORWARDED_ALLOW_IPSPeers whose X-Forwarded-* Uvicorn accepts (default 127.0.0.1,::1).
TOOLS_TRUSTED_PROXIESApp-level CIDRs trusted for client IP / scheme / host. Empty = never trust X-Forwarded-*.
UVICORN_LOG_LEVELDefault warning.
WEB_GEO_*Optional HTTP geo allow/deny (MaxMind Country/City MMDB).
⁠Web test storage
VariablePurpose
TOOLS_WEB_TTL_PENDINGSeconds to wait for mail after minting a token (default 3600).
TOOLS_WEB_TTL_STOREDSeconds to keep stored reports (default 86400).
⁠SMTP ingress
VariablePurpose
TOOLS_MAIL_ADDRESSESComma-separated recipient addresses.
TOOLS_RECEIVER_DOMAINOptional explicit domain for analysis.
TOOLS_MAIL_HOSTNAMEHELO / hostname in messages.
TOOLS_REPLY_ENABLEDtrue: classic Echo replies; false (default): ingest + web tests only.
SMTP_RELAY_HOST / PORT / USER / PASSWORD / SSLOptional submission relay (e.g. Mailcow).
TOOLS_DNS_SERVERSOptional public resolver IPs for lookups.
SMTP_LISTEN_HOST / SMTP_LISTEN_PORTSMTP bind (default 0.0.0.0 / 8025).
SMTP_*Rate limits, size limits, allow/deny lists, geo — see comments in docker-compose.yml.
⁠Mail record generators

UI under /generators (SPF wizard, DMARC, MTA-STS, TLS-RPT). Live preview via /api/tools/mail-generate and related load endpoints. No extra env vars required beyond TOOLS_IT_TOOLS_ENABLED.

⁠IT tools API
VariablePurpose
TOOLS_GEOIP_DBPath to GeoLite2-City or Country .mmdb inside the container.
TOOLS_SUBDOMAIN_CAP / TOOLS_PORTSCAN_CAP / …Caps — see onesystems_tools.api.router defaults.
TOOLS_RIPE_TIMEOUTRIPEstat HTTP timeout (default 12).
TOOLS_WHOIS_TIMEOUTWHOIS / RDAP timeout (default 15).
TOOLS_LOOKUP_CACHE_TTLIn-memory cache TTL for WHOIS/AS/RIPEstat (600; 0 = off).
⁠SSRF filter (services/net_guard)

User-supplied hosts/IPs/URLs are checked before any outbound connection (public IPs only; DNS-rebinding mitigated by connecting to the validated IP).

VariablePurpose
TOOLS_ALLOW_PRIVATE_TARGETSDefault false. Opt-in for private/loopback targets (trusted labs only).
TOOLS_EXTRA_BLOCK_CIDRSExtra CIDRs always blocked.
⁠HTTP rate limit (services/rate_limit)
VariableDefaultPurpose
TOOLS_HTTP_API_RATE_PER_MIN60Sustained /api/… requests per client bucket.
TOOLS_HTTP_API_BURST20Burst for /api/….
TOOLS_HTTP_UI_RATE_PER_MIN300Sustained UI / assets.
TOOLS_HTTP_UI_BURST120Burst for UI / assets.
TOOLS_HTTP_IPV6_PREFIX64IPv6 bucket key.
TOOLS_HTTP_IPV4_PREFIX32IPv4 bucket key.
TOOLS_HTTP_RATE_EXEMPT_CIDRS(empty)Networks that skip the limit.
TOOLS_HTTP_RATE_LOGtrueLog rejected requests.

Limit hits return 429 with Retry-After.

⁠LibreSpeed (/speedtest, optional)

Optional sidecar (speedtest/Dockerfile) behind Caddy + Tools forward_auth gate (host-tls profile).

VariablePurpose
TOOLS_SPEEDTEST_ENABLEDtrue: gate active (default false). Needs geo allow and/or deny + MMDB.
TOOLS_SPEEDTEST_GEO_ALLOW_COUNTRIESISO allowlist (e.g. CH,DE,AT,LI).
TOOLS_SPEEDTEST_GEO_DENY_COUNTRIESOptional deny list.
TOOLS_SPEEDTEST_GEO_ALLOW_NON_PUBLICSkip geo for private/loopback (default true).
TOOLS_SPEEDTEST_COOLDOWN_SECPause after a test window (default 300).
TOOLS_SPEEDTEST_TICKET_TTL_SECSession ticket lifetime (default 90).
TOOLS_SPEEDTEST_MAX_CONCURRENTGlobal parallel tickets (default 3).
TOOLS_SPEEDTEST_TICKET_SECRETHMAC secret (set a strong value when enabled).
TOOLS_SPEEDTEST_RESULTS_TTL_HOURSSQLite retention before purge (default 168).
TOOLS_SPEEDTEST_DB_PATHSQLite path in the tools container (same volume as LibreSpeed /database).
TOOLS_SPEEDTEST_SERVERS_FILEOptional multi-server JSON path (default via Compose mount).
TOOLS_SPEEDTEST_STATS_PASSWORDLibreSpeed /results/stats.php password.
TOOLS_SPEEDTEST_GDPR_EMAILContact in LibreSpeed privacy text.
  • UI: /tools/speedtest (info) → /speedtest/ (LibreSpeed). Override logo with ./speedtest/branding/logo.png.
  • Servers: optional ./speedtest/servers.json. Missing/empty → local backend only. With 2+ entries a server picker appears.
  • Secrets: copy .env.example → .env and set TOOLS_SPEEDTEST_TICKET_SECRET / TOOLS_SPEEDTEST_STATS_PASSWORD.
  • Mount a MaxMind Country/City MMDB and point TOOLS_GEOIP_DB / the Compose volume at your host path.

⁠Web test flow

With TOOLS_WEB_ENABLED=true and HTTP exposed:

  1. Open http://<host>:8080/<TOOLS_WEB_SUBPATH>/new (default …/echo/new) or /.
  2. You receive test-<token>@<domain> from TOOLS_MAIL_ADDRESSES.
  3. Send a test message to that address.
  4. /w/<token> auto-refreshes; /r/<token> shows the report.

⁠Networking (NAT / Docker)

Outbound connections often use an internal source IP. Plan SPF, PTR, and optional SMTP_RELAY_* accordingly.

VariableExampleNotes
TOOLS_REPLY_ENABLEDtrueRequired — replies are off by default.
SMTP_RELAY_HOSTmail.example.comMailcow hostname.
SMTP_RELAY_PORT587STARTTLS (default). Use 465 for SMTPS.
SMTP_RELAY_USER[email protected]Mailbox in Mailcow.
SMTP_RELAY_PASSWORD(secret)Prefer env_file / secrets — do not commit.
SMTP_RELAY_SSLtrueOptional; port 465 enables SSL automatically.

Allow sending as the Echo From address and keep SPF/DKIM aligned with Mailcow outbound.


⁠Logs

  • Short connections to 127.0.0.1 at startup are normal (listener check).
  • Connection lost during _handle_client() after QUIT is normal.
  • Empty MAIL FROM:<> DSN-style messages are accepted without reply loops.

⁠Security

  • SSRF filter on outbound targets — TOOLS_ALLOW_PRIVATE_TARGETS=true to opt out.
  • DNS-rebinding mitigation — connect to the validated IP; hostname only as SNI/HELO.
  • HTTP rate limit per client subnet (API vs UI buckets).
  • Trusted proxies — honor X-Forwarded-* only for peers in TOOLS_TRUSTED_PROXIES.
  • Security headers + CSP with per-request nonce / strict-dynamic.
  • Non-root (nobody), no-new-privileges; host-tls adds NET_BIND_SERVICE for port 25.
  • TOOLS_REPLY_ENABLED=false by default — avoid backscatter on public SMTP.
  • Treat TOOLS_WEB_ADS_* as privileged HTML input.
  • Behind a reverse proxy set both WEB_FORWARDED_ALLOW_IPS and TOOLS_TRUSTED_PROXIES.

⁠Development

python3 -m venv .venv && .venv/bin/pip install -r requirements.txt -e . pytest
.venv/bin/pytest -q
python -m onesystems_tools

⁠Author

Michael Kleger — OneSystems GmbH

https://www.onesystems.ch⁠ · [email protected]⁠


⁠License

MIT — free for commercial and private use.

Tag summary

Content type

Image

Digest

sha256:d1d9e5a65…

Size

43.1 MB

Last updated

8 days ago

docker pull onesystems/tools