No description
  • Go 98%
  • Dockerfile 2%
Find a file
Vincent Batts e8d4368d44 Add docker-compose.yml with host networking
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>
2026-06-07 13:10:18 -04:00
data/users/example data: remove sample 'vbatts' data 2026-06-06 13:26:37 -04:00
.gitignore renaming to 'simple-finger' 2026-06-05 22:40:54 -04:00
CLAUDE.md Add disable file to hide users entirely 2026-06-06 13:24:36 -04:00
docker-compose.yml Add docker-compose.yml with host networking 2026-06-07 13:10:18 -04:00
Dockerfile something like a basic working implementation 2026-05-16 21:21:10 -04:00
go.mod renaming to 'simple-finger' 2026-06-05 22:40:54 -04:00
main.go Add disable file to hide users entirely 2026-06-06 13:24:36 -04:00
README.md Add disable file to hide users entirely 2026-06-06 13:24:36 -04:00
rfc1288.txt Add flags, multi-source data, and RFC 1288 security controls 2026-05-16 21:39:13 -04:00

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.