Без опису

Josef Straßl d383deaeb6 expanding 3 тижнів тому
certs d383deaeb6 expanding 3 тижнів тому
static d383deaeb6 expanding 3 тижнів тому
templates d383deaeb6 expanding 3 тижнів тому
.gitignore d383deaeb6 expanding 3 тижнів тому
README.md d383deaeb6 expanding 3 тижнів тому
app.py d383deaeb6 expanding 3 тижнів тому
config.example.toml d383deaeb6 expanding 3 тижнів тому
pps_client.py b6a44ab948 initial poc 3 тижнів тому
requirements.txt b6a44ab948 initial poc 3 тижнів тому
worker.py d383deaeb6 expanding 3 тижнів тому

README.md

PPS Quarantine Manager (PoC)

A small web client for the Proofpoint Protection Server (PPS) quarantine. It lists quarantined mail by folder and runs bulk actions — Release, Report & Release, Delete, Move — as asynchronous background jobs, so the operator never waits on slow PPS API calls (deletes in particular).

Proof of concept: static login, plaintext secrets in a config file, no permission model. SAML/OIDC and RBAC come later.

How it works

  • Frontend (templates/, static/): one page. A folder switcher scopes the whole view to one quarantine folder; the list shows date / sender / recipient / subject with a checkbox per row. Clicking a row opens the message content in an overlay over the list (HTML parts render in a sandboxed iframe). Actions apply to all checked rows, which then disappear from the list immediately.
  • Backend (app.py, pps_client.py): Flask serves the UI and proxies the PPS Quarantine Search REST API. Actions are queued and return instantly (HTTP 202).
  • Worker (worker.py): a daemon thread drains a SQLite-backed job queue, batching message ids into chunked PPS POSTs. Jobs are persistent, so queued work survives a restart; a job left mid-flight is requeued on startup.

Action semantics

UI action PPS API call(s)
Release release without rescan; deletedfolder set so it leaves the folder
Report & Release release (in place, no rescan) then move a copy to report_release_folder
Delete delete with deletedfolder (moves to deleted items, not a hard delete)
Move move to the folder chosen in the dropdown

Report & Release keeps a copy in report_release_folder (default "Debugging - Josef") for manual false-positive submission while the mail is delivered. If this PPS deployment's release relocates the message to the deleted folder (like the admin GUI does), the worker re-finds it there by its stable guid and moves it from there — no configuration needed.

Operations log (audit trail)

Every message the background worker acts on is recorded in a dedicated, rotating log file (worker_log, default worker.log — 5 MB × 5 files). One line per message, e.g.:

2026-07-14 17:39:56 job=#12 action=delete result=ok src='Quarantine' dst='Deleted' localguid=6:6:1 guid=g-aaa from='sender@spam.example' rcpt='victim@corp.com' subject='You won a prize'

result is ok or FAILED (failures also carry error=...). Fields are key=value for easy grep. This is separate from the general application log (stdout, controlled by log_level), which covers requests, job lifecycle, and PPS API calls.

Setup

python3 -m venv venv
venv/bin/pip install -r requirements.txt
cp config.example.toml config.toml
# edit config.toml: PPS host, API credentials, folders, login
venv/bin/python app.py

Open http://127.0.0.1:8080 and sign in with the [auth] credentials from config.toml.

Configuration notes

  • Folders are maintained locally. The PPS API has no endpoint to list folders, so the folders array in config.toml is the source of truth for the switcher, the Move dropdown, and the delete/report targets. Names must match PPS exactly (case- and space-sensitive, e.g. "Debugging - Josef"); a wrong name only errors at action time.
  • list_query = "from=*". The search API requires a from/rcpt/subject filter — it can't list a folder by folder alone. A bare wildcard means "everything in the folder". If your PPS doesn't treat from=* as match-all, set it to something like rcpt=@yourdomain.com.
  • default_days_back. The search API alone returns only the last 24h; this widens the window (uses startdate).
  • Limits. Up to 1000 messages per search (API cap, no pagination). default_limit sets the UI default; the operator can raise it to 1000.
  • verify_tls. PPS admin certs are often self-signed; false skips verification (PoC). Point it at a CA bundle path to verify instead.
  • Mutual TLS (client certificate). If PPS is fronted by nginx requiring a client cert, requests without one fail with 400 No required SSL certificate was sent. Set client_cert (a combined cert+key PEM, or a cert PEM plus client_key for the key). Note this is a TLS client cert, separate from the [pps] Basic-auth credentials — the endpoint may require both.

API credentials

The account in [pps] must be a PPS admin with an API Role that has the Quarantine module enabled (see the PPS management interface / a Proofpoint support ticket for PoD). Auth is HTTP Basic against the admin port (default 10000).

Files

app.py               Flask app: routes, login, config, worker startup
pps_client.py        PPS Quarantine REST client (search / get_raw / act)
worker.py            SQLite job queue + background worker thread
config.example.toml  Config template (copy to config.toml)
templates/           login.html, index.html
static/              app.js, style.css