Skip to main content

Monitor network traffic per executable using BPF

Project description

Picosnitch

  • 🔔 Receive notifications whenever a new program connects to the network, or when it's modified
  • 📈 Monitors your bandwidth, breaking down traffic by executable, hash, parent, domain, port, or user over time
  • 🌍 Web and terminal interfaces with GeoIP lookups for each connection (IP Geolocation by DB-IP)
  • 🛡️ Can optionally check hashes or executables using VirusTotal
  • 🚀 Executable hashes are cached based on device + inode for improved performance
  • 🐳 Detects applications running inside containers, multiple versions of the same app are differentiated based on their hash
  • 🕵️ Uses BPF for accurate, low overhead bandwidth monitoring and fanotify to watch executables for modification
  • 👨‍👦 Since applications can call others to send/receive data for them, the parent and grandparent executables (with hashes) are also logged for each connection
  • 🧰 Pragmatic and minimalist design focusing on accurate detection with clear and reliable error reporting when it isn't possible
picosnitch web UI: bandwidth grouped by executable with drill-down
picosnitch webui
browse and chart past connections
picosnitch terminal UI: bandwidth per executable
picosnitch tui
read-only browser over the same DB
picosnitch top: live event feed sorted by bytes received
picosnitch top
live event feed

More screenshots and short demo videos in the picosnitch screenshots gallery.

Installation

Packaging status

The recommended install is a system-wide pipx install. It works on every Linux distribution with Python >= 3.12 and a kernel new enough to run modern libbpf CO-RE programs.

sudo pipx ensurepath --global
sudo pipx install picosnitch --global
sudo picosnitch systemd
sudo systemctl enable --now picosnitch
  • requires pipx >= 1.5.0 (this is when the --global flag was added); see pipx installation if you don't already have it
  • sudo picosnitch systemd writes /usr/lib/systemd/system/picosnitch.service
  • install with sudo pipx install 'picosnitch[sql]' --global for optional MariaDB / MySQL / PostgreSQL drivers for remote logging

Usage

  • Run/enable the daemon
    • sudo systemctl enable|disable picosnitch: autostart on reboot
    • sudo systemctl start|stop|restart picosnitch: daemon lifecycle
    • or without systemd: sudo picosnitch start|stop|restart
    • or to keep it in the foreground: sudo picosnitch start-no-daemon
  • picosnitch webui: web UI for browsing past connections
  • picosnitch tui: terminal UI for browsing past connections
  • sudo picosnitch top: live event feed (requires root to read the daemon's event socket; starts and stops its own daemon if one isn't running)
  • picosnitch status: show daemon pid and systemd service state
  • picosnitch help: full usage

Configuration

Config is stored at /etc/picosnitch/config.toml and is created with defaults on first run.

[database]
enabled = true                # write connection logs to /var/lib/picosnitch/picosnitch.db (SQLite)
retention_days = 30           # how many days to keep connection logs in the local database
                              # (the remote database is append-only; see [database.remote])
write_limit_seconds = 10      # minimum time between connection log entries
                              # increasing it groups traffic into larger time windows, decreasing
                              # disk writes, time precision, and database size
text_log = false              # also write a CSV connection log to /var/log/picosnitch/conn.log

[database.remote]             # optional: also write connection logs to an external SQL server
                              # used for off-system / tamper-evident logs (see Logging below).
                              # mirrors the local SQLite schema (connections, executables,
                              # domains, addresses).
                              # set `client` to "mariadb", "psycopg", "psycopg2", or "pymysql";
                              # add the rest of the connection parameters as key/value pairs and
                              # optionally `connections_table` to override the default; this lets
                              # multiple hosts share one server with a `connections` table each
                              # while reusing the shared `executables`/`domains`/`addresses`

[data]
owner = "root"                # owner for files in /etc/picosnitch, /var/lib/picosnitch,
group = "root"                # /var/log/picosnitch, and /var/cache/picosnitch
mode = "0644"                 # mode applied to those files (directories add execute bits)

[log]
addresses = true              # log remote addresses for each connection
commands = true               # log command line args for each executable
ports = true                  # log local and remote ports for each connection
ignore_ports = []             # list of ints; matching connections are omitted from the log
ignore_domains = []           # list of strings in reverse-dns notation (matches all subdomains)
ignore_ips = []               # list of IPs/CIDRs (e.g. "192.168.0.0/16")
ignore_sha256 = []            # list of executable sha256 hashes
                              # the process name, executable, and hash are still recorded

[desktop]
user = ""                     # username to send notifications to; defaults to $SUDO_UID
notifications = true          # try connecting to dbus to show desktop notifications
geoip_lookup = true           # annotate remote addresses with a country code in the TUI/webui
                              # uses the DB-IP Country Lite CSV cached under /var/cache/picosnitch

[monitoring]
every_exe = false             # check every running executable, not just ones that open sockets
                              # these are treated as "connections" with a port of -1
                              # experimental; expect occasional errors for short-lived processes
                              # if you only want process logs (no hashes), see execsnoop / forkstat
perf_ring_buffer_pages = 256  # power of two number of pages per BPF perf buffer
                              # only change this if you are seeing missed-event errors
# rlimit_nofile = 65536       # optional int; raises RLIMIT_NOFILE for the daemon
                              # picosnitch caches one file descriptor per (device, inode);
                              # set this if you see "Too many open files" errors
# st_dev_mask = 0             # optional int; masks the device number reported for opened fds
                              # auto-detected at startup; only set this to override the default
                              # for filesystems that reuse inodes across subvolumes (e.g. btrfs)

[virustotal]
api_key = ""                  # VirusTotal API key, leave blank to disable
file_upload = false           # upload the executable when its hash isn't already known
                              # leave false to only submit hashes
request_limit_seconds = 15    # seconds between requests (free-tier quota)

Restart picosnitch for any configuration changes to take effect.

Logging

Picosnitch splits its on-disk state across the FHS directories. All defaults assume the systemd unit (the unit also creates these on first start).

Path Contents
/etc/picosnitch/config.toml configuration
/var/lib/picosnitch/picosnitch.db SQLite connection log (read by picosnitch tui and picosnitch webui)
/var/lib/picosnitch/state.json known executables + sha256 hashes, used to decide when to notify
/var/log/picosnitch/exe.log history of new-executable notifications
/var/log/picosnitch/error.log errors (also surfaced as desktop notifications)
/var/log/picosnitch/conn.log optional CSV connection log (enable with [database].text_log = true)
/var/cache/picosnitch/ DB-IP Country Lite database, refreshed monthly
/run/picosnitch/picosnitch.pid pid file (world-readable, used by picosnitch status)
/run/picosnitch/events.sock live event socket consumed by picosnitch top

[database.remote] can be used to additionally ship every connection to a MariaDB, MySQL, or PostgreSQL server. It mirrors the local SQLite schema (connections, executables, domains, addresses); only the connections table name can be overridden (via connections_table), which lets multiple hosts share one server with a connections table each while reusing the shared reference tables. Picosnitch only ever issues INSERT against the remote (no retention, no garbage collection), so it is intended for keeping an off-system copy of your logs; grant INSERT only to prevent an adversary on the monitored host from deleting picosnitch's off-system logs.

conn.log is a CSV with these fields (commas, newlines, and NUL characters are stripped from values): entry time, sent bytes, received bytes, event count, executable path, process name, cmdline, sha256, parent executable, parent name, parent cmdline, parent sha256, grandparent executable, grandparent name, grandparent cmdline, grandparent sha256, user id, address family, protocol, local port, remote port, local address, remote address, domain, network namespace.

Entries in error.log are usually triggered by an unusually large burst of new processes or connections, by extremely short-lived processes that exit before picosnitch can open a file descriptor to them, or by suspending the system while a new executable is being hashed. Unexpected entries are worth investigating, since picosnitch is designed to surface an error whenever a process slips past its normal observation path.

Limitations

  • Picosnitch is a userspace daemon. A program with sufficient privileges can alter picosnitch or its logs, or fall back to communication channels invisible to the kernel. Use [database.remote] for an off-system copy of the connection log, and consider corroborating with a separate router/firewall.
  • Detecting open sockets and the originating process is reliable via BPF, but the executable path and name could be ambiguous or spoofed. As a countermeasure picosnitch hashes the executable itself; only the process executable is hashed, so shared libraries, scripts, and runtime extensions are not covered by the hash.
  • The device and inode of the opened file descriptor are checked against what the BPF program reported to detect runtime replacement of the executable. Filesystems that reuse inodes across subvolumes (e.g. btrfs) defeat this check and are auto-detected at startup (st_dev_mask = 0).
  • For extremely short-lived processes, picosnitch may not be able to open a file descriptor in time to hash the executable. The connection is still logged with everything else picosnitch has, along with an entry in error.log.
  • A large influx of new processes or connections can cause missed log entries, since picosnitch preserves system traffic latency rather than blocking to catch up. Such incidents are detected, logged, and notified, and can be mitigated by raising [monitoring].perf_ring_buffer_pages.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

picosnitch-2.0.0.tar.gz (109.4 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

picosnitch-2.0.0-py3-none-manylinux_2_34_x86_64.whl (168.8 kB view details)

Uploaded Python 3manylinux: glibc 2.34+ x86-64

picosnitch-2.0.0-py3-none-manylinux_2_34_aarch64.whl (169.1 kB view details)

Uploaded Python 3manylinux: glibc 2.34+ ARM64

File details

Details for the file picosnitch-2.0.0.tar.gz.

File metadata

  • Download URL: picosnitch-2.0.0.tar.gz
  • Upload date:
  • Size: 109.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for picosnitch-2.0.0.tar.gz
Algorithm Hash digest
SHA256 65e411dc8f83bee3c2e731b15fc2f7c9022ecbfd1e615d21fadcf17de11669a6
MD5 bb58e765511ed9eb6c9a5affa7ecd380
BLAKE2b-256 d2d6356a93bb3f1072ada2af64bf551ac1d546857d40ad92748e8b3dd48d3f2f

See more details on using hashes here.

Provenance

The following attestation bundles were made for picosnitch-2.0.0.tar.gz:

Publisher: pypi.yml on elesiuta/picosnitch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file picosnitch-2.0.0-py3-none-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for picosnitch-2.0.0-py3-none-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 c9cd2917e6169739f1f1939efd331c82133433f767332b8a0600ff8b38a044ad
MD5 3dcdeb17dc6d515b45bcec3d12dacdb6
BLAKE2b-256 5047fca6ec8ae24f249f6f39fec972ff14a01b4e7d3aed4c7a3c32b1c8349fb4

See more details on using hashes here.

Provenance

The following attestation bundles were made for picosnitch-2.0.0-py3-none-manylinux_2_34_x86_64.whl:

Publisher: pypi.yml on elesiuta/picosnitch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file picosnitch-2.0.0-py3-none-manylinux_2_34_aarch64.whl.

File metadata

File hashes

Hashes for picosnitch-2.0.0-py3-none-manylinux_2_34_aarch64.whl
Algorithm Hash digest
SHA256 c93b82faa2e11ddee8fdf929bde354570107c4e0ae821090a5e9a1da2a82f90d
MD5 f9b17e0154f9b98c2b992794b02573f6
BLAKE2b-256 ff27929b75a6f95c17d7acc6dac408050cc3381cb00079c297b2f15d76821e08

See more details on using hashes here.

Provenance

The following attestation bundles were made for picosnitch-2.0.0-py3-none-manylinux_2_34_aarch64.whl:

Publisher: pypi.yml on elesiuta/picosnitch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page