Skip to content

db_sqlite: isolate cache db path per user to avoid cross-user permission collisions #181

@markuslf

Description

@markuslf

Problem

db_sqlite.connect() builds the cache db path from disk.get_tmpdir() plus a static filename, with no per-user component (db_sqlite.py, get_filename() around lines 315-321). The result is a shared, world-readable path such as /tmp/linuxfabrik-monitoring-plugins-docker-stats.db.

The first process to run a plugin creates that file as its owner with the default umask (0644). Any later run under a different user can read but not write it. The next write (insert, cut, commit) then fails:

OperationalError: attempt to write a readonly database

which lib.base.coe turns into an UNKNOWN/CRIT abort. This bites whenever a plugin is first run manually (e.g. as root, per several plugins' "Requires root or sudo") and later scheduled under the monitoring agent's own user.

Reproduced:

c = sqlite3.connect('db'); c.execute('CREATE TABLE cpu (...)'); c.commit(); c.close()
os.chmod('db', 0o444)             # owned by another user
sqlite3.connect('db').execute("INSERT ...")   # OperationalError: attempt to write a readonly database

Proposed fix

Embed the user identity into the default filename inside get_filename(), so each user gets its own cache db:

/tmp/linuxfabrik-monitoring-plugins-docker-stats-uid<UID>.db

Per-UID isolation is preferred over making the file world-writable (0666): no shared-state tampering, no symlink/ownership games in a shared /tmp, and trend data still persists across runs for the same runner (UID is stable per user).

Considerations

  • Must stay portable: os.getuid() does not exist on Windows (lib targets py39 and ships Windows plugins). Use a portable identity with a fallback, e.g. getpass.getuser(), sanitized for the filename.
  • Apply centrally in get_filename() so every caller benefits without touching the plugins.
  • Stale shared-path db files from older versions can be left to age out of /tmp or removed by the new code on first run.

Surfaced via Linuxfabrik/monitoring-plugins#1143 (docker-stats), but this affects every plugin using db_sqlite as a cross-run cache.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions