Skip to main content

Web file manager for Linux with PAM auth, per-user privilege isolation via setuid, and bioinformatics-friendly previews

Project description

filemgr

A lightweight, multi-user web file manager for Linux servers with real PAM authentication and per-user privilege isolation. Each login performs filesystem operations through a setuid-dropped child process, so access control is enforced by the kernel — two users using the same tool at the same time cannot see or touch each other's files unless the filesystem permissions allow it.

Extra batteries for bioinformatics workflows: recognizes FASTQ / BAM / VCF / GFF / BED / H5AD / RData / ipynb / SIF and more, with transparent .gz preview.

Features

  • PAM login against real system accounts (with a configurable whitelist)
  • Multi-user isolation via setuid per request (service runs as root, work happens as the logged-in user's uid)
  • Browse, upload, download, rename, delete, mkdir with keyboard shortcuts, drag-and-drop, and right-click context menus
  • Sortable list with sticky headers and aria-sort; sort by name / size / modified time
  • Fuzzy search (VS Code-style subsequence scoring) — instant local filter in the current directory, Enter triggers a recursive global search from the home root with match highlighting
  • Previews for text, code, images, video, audio, PDF; touchpad pinch and keyboard zoom for images; transparent gunzip preview for .fastq.gz, .vcf.gz, .fa.gz, .bed.gz, and other compressed text files
  • Folder sizes computed recursively with a timeout + file-count cap to avoid runaway scans
  • Statistics panel at the top of the page: total usage, file count, folder count, recent-7-days modifications, breakdown by type (with bio-aware categories), top-N largest files, most-recently-modified files; click any type to see top-N files of that type with an adjustable N
  • Recycle bin: delete is a soft move to ~/.filemgr-trash, toast shows an "Undo" button, a recycle-bin modal lists entries with remaining-time indicator, and items older than trash_retention_days (default 3) are auto-purged
  • Transfer panel with progress bar, live speed, ETA, and cancel button (for uploads and downloads)
  • HTTP Range support for video/audio scrubbing and resumable downloads
  • URL deep linking — current directory and search state live in the URL hash, so refresh / share / back-button all just work
  • Accessibility: aria-label on icon buttons, focus-visible outlines, aria-live="polite" toasts, keyboard navigation on rows (Enter / Space / Delete / F2 / Shift+F10 / ContextMenu), and prefers-reduced-motion support
  • Dark mode: follows prefers-color-scheme by default, with a manual toggle in the top bar (persisted in localStorage)
  • i18n: ships with English and Simplified Chinese; defaults to the browser's navigator.language, switch via the EN/中 top-bar button (persisted in localStorage)
  • List virtualization kicks in automatically above 500 files per directory so huge home dirs stay smooth

Screenshots

Main view (English)
English UI (dark theme) — stats panel up top, sortable file list below. Switch via the EN/中 button in the top bar.
Main view (中文)
中文 UI — same view, different language. Preference persists in localStorage.
Global fuzzy search
Fuzzy search — subsequence matching with score-ranked results and match highlighting.
Top-N by file type
Top-N by type — click any category in the stats panel to see its largest files; N is adjustable.
In-browser text preview
Text preview — line numbers, word-wrap toggle, 1 MB cap; transparent .gz decompression for bio formats.
Login page
Login — PAM auth against real system accounts on a whitelist.

How it works

┌───────────────────────────────┐        ┌────────────────────────────┐
│ Browser                       │  HTTPS │ uvicorn + FastAPI (root)   │
│  - cookie session             │◀──────▶│  - PAM auth                │
│  - no mutation of fs directly │        │  - per-request helper.py   │
└───────────────────────────────┘        │    subprocess:             │
                                         │    initgroups/setgid/setuid│
                                         │    to the logged-in user   │
                                         │    → os.scandir/open/...   │
                                         └──────────┬─────────────────┘
                                                    │ kernel-enforced
                                                    ▼ permissions
                                              /home/data/zrx/...
  • app.py runs as root (via systemd) so it can drop privileges for each request.
  • Every filesystem operation is executed by spawning helper.py with args --uid --gid --home … and a sub-command (list, stat, dirsize, read_stream, write_stream, mkdir, rename, delete, search, stats, top_by_type, trash_*). The helper drops privileges immediately via os.setgroups([]) / os.initgroups / os.setgid / os.setuid, then does the work.
  • A small per-session stat cache plus HTTP ETag / Cache-Control on media previews keeps typical browsing snappy without sacrificing isolation.

Prerequisites

  • Linux with systemd
  • Python 3.11+ (uses tomllib; tested on 3.12)
  • libpam0g-dev headers (Debian/Ubuntu) to build the python-pam wheel
  • A PAM service on the host (usually already present — login, common-auth, passwd, and sshd are auto-tried as fallbacks)
  • Root on the host (the service and setuid require it)

Install & run

# 1. Install. A virtualenv is strongly recommended so pip doesn't fight
#    Debian's PEP 668 protection.
python3 -m venv /opt/filemgr-venv
/opt/filemgr-venv/bin/pip install git+https://github.com/Lings01/filemgr.git

# 2. Generate a config and edit the [[users]] whitelist.
sudo mkdir -p /etc/filemgr
sudo /opt/filemgr-venv/bin/filemgr init-config /etc/filemgr/config.toml
sudo $EDITOR /etc/filemgr/config.toml

# 3. Install and start the systemd unit.
sudo /opt/filemgr-venv/bin/filemgr install-service --config /etc/filemgr/config.toml
sudo systemctl enable --now filemgr
filemgr status

Or, to try it quickly without systemd (needs sudo for real setuid):

sudo /opt/filemgr-venv/bin/filemgr run --config /etc/filemgr/config.toml

The filemgr CLI exposes:

Command What it does
filemgr run Run the server in the foreground
filemgr init-config [PATH] Write a sample config.toml to PATH (default ./config.toml)
filemgr install-service Generate and install the systemd unit (needs root)
filemgr uninstall-service Remove the systemd unit
filemgr status systemctl status filemgr
filemgr logs [-f] [-n N] journalctl -u filemgr
filemgr version Print the version

Config file is discovered in this order: --config$FILEMGR_CONFIG./config.toml~/.config/filemgr/config.toml/etc/filemgr/config.toml.

Open http://127.0.0.1:8765 (or your configured listen_host:port) in a browser. For access from another machine, either change listen_host to 0.0.0.0 and open a firewall port, or use an SSH tunnel:

ssh -L 8765:127.0.0.1:8765 you@server

For production, put nginx/Caddy in front for TLS. A sample nginx block is included at the end of this README.

Development install

git clone https://github.com/Lings01/filemgr.git
cd filemgr
python3 -m venv venv
./venv/bin/pip install -e .
./venv/bin/filemgr init-config ./config.toml
./venv/bin/filemgr run

Configuration (config.toml)

listen_host = "127.0.0.1"
listen_port = 8765
session_ttl_seconds = 28800
max_upload_bytes = 10_737_418_240
dirsize_max_files = 100_000
dirsize_timeout_seconds = 30
preview_text_max_bytes = 1_048_576
pam_service = "login"
trash_retention_days = 3

[[users]]
name = "alice"
root = "/home/alice"
  • pam_service: the PAM service name used for authentication. If the primary value fails, common-auth, passwd, and sshd are tried as fallbacks.
  • [[users]]: repeatable block. name must exist as a system account; root is the starting directory a user sees after login (defaults to their passwd-entry home).

Keyboard shortcuts

Key Action
/ or Ctrl+F Focus the search box
Enter (in search) Run global recursive search from home
Esc (in search) Clear search, return to the current directory
R Refresh the current directory
Backspace Go up one level
Enter (on a row) Open: enter folder / preview file
Space (on a row) Toggle selection
Delete Move selected to recycle bin
F2 Rename the selected item
Shift+F10 / ContextMenu Open the row action menu
Ctrl+wheel / pinch Zoom (image preview; also works in PDF viewer)
+ / - / 0 / 1 Image preview: zoom in / out / fit / 1:1

Security model

  1. Authentication is delegated to PAM via python-pam. Failed logins pay a fixed 1-second penalty to resist brute force. Only accounts listed in [[users]] can authenticate, even if PAM would accept other users.
  2. Authorization is enforced by the kernel. app.py forks a helper subprocess and the helper calls setgroups([]) / initgroups / setgid / setuid to the logged-in uid/gid before touching the filesystem. Read, write, and traversal permissions are then whatever Linux says they are.
  3. Path validation in the helper rejects paths that escape the configured home root (via symlinks or ..). This is a UX guard; the kernel is still the ground truth.
  4. Session tokens are 256-bit secrets.token_urlsafe, stored server-side in memory. Cookies are HttpOnly + SameSite=Strict.
  5. The service must run as root. This is required to drop privileges per request. Running as a non-root user means filesystem operations fail.

Recycle bin

Soft deletes move the file or folder to ~/.filemgr-trash/{id}__{name} and write a sidecar JSON under ~/.filemgr-trash/.meta/{id}.json with the original path and deletion timestamp.

  • The toast after a successful delete shows an "Undo" button for 7 seconds.
  • A recycle-bin modal (top-bar button) lists every entry, with remaining time before auto-purge, per-item "Restore" / "Permanently delete", and an "Empty recycle bin" button.
  • Auto-purge happens opportunistically on every trash_list call and on every delete call — no cron / systemd timer needed.

Project layout

filemgr/
├── pyproject.toml              Package + entry point definition
├── README.md
├── LICENSE
├── src/
│   └── filemgr/
│       ├── __init__.py
│       ├── app.py              FastAPI app: endpoints, session, PAM, helper RPC
│       ├── helper.py           Setuid child process; all filesystem ops live here
│       ├── cli.py              `filemgr` CLI entry point
│       ├── static/             Single-page frontend (vanilla HTML/CSS/JS)
│       │   ├── index.html
│       │   ├── app.js
│       │   └── app.css
│       └── templates/
│           ├── config.toml.example
│           └── filemgr.service
└── docs/screenshots/

Optional: nginx reverse proxy (HTTPS)

server {
    listen 443 ssl http2;
    server_name files.example.com;

    ssl_certificate     /etc/letsencrypt/live/files.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/files.example.com/privkey.pem;

    client_max_body_size 10G;
    proxy_request_buffering off;   # stream uploads
    proxy_buffering off;           # stream downloads
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;

    location / {
        proxy_pass http://127.0.0.1:8765;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For  $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Non-goals

  • Not a cloud storage service: no federation, no external auth, no sharing links to strangers.
  • Not a full OS file manager replacement: no desktop icons, no mount management, no permission editor (use your shell).
  • Not hardened against a malicious service operator: the service runs as root, so whoever controls the box controls the files anyway. The guarantees here are between end users of the same box, not against root.

License

MIT — see LICENSE.

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

filemgr-0.1.0.tar.gz (72.7 kB view details)

Uploaded Source

Built Distribution

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

filemgr-0.1.0-py3-none-any.whl (69.6 kB view details)

Uploaded Python 3

File details

Details for the file filemgr-0.1.0.tar.gz.

File metadata

  • Download URL: filemgr-0.1.0.tar.gz
  • Upload date:
  • Size: 72.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for filemgr-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b9b8d531e25b509d8149b653802d19131012b3aeb9ea5a8bd6004ac34c940678
MD5 60e0e216ef3cb377cfb51c9b00e306e6
BLAKE2b-256 1e7faef15ee9301d738c91dbb1dcf9539a35a5e6d6d8c2ffc87d550b0ac8e917

See more details on using hashes here.

File details

Details for the file filemgr-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: filemgr-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 69.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for filemgr-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4abdf5ec0009a779196f31351c158e705c523736e002e269bdf2728f47bb477a
MD5 2ce5188c9c56e0406612bf5a88fda19d
BLAKE2b-256 7c026f5b59c20fd8c1f1aee42f42406c403e39c826a688c93753535b0a77f1d9

See more details on using hashes here.

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