3115ac64b22e894307efe1a82cd93916b16ea69d
The app sits behind nginx, which caches 200s for 30s — so repeated lookups of the same user are cheap. What bypasses the cache is enumeration of distinct usernames: each is a unique cache key -> miss -> a fresh finger call to the mammut daemon, all attributed to admin's single IP (so the daemon cannot ban the real source). The app only ever receives cache misses, so a per-IP limit here throttles exactly that uncached path without touching the cached hot path. - Flask-Limiter keyed per client IP: 30/min on the finger lookup endpoints, 10/min on /api/upload (auth brute-force), 120/min global default. Index and the container healthcheck are exempt. All limits env-tunable (RATELIMIT_*). - ProxyFix(x_for=1): trust nginx's X-Forwarded-For so the real client IP is used for keying and logging. Without it the app only saw the Docker bridge gateway (172.20.0.1) and every client shared one bucket. - 429 handler (JSON for /api, HTML 429.html otherwise) and WARNING logging of failed/invalid lookups and limit hits, so enumeration is observable.
finger-web
A Flask web application that fronts a finger daemon, with a JSON API, a CLI client, and an MCP server for Claude integration.
Components
| Path | Description |
|---|---|
app.py |
Flask web app and JSON API |
cli/finger.py |
Command-line client |
mcp/server.py |
MCP server for Claude |
Web App
Requirements
- Python 3.9+
- A
fingerbinary available on the server's PATH
Installation
pip install -r requirements.txt
Configuration
All settings are read from environment variables.
| Variable | Description | Default |
|---|---|---|
SECRET_KEY |
Flask secret key | dev-secret-key-change-in-production |
FLASK_DEBUG |
Enable debug mode | True |
BASIC_AUTH_USERS |
Comma-separated user:pass pairs for upload auth |
(none — upload disabled) |
SCP_ENABLED |
Enable SCP transfer of uploaded plan files | false |
REMOTE_HOST |
Remote host for SCP | — |
REMOTE_USER |
Remote user for SCP | — |
REMOTE_PATH |
Remote path for SCP destination | — |
REMOTE_PORT |
Remote SSH port | 22 |
REMOTE_PRIVATE_KEY |
Path to SSH private key | — |
Running
python app.py
# or with gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 app:app
Docker
docker-compose up -d
API
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/finger |
GET/POST | — | Web UI finger query |
/finger/<username> |
GET | — | Web UI finger query (URL form) |
/api/finger |
GET | — | JSON: list logged-in users |
/api/finger/<username> |
GET | — | JSON: finger a specific user |
/api/upload |
POST | Basic | Upload a plan file |
/api/info |
GET | — | API metadata |
Example
curl http://localhost:5000/api/finger/[email protected]
{
"status": "success",
"username": "[email protected]",
"result": "Login: pete\t\t\tName: Pete Blair\n..."
}
CLI
Installation
pip install -r cli/requirements.txt
Configuration
export FINGER_WEB_URL=http://localhost:5000
export FINGER_USER=youruser # only needed for plan uploads
export FINGER_PASS=yourpassword # only needed for plan uploads
Usage
# Finger a user
python cli/finger.py query [email protected]
# Upload your plan file
python cli/finger.py plan ~/.plan
MCP Server
Exposes finger query and plan upload as tools for Claude.
Installation
pip install -r mcp/requirements.txt
Configuration
export FINGER_WEB_URL=http://localhost:5000
export FINGER_USER=youruser # only needed for upload_plan tool
export FINGER_PASS=yourpassword # only needed for upload_plan tool
Running
python mcp/server.py
Claude Desktop configuration
Add to your claude_desktop_config.json:
{
"mcpServers": {
"finger": {
"command": "python",
"args": ["/path/to/finger-web/mcp/server.py"],
"env": {
"FINGER_WEB_URL": "http://localhost:5000",
"FINGER_USER": "youruser",
"FINGER_PASS": "yourpassword"
}
}
}
}
Available tools
| Tool | Description |
|---|---|
finger_user(username) |
Query finger info for a user, or leave empty to list logged-in users |
upload_plan(filename, content) |
Upload or update a plan file |
Languages
Python
49.7%
HTML
20.9%
CSS
14.2%
JavaScript
13.2%
Dockerfile
2%