ssh-timeout
This commit is contained in:
commit
b5f0ab4908
|
@ -0,0 +1,57 @@
|
||||||
|
# SSH Timeout Proxy
|
||||||
|
|
||||||
|
## Description
|
||||||
|
SSH Timeout Proxy is a simple TCP reverse proxy designed to handle SSH connections, forwarding them to a specified backend SSH server and enforcing a maximum connection duration. It utilizes environment variables to configure the backend SSH server's address and the maximum allowed connection duration, making it flexible for various deployment environments.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- **Go** (at least Go 1.13) - To run the program.
|
||||||
|
- **SSH Server** - The backend server where SSH connections should be forwarded. Typically, this is `localhost:22` for testing purposes.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
1. **Clone the repository:**
|
||||||
|
```bash
|
||||||
|
git clone https://example.com/ssh-timeout-proxy.git
|
||||||
|
cd ssh-timeout-proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Build the project:**
|
||||||
|
```bash
|
||||||
|
go build -o ssh-timeout
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
The application uses two environment variables:
|
||||||
|
- `SSH_BACKEND`: Specifies the IP address and port of the backend SSH server (e.g., "localhost:22").
|
||||||
|
- `SSH_MAX_DURATION`: Specifies the maximum duration (in seconds) that a connection should be allowed to persist.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
To run the application, use the following command, setting the environment variables as needed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SSH_BACKEND="your_backend_ip:port" SSH_MAX_DURATION="duration_in_seconds" ./ssh-timeout
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, to set the backend SSH server to `localhost` on port `22` and limit connection duration to 600 seconds (10 minutes), you would use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SSH_BACKEND="localhost:22" SSH_MAX_DURATION="600" ./ssh-timeout
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
To test the SSH Timeout Proxy, perform the following steps:
|
||||||
|
|
||||||
|
1. **Start the Proxy:**
|
||||||
|
Run the proxy with the desired backend and maximum duration configuration as shown in the Usage section.
|
||||||
|
|
||||||
|
2. **SSH Through the Proxy:**
|
||||||
|
In a separate terminal window, use SSH to connect through the proxy:
|
||||||
|
```bash
|
||||||
|
ssh -p 2222 your-username@localhost
|
||||||
|
```
|
||||||
|
Replace `your-username` with your actual username, and ensure you connect to the port where the proxy listens (default `2222`).
|
||||||
|
|
||||||
|
3. **Observe:**
|
||||||
|
Monitor the terminal running the proxy to see the connection and disconnection logs. Ensure the connection is terminated after the specified duration.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
Contributions to the SSH Timeout Proxy are welcome. Please feel free to fork the repository, make changes, and submit pull requests.
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Default architecture
|
||||||
|
DEFAULT_ARCH="linux/amd64"
|
||||||
|
|
||||||
|
# Supported architectures (adjust as needed)
|
||||||
|
ARCHITECTURES=("linux/amd64" "linux/arm64" "linux/arm/v7" "darwin/amd64" "darwin/arm64")
|
||||||
|
|
||||||
|
# Ensure all necessary directories exist and go modules are ready
|
||||||
|
prepare_build() {
|
||||||
|
# Create necessary directories if they don't exist
|
||||||
|
mkdir -p dist
|
||||||
|
mkdir -p build_logs
|
||||||
|
|
||||||
|
# Initialize go modules if go.mod does not exist
|
||||||
|
if [ ! -f go.mod ]; then
|
||||||
|
echo "Initializing Go modules"
|
||||||
|
go mod init yourmodule # Replace 'yourmodule' with your actual module name or path
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fetch and ensure all dependencies are up to date
|
||||||
|
echo "Checking dependencies..."
|
||||||
|
go mod tidy
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build function
|
||||||
|
build_binary() {
|
||||||
|
os=$1
|
||||||
|
arch=$2
|
||||||
|
output_name="ssh-timeout"
|
||||||
|
|
||||||
|
if [[ "$os/$arch" != "$DEFAULT_ARCH" ]]; then
|
||||||
|
output_name="${output_name}_${os}_${arch}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
output_name="dist/${output_name}"
|
||||||
|
|
||||||
|
echo "Building for ${os}/${arch} -> ${output_name}"
|
||||||
|
GOOS=${os} GOARCH=${arch} go build -o ${output_name} main.go 2>build_logs/${os}_${arch}_build.log
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Successfully built ${output_name}"
|
||||||
|
else
|
||||||
|
echo "Failed to build ${output_name}. Check build_logs/${os}_${arch}_build.log for errors."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main Build Process
|
||||||
|
prepare_build
|
||||||
|
for arch in "${ARCHITECTURES[@]}"; do
|
||||||
|
IFS='/' read -r -a parts <<< "$arch" # Split architecture string
|
||||||
|
os=${parts[0]}
|
||||||
|
arch=${parts[1]}
|
||||||
|
build_binary $os $arch
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Build process completed."
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
module yourmodule
|
||||||
|
|
||||||
|
go 1.21.1
|
||||||
|
|
||||||
|
require github.com/google/uuid v1.6.0
|
|
@ -0,0 +1,2 @@
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
@ -0,0 +1,78 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
listenAddr := ":2222" // The local port on which to listen for incoming SSH connections.
|
||||||
|
backendAddr := os.Getenv("SSH_BACKEND") // Backend SSH server address from environment variable.
|
||||||
|
maxDuration := os.Getenv("SSH_MAX_DURATION") // Max connection duration from environment variable.
|
||||||
|
|
||||||
|
duration, err := strconv.Atoi(maxDuration)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Invalid SSH_MAX_DURATION value: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to open listener: %s", err)
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
log.Println("Listening on", listenAddr)
|
||||||
|
|
||||||
|
for {
|
||||||
|
clientConn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to accept connection:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go handleConnection(clientConn, backendAddr, time.Duration(duration)*time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConnection(clientConn net.Conn, backendAddr string, maxDuration time.Duration) {
|
||||||
|
connID := uuid.New().String()
|
||||||
|
log.Printf("New connection [%s] started from %s", connID, clientConn.RemoteAddr())
|
||||||
|
|
||||||
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
backendConn, err := net.Dial("tcp", backendAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to connect to backend [%s]: %s", connID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer backendConn.Close()
|
||||||
|
|
||||||
|
// Set up a timer to close both connections when the maxDuration is exceeded
|
||||||
|
timer := time.AfterFunc(maxDuration, func() {
|
||||||
|
log.Printf("Connection [%s] exceeded max duration, terminating", connID)
|
||||||
|
clientConn.Close()
|
||||||
|
backendConn.Close()
|
||||||
|
})
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
// Forward traffic between the client and the backend
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(backendConn, clientConn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error forwarding from client to backend [%s]: %s", connID, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = io.Copy(clientConn, backendConn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error forwarding from backend to client [%s]: %s", connID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Connection [%s] terminated", connID)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue