stftp
An SSH SFTP-to-TFTP gateway. The server accepts SFTP subsystem requests and forwards file reads and writes to TFTP servers reachable from the gateway host. It is built on golang.org/x/crypto/ssh and does not implement the legacy SCP protocol.
Run the gateway executable
Generate an SSH host key, then start the listener:
ssh-keygen -t ed25519 -f ./ssh_host_ed25519_key
go run ./cmd/stftp-gateway -listen :2222 -host-key ./ssh_host_ed25519_key
make build builds for the current host. The binary is written to dist/<GOOS>-<GOARCH>/stftp-gateway (.exe on Windows). Cross-build with make build-linux, make build-darwin, or make build-windows; set GOARCH to select the target architecture. The Makefile enables CGO for Linux/macOS PAM authentication and disables it for Windows.
The executable accepts OS account passwords and per-user SSH public keys. Public keys must be plain entries in the local account's ~/.ssh/authorized_keys; entries with authorized_keys options are ignored. The SSH username must name a local account with a home directory. On Windows, the key file is under that account's home directory.
On Linux and macOS, password authentication uses PAM service sshd by default; change it with -pam-service. Build with CGO enabled and the platform's PAM headers and library available. PAM account policy is checked after the password.
On Windows, passwords are validated with the Windows local/domain logon provider. Use DOMAIN\username or UPN form (username@example.com) for domain accounts. -pam-service is ignored on Windows.
The executable never stores account passwords. Configure SSH host keys and protect local authorized_keys files as you would for other SSH services. The package API is also available through stftp.ServeSSH with a caller-supplied *ssh.ServerConfig; see the Go API documentation.
Install as a system service
The executable runs in the foreground; the service manager supervises and restarts it.
The process must be able to read each account's .ssh/authorized_keys; PAM password authentication may also need access to system credential databases. The examples run it as root or LocalSystem for that reason. Those identities grant broad local access, so restrict inbound SSH access and protect the host key. The gateway exposes SFTP file operations, not a shell. Use a restricted service account only after granting required access and testing the configured PAM policy.
Linux with systemd
Install the binary and host key. Replace amd64 below if building for another architecture:
sudo install -d -m 0755 /etc/stftp /usr/local/bin
sudo install -m 0755 dist/linux-amd64/stftp-gateway /usr/local/bin/stftp-gateway
sudo install -m 0600 ./ssh_host_ed25519_key /etc/stftp/ssh_host_ed25519_key
Then create /etc/systemd/system/stftp-gateway.service:
[Unit]
Description=SSH SFTP to TFTP gateway
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/stftp-gateway -listen :2222 -host-key /etc/stftp/ssh_host_ed25519_key -pam-service sshd
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Load and start it with sudo systemctl daemon-reload && sudo systemctl enable --now stftp-gateway.
macOS with launchd
Install the binary and host key (arm64 shown; substitute your target architecture), then save a launch daemon plist as /Library/LaunchDaemons/org.codeberg.stftp-gateway.plist:
sudo install -d -m 0755 /etc/stftp /usr/local/bin
sudo install -m 0755 dist/darwin-arm64/stftp-gateway /usr/local/bin/stftp-gateway
sudo install -m 0600 ./ssh_host_ed25519_key /etc/stftp/ssh_host_ed25519_key
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>org.codeberg.stftp-gateway</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/stftp-gateway</string>
<string>-listen</string><string>:2222</string>
<string>-host-key</string><string>/etc/stftp/ssh_host_ed25519_key</string>
<string>-pam-service</string><string>sshd</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardOutPath</key><string>/var/log/stftp-gateway.log</string>
<key>StandardErrorPath</key><string>/var/log/stftp-gateway.log</string>
</dict>
</plist>
Set the plist owner to root:wheel and mode 0644, then load it with sudo launchctl bootstrap system /Library/LaunchDaemons/org.codeberg.stftp-gateway.plist.
Windows with NSSM
The executable is a console program, not a native Windows Service. Use a service wrapper such as NSSM. From an elevated PowerShell prompt, install the binary and host key, then configure NSSM:
New-Item -ItemType Directory -Force "C:\Program Files\stftp-gateway", "C:\ProgramData\stftp"
Copy-Item .\dist\windows-amd64\stftp-gateway.exe "C:\Program Files\stftp-gateway\stftp-gateway.exe"
Copy-Item .\ssh_host_ed25519_key "C:\ProgramData\stftp\ssh_host_ed25519_key"
icacls "C:\ProgramData\stftp\ssh_host_ed25519_key" /inheritance:r /grant:r "SYSTEM:F" "Administrators:F"
If NSSM runs under a different account, grant that identity access instead of SYSTEM.
nssm install StftpGateway "C:\Program Files\stftp-gateway\stftp-gateway.exe"
nssm set StftpGateway AppDirectory "C:\Program Files\stftp-gateway"
nssm set StftpGateway AppParameters "-listen :2222 -host-key C:\ProgramData\stftp\ssh_host_ed25519_key"
nssm set StftpGateway Start SERVICE_AUTO_START
nssm start StftpGateway
Run the service under LocalSystem or an account with access to the authorized-key files and local/domain account logon. Protect the host key with an ACL that grants access only to the service identity and administrators.
Address a TFTP file
For scp, use the SSH server as the remote host and put the TFTP address and filename in the remote path:
scp -P 2222 ./firmware.bin gateway.example:192.0.2.10/firmware.bin
scp -P 2222 gateway.example:192.0.2.10/firmware.bin ./firmware.bin
Current OpenSSH scp uses SFTP by default, which this server supports. Legacy scp -O is not supported.
An SFTP client addresses the same file with the SFTP path /192.0.2.10/firmware.bin:
sftp -P 2222 gateway.example
sftp> get /192.0.2.10/firmware.bin
sftp> put ./firmware.bin /192.0.2.10/firmware.bin
The TFTP address may be a hostname or an IP address. TFTP uses UDP port 69 unless the address includes a port, for example tftp.example.net:1069/firmware.bin. Bracket IPv6 literals, for example /[2001:db8::10]:1069/firmware.bin.
The gateway supports single-file octet-mode reads and writes. TFTP has no directory-listing operation, so clients cannot browse directories, list files, create directories, or perform recursive transfers. Nested TFTP filenames are forwarded as written, for example /192.0.2.10/boot/image.bin. SFTP file attributes are ignored because TFTP has no metadata operation; rename and delete are unsupported. SFTP uploads are staged in a temporary file before transfer to TFTP.