Zum Inhalt springen

Plink (PuTTY Link)

Plink is PuTTY’s command-line SSH client for non-interactive remote command execution, port forwarding, tunneling, and automation across Windows, Linux, and macOS.

Installation

Windows

# Chocolatey
choco install putty

# Scoop
scoop install putty

# Manual download
# https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
# Extract plink.exe and add to PATH

Linux

# Ubuntu/Debian
sudo apt-get install putty-tools

# RHEL/CentOS/Fedora
sudo dnf install putty

# Arch
sudo pacman -S putty

# Build from source
git clone https://git.tartarus.org/simon/putty.git
cd putty
mkdir build && cd build
cmake ..
make && sudo make install

macOS

# Homebrew
brew install putty

# Build from source (requires Xcode)
git clone https://git.tartarus.org/simon/putty.git
cd putty/unix
./configure && make && sudo make install

Basic SSH Commands

Simple Remote Execution

# Execute single command on remote server
plink user@example.com ls -la /home/user

# Execute command with password (not recommended)
plink -pw password user@example.com whoami

# Execute multiple commands
plink user@example.com "cd /tmp && pwd && ls -la"

# Get command exit code
plink user@example.com "test -f /etc/shadow && echo 'exists' || echo 'not found'"
echo "Exit code: $?"

Interactive vs Non-Interactive

# Non-interactive (returns immediately after command)
plink -batch user@example.com "grep pattern /var/log/syslog"

# Suppress banner and prompts
plink -batch -v0 user@example.com "uptime"

# Disable SSH protocol warnings
plink -sshver 2 user@example.com "id"

Connection Options

# Specify SSH port
plink -P 2222 user@example.com "uname -a"

# Specify private key file
plink -i ~/.ssh/id_rsa user@example.com "hostname"

# Use stored PuTTY session
plink "My Server" "date"

# Connection timeout (seconds)
plink -connectionattempts 3 user@example.com "echo connected"

# Verbosity levels (0=silent, 1=fatal, 2=error, 3=warning, 4=info)
plink -v2 user@example.com "uptime"

Key-Based Authentication

Using SSH Keys

# Convert OpenSSH key to PuTTY format
puttygen id_rsa -o -O private-sshcom-3des-cbc -o private.ppk

# Use PuTTY private key (.ppk)
plink -i C:\Users\user\.ssh\private.ppk user@example.com "pwd"

# Auto-add key to agent
plink -A user@example.com "ssh internal-server 'uname -a'"

# Load key from ssh-agent
plink -agent user@example.com "whoami"

SSH Agent Setup

# Start Pageant (PuTTY SSH agent) with key
pageant.exe C:\Users\user\.ssh\private.ppk

# Use agent-forwarded connection
plink -A user@example.com "ssh internal-host 'id'"

# Verify agent has keys
plink -agent user@example.com "ssh-add -l"

Port Forwarding and Tunneling

Local Port Forwarding

# Forward local port to remote service
plink -L 3306:localhost:3306 user@example.com -N

# Forward localhost:5432 to database server through bastion
plink -L 5432:db.internal:5432 bastion.example.com -N

# Multiple forwards in one connection
plink -L 8080:webserver:80 -L 3306:dbserver:3306 user@example.com -N

# Bind to specific interface
plink -L 127.0.0.1:9200:elasticsearch:9200 user@example.com -N

# Keep connection open indefinitely
plink -N -once user@example.com cat

Remote Port Forwarding

# Expose local port to remote host
plink -R 8080:localhost:3000 user@example.com -N

# Allow remote connections to forwarded port
plink -R 0.0.0.0:8080:localhost:3000 user@example.com -N

# Multiple remote forwards
plink -R 8080:localhost:3000 -R 9000:localhost:5000 user@example.com -N

Dynamic Port Forwarding (SOCKS Proxy)

# Create SOCKS proxy through SSH
plink -D 1080 user@example.com -N

# Use with curl via SOCKS proxy
curl -x socks5://localhost:1080 http://internal.example.com

# Use with Firefox or other apps (configure proxy to localhost:1080)

Batch and Script Automation

Simple Script Execution

#!/bin/bash
# Execute commands on multiple servers

SERVERS=(
    "server1.example.com"
    "server2.example.com"
    "server3.example.com"
)
KEY="~/.ssh/id_rsa"
USER="admin"

for server in "${SERVERS[@]}"; do
    echo "=== $server ==="
    plink -i "$KEY" -P 22 "$USER@$server" "uptime"
done

Remote Script Execution

#!/bin/bash
# Run local script on remote server

plink user@example.com bash < local_script.sh

# With arguments
plink user@example.com "bash -s argument1 argument2" < local_script.sh

# Run script from stdin
echo '#!/bin/bash
echo "Hostname: $(hostname)"
echo "Uptime: $(uptime)"
free -h' | plink user@example.com /bin/bash

Piping Between Commands

#!/bin/bash
# Complex piping through SSH

plink user@example.com "cat /var/log/auth.log | grep 'Failed' | wc -l"

# Compress and transfer
tar czf - /important/data | plink user@example.com "cat > /tmp/backup.tar.gz"

# Stream processing
plink user@example.com "tail -f /var/log/syslog" | grep "ERROR"

Conditional Execution

#!/bin/bash
# Exit status handling

if plink -batch user@example.com "test -f /etc/config"; then
    echo "Config file exists"
    plink user@example.com "cat /etc/config"
else
    echo "Config file not found"
    plink user@example.com "ls -la /etc/"
fi

# Check exit code
plink user@example.com "exit 42"
EXIT_CODE=$?
echo "Remote exit code: $EXIT_CODE"

Advanced Tunneling Scenarios

Database Access Through Bastion

#!/bin/bash
# Create tunnel to MySQL through bastion host

BASTION="bastion.example.com"
DB_HOST="db.internal"
DB_PORT="3306"
LOCAL_PORT="3306"
USER="sysadmin"

# Create port forward in background
plink -i ~/.ssh/bastion.ppk \
    -L $LOCAL_PORT:$DB_HOST:$DB_PORT \
    $USER@$BASTION -N &

TUNNEL_PID=$!
sleep 2

# Connect to database through tunnel
mysql -h 127.0.0.1 -P $LOCAL_PORT -u dbuser -p

# Clean up tunnel
kill $TUNNEL_PID

Multi-Hop Tunneling

#!/bin/bash
# Chain tunnels: Local -> Jump Host -> Internal Server -> Database

JUMP="jump.example.com"
INTERNAL="internal.example.com"
DB="database.internal"

# Create tunnel to internal server through jump host
plink -i ~/.ssh/id_rsa \
    -L 2222:$INTERNAL:22 \
    user@$JUMP -N &

sleep 1

# Create tunnel to database through internal server
plink -i ~/.ssh/id_rsa \
    -P 2222 \
    -L 3306:$DB:3306 \
    user@localhost -N &

sleep 1

# Now connect to database at localhost:3306
mysql -h 127.0.0.1

Reverse Shell Tunnel

#!/bin/bash
# Create reverse tunnel for accessing local service from remote

LOCAL_PORT="8000"
REMOTE_PORT="9000"
REMOTE_HOST="user@example.com"

plink -i ~/.ssh/id_rsa \
    -R $REMOTE_PORT:localhost:$LOCAL_PORT \
    $REMOTE_HOST -N

# Remote user can access: localhost:9000 -> local:8000

File Transfer Integration

# While plink doesn't do SCP, use pscp (PuTTY SCP)
# Download file from remote
pscp -i ~/.ssh/id_rsa user@example.com:/tmp/file.txt .

# Upload file to remote
pscp -i ~/.ssh/id_rsa file.txt user@example.com:/tmp/

# Recursive directory copy
pscp -i ~/.ssh/id_rsa -r user@example.com:/home/user/data .

Sync Through Tunnel

#!/bin/bash
# Create rsync tunnel and sync files

HOST="backup.example.com"
USER="backupuser"
REMOTE_PATH="/backup/data"
LOCAL_PATH="./local_backup"

# Create tunnel
plink -i ~/.ssh/id_rsa \
    -L 2222:localhost:22 \
    user@$HOST -N &

TUNNEL_PID=$!
sleep 1

# Rsync through tunnel
rsync -av -e "ssh -p 2222" $USER@localhost:$REMOTE_PATH $LOCAL_PATH

kill $TUNNEL_PID

Scripting Examples

System Monitoring Script

#!/bin/bash
# Monitor multiple servers via plink

SERVERS="server1 server2 server3"
KEY="~/.ssh/id_rsa"
USER="monitor"

for server in $SERVERS; do
    echo "=== $server at $(date) ==="

    plink -i "$KEY" -batch "$USER@$server" << 'EOF'
echo "CPU: $(grep 'cpu ' /proc/stat | head -1)"
echo "Memory: $(free -h | grep '^Mem')"
echo "Disk: $(df -h / | tail -1)"
echo "Load: $(uptime | awk -F 'load average:' '{print $NF}')"
EOF

    echo ""
done

Backup Automation

#!/bin/bash
# Automated backup script using plink tunnels

BACKUP_SERVER="backup.example.com"
BACKUP_USER="backupuser"
DB_PORT="3306"
DB_USER="dbuser"
DB_PASS="dbpass"
DB_NAME="production"
BACKUP_DIR="/backups"

# Create tunnel to database
plink -i ~/.ssh/id_rsa \
    -L 3306:db.internal:3306 \
    $BACKUP_USER@$BACKUP_SERVER -N &

TUNNEL_PID=$!
sleep 2

# Create backup through tunnel
mysqldump -h 127.0.0.1 \
    -u $DB_USER -p$DB_PASS \
    $DB_NAME | gzip > ${BACKUP_DIR}/db_$(date +%Y%m%d).sql.gz

# Upload backup to server
pscp -i ~/.ssh/id_rsa \
    ${BACKUP_DIR}/db_$(date +%Y%m%d).sql.gz \
    $BACKUP_USER@$BACKUP_SERVER:$BACKUP_DIR/

kill $TUNNEL_PID

Cron Job Example

#!/bin/bash
# Plink in cron: fetch remote logs and analyze

USER="sysadmin"
HOST="production.example.com"
KEY="/home/monitor/.ssh/id_rsa"

# Run maintenance task daily at 2 AM
# 0 2 * * * /usr/local/bin/remote_maintenance.sh

plink -i "$KEY" -batch "$USER@$HOST" << 'EOF'
# Clean old logs
find /var/log -mtime +30 -delete

# Archive system logs
tar czf /backups/syslog_$(date +\%Y\%m\%d).tar.gz /var/log/syslog*

# Report disk usage
echo "Disk Usage Report: $(date)" >> /var/log/maintenance.log
df -h >> /var/log/maintenance.log
EOF

Troubleshooting

Common Issues

Issue: “Access denied” or “Disconnected: No supported authentication methods”

# Verify key permissions (should be 600)
ls -la ~/.ssh/id_rsa

# Test SSH connection with verbose output
plink -v user@example.com "id"

# Check if key is loaded in Pageant
pageant -l

# Try password authentication
plink -pw password user@example.com "whoami"

Issue: “Server unexpectedly closed network connection”

# Use batch mode to suppress interactive prompts
plink -batch user@example.com "uptime"

# Increase connection timeout
plink -connectionattempts 5 user@example.com "uptime"

# Check SSH server logs on remote host
ssh user@example.com "tail -f /var/log/auth.log"

Issue: Port forwarding not working

# Verify tunnel is listening
netstat -tlnp | grep 3306

# Test connectivity to tunnel
telnet localhost 3306

# Check remote port is accessible
plink user@example.com "netstat -tlnp | grep 3306"

# Keep tunnel open with -N flag
plink -N user@example.com -L 3306:localhost:3306

Issue: “putty not found” on Linux/macOS

# PuTTY tools are in putty-tools package
sudo apt-get install putty-tools

# Verify plink is in PATH
which plink
plink --version

Issue: Timeout on slow connections

# Increase timeout value (milliseconds)
plink -sessiontimeout 60 user@example.com "slow command"

# Keep connection alive with keepalive
plink -sshver 2 user@example.com -N

# Use with longer-running operations
plink user@example.com "timeout 300 long_running_job"

Security Best Practices

  • Convert OpenSSH keys to PuTTY format (.ppk) with puttygen
  • Use -batch flag to prevent interactive prompts in scripts
  • Store private keys securely with restricted permissions (600)
  • Use Pageant (SSH agent) to manage multiple keys
  • Avoid embedding passwords; use key-based authentication
  • Use -sshver 2 to force SSH2 protocol (more secure)
  • Enable verbose logging (-v4) to debug connection issues
  • Implement proper timeout and retry logic in scripts
  • Restrict SSH access at firewall and server level
  • Regularly update PuTTY to latest version

Performance Tips

  • Use -batch mode for non-interactive execution (faster)
  • Reuse SSH connections with connection pooling
  • Implement connection caching for multiple commands
  • Use -N flag for tunnels (reduces overhead)
  • Minimize command verbosity to reduce traffic
  • Test connection speed with -connectionattempts
  • Use parallel execution for multiple servers
  • Implement exponential backoff for retries

Integration Examples

With cron for scheduled tasks

# /etc/cron.d/plink_tasks
0 3 * * * root plink -i /root/.ssh/id_rsa -batch user@server "backup_command" >> /var/log/plink_backup.log 2>&1

With systemd for tunneling service

[Unit]
Description=SSH Tunnel to Database
After=network.target

[Service]
Type=simple
User=app
ExecStart=/usr/bin/plink -N -i /home/app/.ssh/id_rsa -L 3306:db:3306 user@bastion.example.com
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
  • OpenSSH - Native SSH client (Linux/macOS alternative)
  • WinSCP - GUI SFTP/SCP client with scripting
  • Putty - Full PuTTY suite with GUI client
  • SSH Key Management - Key generation and conversion

Last updated: 2026-03-30