- Go 98%
- Dockerfile 2%
network_mode: host ensures real client IPs are logged rather than the Docker bridge address. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| data/users/example | ||
| .gitignore | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| main.go | ||
| README.md | ||
| rfc1288.txt | ||
simple-finger
A lightweight finger protocol (RFC 1288) server written in Go. Runs in a container; user data is embedded at build time.
Build
go build ./...
docker build -t simple-finger .
Usage
finger [flags]
-addr string listen address (default ":79")
-data string path to data directory; overrides embedded data
-traditional read user info from /etc/passwd and home directories
-no-forward refuse finger forwarding ({Q2}) queries
-no-list refuse finger list ({C}) queries
Run locally (port 79 requires root or CAP_NET_BIND_SERVICE):
sudo go run .
# or on an unprivileged port for testing:
go run . -addr :7979
Test with nc or the finger client:
echo -e "vbatts\r" | nc localhost 79
echo -e "vbatts+pgpkey\r" | nc localhost 79
finger vbatts@localhost
Data modes
Embedded (default)
User metadata lives in data/users.json; per-user files (plan, project, pgpkey, forward, nofinger) live in data/users/<login>/. All content is compiled into the binary — edit and rebuild to update.
Local directory (-data <dir>)
Same layout as data/, but read from the filesystem at runtime. No rebuild needed to update content.
Traditional (-traditional)
Reads users from /etc/passwd (name, office, and phone are parsed from the GECOS field) and per-user dotfiles from each user's home directory: ~/.plan, ~/.project, ~/.pgpkey, ~/.forward, ~/.nofinger.
Adding a user (embedded or -data mode)
The data/ directory is an example skeleton — replace it with your own content before building.
Each user gets their own directory with an info.json for metadata:
data/users/alice/info.json
data/users/alice/plan
data/users/alice/project
data/users/alice/pgpkey
info.json fields:
{
"login": "alice",
"name": "Alice Example",
"shell": "/bin/bash",
"directory": "/home/alice",
"office": "Remote",
"phone": ""
}
Users without an info.json are ignored. All other per-user files are optional.
Per-user control files
| File | Effect |
|---|---|
forward |
Proxy the query to another finger server (@host or user@host) |
nofinger |
Refuse finger queries with an explicit denial message |
disable |
Hide user entirely — not listed, returns "no such user" on direct query |
Logs
All queries are logged to stdout as structured JSON (RFC 1288 §3.2.7):
{"time":"...","level":"INFO","msg":"query","remote_addr":"1.2.3.4:54321","user":"vbatts","action":"served","suffix":""}
{"time":"...","level":"INFO","msg":"query","remote_addr":"1.2.3.4:54322","user":"vbatts","action":"forward","forward_to":"vbatts@otherhost.com"}
{"time":"...","level":"INFO","msg":"query","remote_addr":"1.2.3.4:54323","user":"ghost","action":"not_found"}
action is one of: served, forward, forward_refused, list_users, list_refused, not_found, nofinger.