# endlessh-go: an SSH tarpit with Prometheus metrics

> endlessh-go is a Go implementation of an SSH tarpit that slows automated SSH connections while exporting structured Prometheus data and including a Grafana dashboard. It suits operators who want connection telemetry without maintaining a separate log parser.

**shizunge/endlessh-go** — A golang implementation of endlessh (SSH tarpit) exporting Prometheus metrics, visualized by a Grafana dashboard.

- Repository: https://github.com/shizunge/endlessh-go
- Stars: 1,456 · Forks: 55
- Language: Go
- License: GPL-3.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/shizunge-endlessh-go

## Why endlessh-go adds metrics to an SSH tarpit

endlessh-go targets operators of internet-facing SSH services who see repeated automated connection attempts and want to retain those clients without completing an SSH login. Its stated purpose is broader than collecting a failure count: it delays clients while recording connection-level statistics that can feed Prometheus and Grafana.

The project identifies a specific operational problem with the original C implementation of endlessh. Its logs are text based, so an operator who wants a dashboard is pushed toward adding scripts that parse those logs. That arrangement creates another moving part and makes the resulting telemetry dependent on log formatting. endlessh-go instead exposes counters directly through its Prometheus integration, which fits an environment that already scrapes services and operates Grafana dashboards.

This is a specialized telemetry choice, not a general SSH hardening guide. The README does not claim that endlessh-go authenticates users, blocks an address permanently, or replaces firewall rules. It describes a tarpit plus observability. Operators still need a real SSH service for legitimate access and must decide whether delayed connections are acceptable on their chosen address and port. The default SSH listener is `0.0.0.0:2222`, while Prometheus defaults to `0.0.0.0:2112`. Those defaults make local evaluation easy, but both bind broadly enough that network placement deserves attention before deployment.

## The connection, metrics, and GeoIP data path

Based on the repository layout, the main entry point starts the SSH listener, while separate `client/`, `geoip/`, `health/`, and `metrics/` packages divide connection handling, address lookup, health reporting, and metric exposure. The README confirms that clients are deliberately kept waiting, and its first-use instructions say that an SSH client should hang at the connection attempt.

The process does more than hold sockets. It exports counters for connection attempts, completed client disconnects, bytes sent, and accumulated trapped time. It also exposes a connection gauge labeled by remote IP, local port, country, location, and geohash when the relevant GeoIP configuration is available. This structured labeling is the main reason to prefer this implementation over a log-parsing pipeline. Prometheus can query the values directly, and the included `dashboard/` directory provides the Grafana visualization described by the project.

GeoIP lookup is optional. The `-geoip_supplier` flag accepts `off`, `ip-api`, or `max-mind-db`; its default is `off`. Selecting `max-mind-db` also requires a path through `-max_mind_db`. The distinction matters because enabling richer labels adds an external data dependency. The README says the implementation uses about 10MB more memory when offering these extra features, though it does not provide a detailed breakdown by supplier.

Separate health and metrics endpoints are another deliberate design choice. Health checks use `/health` and are disabled unless enabled, with defaults of `127.0.0.1` and port `51000`. Prometheus uses the entry point `metrics`, host `0.0.0.0`, and port `2112`. This separation allows a local health probe to remain bound to loopback while Prometheus listens separately. It also means deployment configuration must account for both ports, although the Dockerfile exposes only the SSH and Prometheus ports because health checking occurs inside the container.

## Building endlessh-go and confirming the tarpit locally

The README documents a source build as the simplest starting point. From a clone of the repository, build the package named in `go.mod`. The resulting binary is named `endlessh-go` by convention from the repository name, although the source build command itself does not set an output filename.

The following command compiles the main package into a local executable:

```bash
go build .
```

Start that binary in the background. With defaults, it listens for SSH connections on port `2222`:

```bash
./endlessh-go &
```

From another terminal, attempt an SSH connection to the machine. According to the README, the client should hang there, which confirms that the tarpit listener is accepting the connection:

```bash
ssh -p 2222 localhost
n```

This test is intentionally local. It does not prove that the service works behind a firewall, load balancer, container port mapping, or PROXY protocol endpoint. It also does not confirm that Prometheus is enabled, because that setting is separate. The README points to `examples/README.md` for a complete stack rather than documenting the complete container and dashboard setup in the main instructions.

Verbose stderr logging is optional. Both `-logtostderr` and `-v=1` must be set to obtain logs in the style described for the C implementation. The health endpoint is also opt-in through `-healthcheck_enabled`. These defaults let a short local test avoid creating the full production configuration, but they also mean that seeing only a delayed SSH session does not tell an operator whether telemetry or health reporting is active.

## Running the Docker image with separate SSH and metrics ports

The repository includes a multi-stage Dockerfile that builds a static Go binary in `golang`, copies it into `gcr.io/distroless/base`, runs as the `nobody` user, and enables health checking by default. The image exposes ports `2222` and `2112`, but a runtime container must still publish the ports that clients and Prometheus need to reach.

The README provides this direct Docker invocation:

```bash
docker run -d -p 2222:2222 shizunge/endlessh-go -logtostderr -v=1
```

That command publishes container port `2222` as local port `2222` and enables the documented logging flags. It does not publish `2112`, so the Prometheus endpoint remains inaccessible from the host unless another `-p` mapping is supplied. Likewise, the image's default command includes `-healthcheck_enabled`, but the container health check performs a request inside the container and does not make `/health` externally reachable.

The Dockerfile confirms that the image uses `CGO_ENABLED=0 go build -o endlessh .`, then copies `/endlessh/endlessh` into a distroless base. Because the final image is distroless, operators should not expect an interactive shell or a conventional package-managed Linux environment inside it. Configuration therefore has to be supplied through the entry point and command-line flags. The examples include `docker-simple`, `docker-maxmind`, and `kustomize-simple`, but the truncated main README does not provide their complete commands.

## Tuning connection delay, capacity, and protocol boundaries

endlessh-go exposes several controls that determine how the tarpit behaves. `-interval_ms` sets the message delay and defaults to `1000`. `-line_length` limits banner line length and defaults to `32`. These settings shape the data sent to held clients, but the README does not prescribe safe values for every network or recommend a particular delay policy.

`-max_clients` defaults to `4096`, placing an explicit ceiling on concurrent clients. That cap is relevant because each delayed connection consumes server resources. The README gives no capacity measurements or guidance for sizing memory and file descriptors, so the default should be treated as configuration rather than a proven workload limit.

The listener supports `tcp`, `tcp4`, and `tcp6` through `-conn_type`, and multiple `-port` flags can create multiple listeners. PROXY protocol support is disabled unless `-proxy_protocol_enabled` is set. Its header timeout defaults to `200` milliseconds, after which an invalid header is ignored according to the flag description. Enabling PROXY protocol changes the expected input from connecting clients to infrastructure that supplies valid headers. A direct SSH test against that endpoint can fail when a plain client does not provide them.

Prometheus lifecycle has a separate concern. `-prometheus_clean_unseen_seconds` removes series after an IP has not been seen for the configured period, while `0` disables that cleanup. The repository does not document the resulting cardinality or retention implications. Enabling cleanup can control stale address series, but it also means historical dashboards may lose per-IP continuity. That behavior should be checked against the intended alerting and investigation period.

## License, upgrades, and the original endlessh alternative

The project is licensed under GPL-3.0, and the Dockerfile labels the published image `GPLv3`. Distribution or integration may therefore carry GPL obligations, although this review does not provide legal advice. Operators should review the repository `LICENSE` for their own deployment and distribution model.

Upgrade cost depends on how the service is deployed. A source installation requires rebuilding after updating dependencies and the repository. A container installation requires replacing the image, but the supplied direct Docker example does not pin a digest. The `go.mod` file requires Go `1.27.1` and direct dependencies for glog, MaxMind GeoIP, geohashing, PROXY protocol handling, and Prometheus client libraries. The repository records releases such as `2026.0730.0`, `2026.0328.0`, and `2025.0914.0`; the last push was on 2026-10-13. The repository is not archived, but the documentation does not provide a migration policy between release tags.

The clearest documented alternative is the original C implementation of endlessh. Its main path is text logging, while endlessh-go generates structured Prometheus metrics and includes dashboard material. The original project may fit a deployment that already accepts log collection, whereas endlessh-go avoids a custom parser tied to log text. Conversely, if Prometheus and Grafana are not part of the desired stack, endlessh-go's primary differentiator adds operational configuration rather than replacing the basic tarpit role.

## Conclusion

Adopt endlessh-go when you specifically want an SSH tarpit that emits structured metrics for Prometheus and Grafana. Do not choose it merely as a replacement for normal SSH access controls, because the documented behavior is to keep clients waiting rather than authenticate them. Before deployment, verify the intended listening address and port, decide whether GeoIP lookup should be enabled, confirm that metrics remain on a trusted network, and test both SSH exposure and Prometheus scrape access. For a plain Go build followed by Docker examples, the documented `go build .`, `./endlessh-go &`, and `docker run -d -p 2222:2222 shizunge/endlessh-go -logtostderr -v=1` commands establish the expected path.

## FAQ

### What port does endlessh-go listen on by default?

The SSH listener defaults to port `2222`. The Prometheus listener is separate and defaults to port `2112`.

### How do I enable Prometheus and GeoIP data in endlessh-go?

Prometheus is enabled with `-prometheus_enabled`; the older `-enable_prometheus` flag is deprecated. GeoIP accepts `off`, `ip-api`, or `max-mind-db` through `-geoip_supplier`, with `off` as the default.

### Can I get endlessh-style logs from endlessh-go?

Set both `-logtostderr` and `-v=1`. The README says the resulting log goes to stderr.

## Sources

- [Issues](https://github.com/shizunge/endlessh-go/issues)
- [License: GPL-3.0](https://github.com/shizunge/endlessh-go/blob/main/LICENSE)
- [README](https://github.com/shizunge/endlessh-go/blob/main/README.md)
- [Releases](https://github.com/shizunge/endlessh-go/releases)
- [shizunge/endlessh-go on GitHub](https://github.com/shizunge/endlessh-go)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/shizunge-endlessh-go
