FILE / ScuroNeko/est

README.md

Исходный файл и его история в репозитории.
FILE 80278ee801e08850a160098d4bcfe5d767183144
Files
est/README.md
T
ScuroNeko 80278ee801
Golang lint / docker-smoke (push) Successful in 13s
Golang lint / lint (push) Successful in 15s
readme cleanup
2026-08-06 12:53:05 +03:00

263 lines
8.3 KiB
Markdown

# est
`est` is a small command-line tool that manages SSH port-forwarding tunnels
from a TOML configuration file. It starts one `ssh` process per tunnel and
shuts every process down when the application receives `SIGINT` (`Ctrl+C`) or
`SIGTERM`.
## Features
- Local forwarding (`rtl`): expose a local port and forward it through SSH to
an address reachable from the remote server.
- Remote forwarding (`ltr`): expose a port on the remote SSH server and
forward it to a local address.
- Run multiple tunnels from one configuration file.
- Use SSH host aliases, a shared SSH config file, and per-tunnel identity keys.
- Fail early when mandatory tunnel fields are missing.
- Start SSH with `ExitOnForwardFailure=yes`, so a refused port forward fails
the application instead of silently leaving a connected but unusable tunnel.
`ssh` must be installed and available on `PATH`.
## Install the CLI
Install the latest released version with Go:
```sh
go install git.scuroneko.dev/ScuroNeko/est/cmd/est@latest
```
The command is installed to `$GOBIN`, or to `$(go env GOPATH)/bin` when
`GOBIN` is not set. Ensure that directory is on your `PATH`:
```sh
export PATH="$(go env GOPATH)/bin:$PATH"
est --config /etc/est/tunnels.toml
```
For a private Git server, configure the Go tool to fetch the module directly:
```sh
go env -w GOPRIVATE=git.scuroneko.dev
go env -w GONOSUMDB=git.scuroneko.dev
go env -w GOPROXY=direct
```
Your Git SSH key or access token must already grant read access to the
repository. Pin a version for repeatable deployments:
```sh
go install git.scuroneko.dev/ScuroNeko/est/cmd/est@v1.0.0
```
## Run from source
The required Go version is declared in [go.mod](go.mod).
```sh
cp config.example.toml config.toml
# Edit config.toml before starting est.
go run ./cmd/est --config ./config.toml
```
`config.toml` is ignored by Git so private hosts and key paths are not
committed accidentally. The default configuration path is `./config.toml`;
use `--config` or `-c` to select another file.
## Docker
Pull the published image:
```sh
docker login git.scuroneko.dev
docker pull git.scuroneko.dev/scuroneko/est:latest
```
Or build it locally:
```sh
docker build -t git.scuroneko.dev/scuroneko/est:local .
```
Run the local Docker smoke test to build the final image and verify that its
SSH client is available:
```sh
./scripts/docker-smoke-test.sh
```
The release build publishes both `linux/amd64` and `linux/arm64` images. It
requires a Buildx builder with ARM64 emulation installed on an AMD64 host:
```sh
docker run --privileged --rm tonistiigi/binfmt --install arm64
docker buildx create --name est-builder --driver docker-container --use
docker buildx inspect --bootstrap
make docker-build
```
`make docker-build` pushes
`git.scuroneko.dev/scuroneko/est:latest` and
`git.scuroneko.dev/scuroneko/est:1.0.0`. Adjust the Makefile tags before a
different release.
Do not bake your private key or tunnel configuration into the image. Mount
them at runtime instead. The command below reuses the host SSH configuration,
keys, and `known_hosts` file without granting the container write access:
```sh
docker run --rm --init \
--user "$(id -u):$(id -g)" \
--env HOME=/ssh \
--volume "$PWD/config.toml:/app/config.toml:ro" \
--volume "$HOME/.ssh:/ssh:ro" \
git.scuroneko.dev/scuroneko/est:latest --config /app/config.toml
```
With `HOME=/ssh`, `est` and OpenSSH can discover `/ssh/config` and
`/ssh/known_hosts`. The mounted configuration can use paths such as
`identityFile = "/ssh/id_ed25519"`. If you use another mount location, set
`sshConfig` and `identityFile` to paths inside the container.
The image intentionally does not publish ports. For `rtl` tunnels, publish the
local listening port explicitly when it must be reachable outside Docker, for
example `-p 127.0.0.1:5433:5433`.
## Configuration
The configuration file is TOML. Every `[[entry]]` section describes one
tunnel.
| Field | Required | Description |
|----------------|---------------|-----------------------------------------------------------------------------------------------------|
| `direction` | yes | Tunnel direction: `ltr` or `rtl`. |
| `host` | yes | SSH destination or host alias, for example `user@example.com` or `vps`. |
| `localIP` | no | Address on the machine that runs `est`. Defaults to `127.0.0.1`. |
| `localPort` | yes | Port on the machine that runs `est`. Must be non-zero. |
| `remoteIP` | no | Remote bind/destination address, depending on the direction. Defaults to `127.0.0.1`. |
| `remotePort` | yes | Remote bind/destination port. Must be non-zero. |
| `identityFile` | no | Private SSH key path for this tunnel. |
| `retry` | no | A top-level or per-entry retry table described below. |
| `sshConfig` | no, top level | SSH config path applied to every tunnel. |
`host` must be non-empty and ports must be non-zero. An empty `localIP` or
`remoteIP` is converted to `127.0.0.1`. When `sshConfig` is omitted, `est`
uses `~/.ssh/config` if the file exists.
The optional top-level retry table applies to every entry:
```toml
[retry]
count = 5
delay = "5s"
```
`count` is the number of reconnect attempts after the initial SSH process
exits. Its default is `5`; use `0` to disable reconnects and `-1` to retry
forever. `delay` is a positive Go duration such as `"5s"`, `"500ms"`, or
`"1m"`; its default is `"5s"`.
Add `[entry.retry]` immediately after a tunnel entry to override the top-level
retry settings for that tunnel. This is a full override: if an entry retry
block omits `count` or `delay`, the omitted value uses the standard default,
not the top-level value.
### Remote forwarding (`ltr`)
`ltr` means local-to-remote. It opens a port on the remote SSH server and
forwards connections to an address reachable from the local machine:
```toml
[[entry]]
direction = "ltr"
host = "vps"
localIP = "127.0.0.1"
localPort = 22
remoteIP = "127.0.0.1"
remotePort = 2222
[entry.retry]
count = -1
delay = "2s"
```
This produces the equivalent SSH command:
```sh
ssh -N -T -o ExitOnForwardFailure=yes \
-R 127.0.0.1:2222:127.0.0.1:22 vps
```
Connecting to `127.0.0.1:2222` on `vps` reaches `127.0.0.1:22` on the machine
running `est`. Binding a remote address other than loopback may require the SSH
server's `GatewayPorts` setting.
### Local forwarding (`rtl`)
`rtl` means remote-to-local. It opens a port on the machine that runs `est`
and forwards connections to an address reachable from the remote SSH server:
```toml
[[entry]]
direction = "rtl"
host = "vps"
localIP = "127.0.0.1"
localPort = 5433
remoteIP = "127.0.0.1"
remotePort = 5432
identityFile = "/home/user/.ssh/vps_key"
```
Equivalent SSH command:
```sh
ssh -N -T -o ExitOnForwardFailure=yes \
-i /home/user/.ssh/vps_key \
-L 127.0.0.1:5433:127.0.0.1:5432 vps
```
After the connection is established, the service reachable by `vps` at
`127.0.0.1:5432` is available locally at `127.0.0.1:5433`.
### Shared SSH config
Set `sshConfig` at the top level to use a non-default SSH configuration file:
```toml
sshConfig = "/etc/est/ssh_config"
[[entry]]
direction = "ltr"
host = "vps"
localIP = "127.0.0.1"
localPort = 22
remoteIP = "127.0.0.1"
remotePort = 2222
```
## Lifecycle and errors
Press `Ctrl+C` or send `SIGTERM` to stop `est`. The shared application context
is cancelled and the `ssh` subprocesses started by it are terminated.
If any tunnel fails to start or exhausts its reconnect attempts, `est` logs the
SSH exit code and standard error, cancels the shared context, and stops the
remaining tunnels. Add `-v`, `-vv`, or `-vvv` to the SSH arguments temporarily
when a connection needs deeper diagnosis.
## Development checks
Run the Go checks locally:
```sh
go test ./...
go vet ./...
```
The Gitea workflow verifies Go formatting, runs `golangci-lint`, and builds
the Docker image with the SSH-client smoke test on pushes and pull requests.