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