Basic connection
Basic connection
With specific key
With specific key
ssh -i ~/.ssh/id_rsa user@host
ssh -i ~/.ssh/id_rsa user@host
With specific port
With specific port
Execute single command
Execute single command
Execute multiple commands
Execute multiple commands
ssh user@host << 'EOF'
cd /app
ls -la
cat config.yaml
EOF
ssh user@host << 'EOF'
cd /app
ls -la
cat config.yaml
EOF
Through Jump Host / Bastion
通过跳转主机/堡垒机
ProxyJump (OpenSSH 7.3+) - preferred
ProxyJump (OpenSSH 7.3+) - preferred
ssh -J jumpuser@bastion user@target
ssh -J jumpuser@bastion user@target
Multiple jumps
Multiple jumps
ssh -J jump1@bastion1,jump2@bastion2 user@target
ssh -J jump1@bastion1,jump2@bastion2 user@target
Legacy ProxyCommand
Legacy ProxyCommand
ssh -o ProxyCommand="ssh -W %h:%p jumpuser@bastion" user@target
ssh -o ProxyCommand="ssh -W %h:%p jumpuser@bastion" user@target
Execute command through jump
Execute command through jump
ssh -J jumpuser@bastion user@target "kubectl get pods"
ssh -J jumpuser@bastion user@target "kubectl get pods"
SSH Config for Persistent Setup
配置SSH Config实现持久化连接
~/.ssh/config
~/.ssh/config
Host bastion
HostName bastion.example.com
User jumpuser
IdentityFile ~/.ssh/bastion_key
Host target
HostName 10.0.1.50
User admin
ProxyJump bastion
IdentityFile ~/.ssh/target_key
Host k8s-*
User ubuntu
ProxyJump bastion
IdentityFile ~/.ssh/k8s_key
Host k8s-prod
HostName 10.0.1.100
Host k8s-staging
HostName 10.0.2.100
Then simply: `ssh target` or `ssh k8s-prod "kubectl get pods"`
Host bastion
HostName bastion.example.com
User jumpuser
IdentityFile ~/.ssh/bastion_key
Host target
HostName 10.0.1.50
User admin
ProxyJump bastion
IdentityFile ~/.ssh/target_key
Host k8s-*
User ubuntu
ProxyJump bastion
IdentityFile ~/.ssh/k8s_key
Host k8s-prod
HostName 10.0.1.100
Host k8s-staging
HostName 10.0.2.100
然后只需执行:`ssh target` 或 `ssh k8s-prod "kubectl get pods"`
Port Forwarding / Tunneling
端口转发/隧道
Local Port Forward (access remote service locally)
本地端口转发(在本地访问远程服务)
Forward local:8080 → remote:80
Forward local:8080 → remote:80
ssh -L 8080:localhost:80 user@host
ssh -L 8080:localhost:80 user@host
Forward to service behind remote host
Forward to service behind remote host
ssh -L 8080:internal-service:80 user@bastion
ssh -L 8080:internal-service:80 user@bastion
Kubernetes API access through tunnel
Kubernetes API access through tunnel
ssh -L 6443:kubernetes.default:443 user@bastion
ssh -L 6443:kubernetes.default:443 user@bastion
Database access
Database access
ssh -L 5432:db.internal:5432 user@bastion
ssh -L 5432:db.internal:5432 user@bastion
Then: psql -h localhost -p 5432 -U dbuser mydb
Then: psql -h localhost -p 5432 -U dbuser mydb
Multiple forwards
Multiple forwards
ssh -L 8080:web:80 -L 5432:db:5432 -L 6379:redis:6379 user@bastion
ssh -L 8080:web:80 -L 5432:db:5432 -L 6379:redis:6379 user@bastion
Remote Port Forward (expose local service remotely)
远程端口转发(向远程暴露本地服务)
Expose local:3000 on remote:8080
Expose local:3000 on remote:8080
ssh -R 8080:localhost:3000 user@host
ssh -R 8080:localhost:3000 user@host
Expose to all interfaces on remote (requires GatewayPorts yes)
Expose to all interfaces on remote (requires GatewayPorts yes)
ssh -R 0.0.0.0:8080:localhost:3000 user@host
ssh -R 0.0.0.0:8080:localhost:3000 user@host
Dynamic SOCKS Proxy
动态SOCKS代理
Create SOCKS5 proxy on local:1080
Create SOCKS5 proxy on local:1080
Use with curl
Use with curl
Use with kubectl (via proxychains or similar)
Use with kubectl (via proxychains or similar)
HTTPS_PROXY=socks5://localhost:1080 kubectl get pods
HTTPS_PROXY=socks5://localhost:1080 kubectl get pods
Tunnel in Background
后台运行隧道
Background tunnel with connection keep-alive
Background tunnel with connection keep-alive
ssh -f -N -L 8080:service:80 user@host
ssh -f -N -L 8080:service:80 user@host
With autossh for auto-reconnect (install: brew install autossh)
With autossh for auto-reconnect (install: brew install autossh)
autossh -M 0 -f -N -L 8080:service:80 user@host
-o "ServerAliveInterval 30"
-o "ServerAliveCountMax 3"
autossh -M 0 -f -N -L 8080:service:80 user@host
-o "ServerAliveInterval 30"
-o "ServerAliveCountMax 3"
Kill background tunnel
Kill background tunnel
Or find and kill specific tunnel
Or find and kill specific tunnel
ps aux | grep "ssh.*-L" | grep -v grep
kill <pid>
ps aux | grep "ssh.*-L" | grep -v grep
kill <pid>
Remote Kubernetes Access
远程Kubernetes访问
Through SSH Tunnel
通过SSH隧道访问
Setup: Tunnel to k8s API
Setup: Tunnel to k8s API
ssh -L 6443:kubernetes.default.svc:443 user@bastion -N &
TUNNEL_PID=$!
ssh -L 6443:kubernetes.default.svc:443 user@bastion -N &
TUNNEL_PID=$!
Configure kubectl for tunnel
Configure kubectl for tunnel
kubectl config set-cluster tunnel-cluster
--server=
https://localhost:6443
--insecure-skip-tls-verify=true
kubectl config set-context tunnel-context
--cluster=tunnel-cluster
--user=admin
kubectl config use-context tunnel-context
kubectl config set-cluster tunnel-cluster
--server=
https://localhost:6443
--insecure-skip-tls-verify=true
kubectl config set-context tunnel-context
--cluster=tunnel-cluster
--user=admin
kubectl config use-context tunnel-context
Use kubectl normally
Use kubectl normally
Execute kubectl Remotely
远程执行kubectl命令
Single command
Single command
ssh user@k8s-master "kubectl get pods -n production"
ssh user@k8s-master "kubectl get pods -n production"
ssh user@k8s-master "kubectl --context=prod get pods"
ssh user@k8s-master "kubectl --context=prod get pods"
Watch (needs pseudo-terminal)
Watch (needs pseudo-terminal)
ssh -t user@k8s-master "kubectl get pods -w"
ssh -t user@k8s-master "kubectl get pods -w"
ssh user@k8s-master "kubectl logs -l app=myapp --tail=100"
ssh user@k8s-master "kubectl logs -l app=myapp --tail=100"
Multiple commands
Multiple commands
ssh user@k8s-master << 'EOF'
kubectl get pods -n production
kubectl get events -n production --sort-by='.lastTimestamp' | tail -20
kubectl top pods -n production
EOF
ssh user@k8s-master << 'EOF'
kubectl get pods -n production
kubectl get events -n production --sort-by='.lastTimestamp' | tail -20
kubectl top pods -n production
EOF
Interactive k9s Remotely
远程交互式使用k9s
Run k9s interactively (requires -t for TTY)
Run k9s interactively (requires -t for TTY)
ssh -t user@k8s-master "k9s"
ssh -t user@k8s-master "k9s"
In specific namespace
In specific namespace
ssh -t user@k8s-master "k9s -n production"
ssh -t user@k8s-master "k9s -n production"
Read-only mode
Read-only mode
ssh -t user@k8s-master "k9s --readonly"
ssh -t user@k8s-master "k9s --readonly"
With specific context
With specific context
ssh -t user@k8s-master "k9s --context production"
ssh -t user@k8s-master "k9s --context production"
Remote Script Execution
远程脚本执行
Run Local Script Remotely
在远程主机运行本地脚本
Execute local script on remote
Execute local script on remote
ssh user@host 'bash -s' < local-script.sh
ssh user@host 'bash -s' < local-script.sh
With arguments
With arguments
ssh user@host 'bash -s' < local-script.sh arg1 arg2
ssh user@host 'bash -s' < local-script.sh arg1 arg2
Inline script
Inline script
ssh user@host << 'SCRIPT'
#!/bin/bash
set -euo pipefail
echo "Running on $(hostname)"
kubectl get pods -n production
SCRIPT
ssh user@host << 'SCRIPT'
#!/bin/bash
set -euo pipefail
echo "Running on $(hostname)"
kubectl get pods -n production
SCRIPT
Deploy and Execute
部署并执行脚本
scp deploy.sh user@host:/tmp/
ssh user@host "chmod +x /tmp/deploy.sh && /tmp/deploy.sh"
scp deploy.sh user@host:/tmp/
ssh user@host "chmod +x /tmp/deploy.sh && /tmp/deploy.sh"
Or one-liner with heredoc
Or one-liner with heredoc
ssh user@host << 'EOF'
cat > /tmp/check.sh << 'INNER'
#!/bin/bash
kubectl get pods -A | grep -v Running
kubectl get events -A --field-selector type=Warning | tail -20
INNER
chmod +x /tmp/check.sh
/tmp/check.sh
EOF
ssh user@host << 'EOF'
cat > /tmp/check.sh << 'INNER'
#!/bin/bash
kubectl get pods -A | grep -v Running
kubectl get events -A --field-selector type=Warning | tail -20
INNER
chmod +x /tmp/check.sh
/tmp/check.sh
EOF
Copy to remote
Copy to remote
scp file.yaml user@host:/path/
scp file.yaml user@host:/path/
Copy from remote
Copy from remote
scp user@host:/path/file.yaml ./
scp user@host:/path/file.yaml ./
Through jump host
Through jump host
scp -J jumpuser@bastion file.yaml user@target:/path/
scp -J jumpuser@bastion file.yaml user@target:/path/
Recursive directory
Recursive directory
scp -r ./manifests user@host:/deploy/
scp -r ./manifests user@host:/deploy/
With specific key
With specific key
scp -i ~/.ssh/mykey file.yaml user@host:/path/
scp -i ~/.ssh/mykey file.yaml user@host:/path/
Rsync over SSH
基于SSH的Rsync同步
Sync directory
Sync directory
rsync -avz -e ssh ./local/ user@host:/remote/
rsync -avz -e ssh ./local/ user@host:/remote/
Through jump host
Through jump host
rsync -avz -e "ssh -J jumpuser@bastion" ./local/ user@target:/remote/
rsync -avz -e "ssh -J jumpuser@bastion" ./local/ user@target:/remote/
Dry run first
Dry run first
rsync -avzn -e ssh ./local/ user@host:/remote/
rsync -avzn -e ssh ./local/ user@host:/remote/
Delete files on remote not in local
Delete files on remote not in local
rsync -avz --delete -e ssh ./local/ user@host:/remote/
rsync -avz --delete -e ssh ./local/ user@host:/remote/
Connection Management
连接管理
Keep Connection Alive
保持连接存活
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@host
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@host
In ~/.ssh/config (global)
In ~/.ssh/config (global)
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
Connection Multiplexing (reuse connections)
连接复用(复用已有连接)
~/.ssh/config
~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
Create socket directory
Create socket directory
mkdir -p ~/.ssh/sockets
First connection creates socket, subsequent connections reuse it (faster).
mkdir -p ~/.ssh/sockets
首次连接会创建套接字,后续连接会复用该套接字(连接速度更快)。
Test SSH connectivity
Test SSH connectivity
ssh -o ConnectTimeout=5 -o BatchMode=yes user@host echo "OK" 2>/dev/null && echo "Connected" || echo "Failed"
ssh -o ConnectTimeout=5 -o BatchMode=yes user@host echo "OK" 2>/dev/null && echo "Connected" || echo "Failed"
Verbose connection debugging
Verbose connection debugging
Test through bastion
Test through bastion
ssh -J jumpuser@bastion -o ConnectTimeout=10 user@target echo "OK"
ssh -J jumpuser@bastion -o ConnectTimeout=10 user@target echo "OK"
tunnel.sh - Manage SSH tunnels
tunnel.sh - Manage SSH tunnels
ACTION="${1:-status}"
TUNNEL_NAME="${2:-default}"
BASTION="user@bastion.example.com"
PIDFILE="/tmp/ssh-tunnel-${TUNNEL_NAME}.pid"
case "$ACTION" in
start)
if [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
echo "Tunnel already running (PID: $(cat "$PIDFILE"))"
exit 0
fi
case "$TUNNEL_NAME" in
k8s)
ssh -f -N -L 6443:kubernetes:443 "$BASTION" \
-o ServerAliveInterval=30 \
-o ExitOnForwardFailure=yes
;;
db)
ssh -f -N -L 5432:postgres.internal:5432 "$BASTION" \
-o ServerAliveInterval=30 \
-o ExitOnForwardFailure=yes
;;
*)
echo "Unknown tunnel: $TUNNEL_NAME"
exit 1
;;
esac
pgrep -f "ssh.*-L.*$TUNNEL_NAME" > "$PIDFILE"
echo "Tunnel $TUNNEL_NAME started (PID: $(cat "$PIDFILE"))"
;;
stop)
if [[ -f "$PIDFILE" ]]; then
kill "$(cat "$PIDFILE")" 2>/dev/null
rm "$PIDFILE"
echo "Tunnel $TUNNEL_NAME stopped"
else
pkill -f "ssh.*-L.*$TUNNEL_NAME"
echo "Tunnel $TUNNEL_NAME stopped (by pattern)"
fi
;;
status)
if [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
echo "Tunnel $TUNNEL_NAME: running (PID: $(cat "$PIDFILE"))"
else
echo "Tunnel $TUNNEL_NAME: not running"
fi
;;
*)
echo "Usage: $0 {start|stop|status} [tunnel-name]"
echo "Tunnels: k8s, db"
;;
esac
ACTION="${1:-status}"
TUNNEL_NAME="${2:-default}"
BASTION="user@bastion.example.com"
PIDFILE="/tmp/ssh-tunnel-${TUNNEL_NAME}.pid"
case "$ACTION" in
start)
if [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
echo "Tunnel already running (PID: $(cat "$PIDFILE"))"
exit 0
fi
case "$TUNNEL_NAME" in
k8s)
ssh -f -N -L 6443:kubernetes:443 "$BASTION" \
-o ServerAliveInterval=30 \
-o ExitOnForwardFailure=yes
;;
db)
ssh -f -N -L 5432:postgres.internal:5432 "$BASTION" \
-o ServerAliveInterval=30 \
-o ExitOnForwardFailure=yes
;;
*)
echo "Unknown tunnel: $TUNNEL_NAME"
exit 1
;;
esac
pgrep -f "ssh.*-L.*$TUNNEL_NAME" > "$PIDFILE"
echo "Tunnel $TUNNEL_NAME started (PID: $(cat "$PIDFILE"))"
;;
stop)
if [[ -f "$PIDFILE" ]]; then
kill "$(cat "$PIDFILE")" 2>/dev/null
rm "$PIDFILE"
echo "Tunnel $TUNNEL_NAME stopped"
else
pkill -f "ssh.*-L.*$TUNNEL_NAME"
echo "Tunnel $TUNNEL_NAME stopped (by pattern)"
fi
;;
status)
if [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
echo "Tunnel $TUNNEL_NAME: running (PID: $(cat "$PIDFILE"))"
else
echo "Tunnel $TUNNEL_NAME: not running"
fi
;;
*)
echo "Usage: $0 {start|stop|status} [tunnel-name]"
echo "Tunnels: k8s, db"
;;
esac
Remote Health Check
远程健康检查
remote-k8s-check.sh - Check k8s cluster health via SSH
remote-k8s-check.sh - Check k8s cluster health via SSH
HOST="${1:?Usage: $0 <ssh-host> [namespace]}"
NS="${2:-default}"
echo "=== Checking $HOST ($NS namespace) ==="
ssh "$HOST" << EOF
echo "--- Pod Status ---"
kubectl get pods -n $NS -o wide
echo ""
echo "--- Non-Running Pods ---"
kubectl get pods -n $NS | grep -v Running | grep -v Completed | grep -v NAME
echo ""
echo "--- Recent Events ---"
kubectl get events -n $NS --sort-by='.lastTimestamp' | tail -15
echo ""
echo "--- Resource Usage ---"
kubectl top pods -n $NS 2>/dev/null || echo "Metrics unavailable"
EOF
HOST="${1:?Usage: $0 <ssh-host> [namespace]}"
NS="${2:-default}"
echo "=== Checking $HOST ($NS namespace) ==="
ssh "$HOST" << EOF
echo "--- Pod Status ---"
kubectl get pods -n $NS -o wide
echo ""
echo "--- Non-Running Pods ---"
kubectl get pods -n $NS | grep -v Running | grep -v Completed | grep -v NAME
echo ""
echo "--- Recent Events ---"
kubectl get events -n $NS --sort-by='.lastTimestamp' | tail -15
echo ""
echo "--- Resource Usage ---"
kubectl top pods -n $NS 2>/dev/null || echo "Metrics unavailable"
EOF
Batch Remote Execution
批量远程执行
run-on-hosts.sh - Run command on multiple hosts
run-on-hosts.sh - Run command on multiple hosts
HOSTS=("k8s-prod" "k8s-staging" "k8s-dev")
CMD="${1:?Usage: $0 'command'}"
for host in "${HOSTS[@]}"; do
echo "=== $host ==="
ssh -o ConnectTimeout=10 "$host" "$CMD" 2>&1 || echo "FAILED: $host"
echo ""
done
HOSTS=("k8s-prod" "k8s-staging" "k8s-dev")
CMD="${1:?Usage: $0 'command'}"
for host in "${HOSTS[@]}"; do
echo "=== $host ==="
ssh -o ConnectTimeout=10 "$host" "$CMD" 2>&1 || echo "FAILED: $host"
echo ""
done
Security Best Practices
安全最佳实践
Generate ed25519 key (preferred)
Generate ed25519 key (preferred)
ssh-keygen -t ed25519 -C "description" -f ~/.ssh/mykey
ssh-keygen -t ed25519 -C "description" -f ~/.ssh/mykey
Copy public key to server
Copy public key to server
ssh-copy-id -i ~/.ssh/mykey.pub user@host
ssh-copy-id -i ~/.ssh/mykey.pub user@host
Restrict key permissions
Restrict key permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/*.pub
chmod 600 ~/.ssh/config
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/*.pub
chmod 600 ~/.ssh/config
Agent Forwarding (careful!)
代理转发(需谨慎!)
Enable agent forwarding (only to trusted hosts)
Enable agent forwarding (only to trusted hosts)
Then on bastion, can SSH to other hosts using local keys
Then on bastion, can SSH to other hosts using local keys
Safer: ProxyJump instead of agent forwarding
Safer: ProxyJump instead of agent forwarding
ssh -J bastion target # Keys never leave your machine
ssh -J bastion target # Keys never leave your machine
Restrict Commands per Key
按密钥限制可执行命令
command="kubectl get pods",no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAA... readonly-key
command="kubectl get pods",no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAA... readonly-key
Verbose output
Verbose output
Check key being offered
Check key being offered
ssh -v user@host 2>&1 | grep "Offering"
ssh -v user@host 2>&1 | grep "Offering"
Test specific key
Test specific key
ssh -i ~/.ssh/specific_key -v user@host
ssh -i ~/.ssh/specific_key -v user@host
Check server allows key auth
Check server allows key auth
ssh -o PreferredAuthentications=publickey user@host
ssh -o PreferredAuthentications=publickey user@host
Check if tunnel port is listening
Check if tunnel port is listening
lsof -i :8080
netstat -an | grep 8080
lsof -i :8080
netstat -an | grep 8080
Test tunnel connectivity
Test tunnel connectivity
ssh -v -L 8080:target:80 user@bastion
ssh -v -L 8080:target:80 user@bastion
Common causes:
- Wrong key permissions:
- Wrong .ssh dir permissions:
- Key not in agent:
- Server doesn't have public key:
- SELinux/firewall blocking
常见原因:
- 密钥权限错误:
- .ssh目录权限错误:
- 密钥未添加到代理:
- 服务器未添加公钥:
- SELinux/防火墙拦截