1.通过跳板机建立加密隧道(SSH 端口转发)
- 本地端口转发(适用于前置机访问数据库)
在前置机上通过 SSH 连接跳板机,将本地端口(如本地 3307)转发到数据库端口(如远程 3306)。
命令示例:
ssh-L 本地端口:数据库IP:数据库端口 跳板机用户@跳板机IP
#例:ssh -L 3307:192.168.1.100:3306 user@jump-host.com
之后,前置机可通过1ocalhost:3307访问数据库,数据经 SSH 隧道加密传输。
- 动态端口转发(适用于多端口或代理场景)
ssh-D 本地代理端口 跳板机用户@跳板机IP
配合代理工具(如 Proxychains),可让前置机的应用通过跳板机访问数据库及其他服务。
2.实战案例:
2.1使用密钥文件进行跳转
数据库需要走ssh隧道:
业务中台中间件
mysql
101.10.20.9:336
# 后台运行可以这样写,弊端是会自动挂,只能临时使用
nohup ssh -N -L 192.168.25.202:3307:101.10.20.9:336 root@192.152.17.181 -i /root/.ssh/private-key &
为解决这个问题编写脚本:ssh_tunnel_monitor.sh
此脚本可监控ssh隧道状态,若ssh隧道关闭,可以自动重启。
内容如下:
#!/bin/bash
# SSH隧道监控和自动重启脚本(优化版)
# 功能:自动监控隧道状态,断开时强制终止旧进程并重启,避免进程堆积
# --------------------- 配置区域 --------------------- #
LOG_FILE="/root/logs/ssh_tunnel_monitor/ssh_tunnel_monitor.log" # 日志文件路径
PRIVATE_KEY="/root/.ssh/private-key" # 私钥路径
SSH_USER="root" # SSH用户名
SSH_SERVER="192.152.17.181" # SSH服务器IP
LOCAL_IP="192.168.25.202" # 本地绑定IP
# 隧道配置:格式为 "本地端口:目标主机:目标端口 描述"
TUNNELS=(
"3307:101.10.20.9:336 MySQL数据库隧道"
)
# --------------------- 工具函数 --------------------- #
# 日志记录函数
log() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local message="$timestamp [$(basename "$0")] $1"
echo "$message"
echo "$message" >> "$LOG_FILE"
}
# 检查依赖工具
check_dependencies() {
local required_commands=("ssh" "pgrep" "netstat" "kill")
for cmd in "${required_commands[@]}"; do
if ! command -v "$cmd" &> /dev/null; then
log "错误: 缺少依赖工具 '$cmd'"
exit 1
fi
done
}
# 强制终止旧隧道进程
kill_old_tunnel() {
local port=$1
local process_pattern="ssh -N -L $LOCAL_IP:$port:"
local pids=$(pgrep -f "$process_pattern")
if [[ -n "$pids" ]]; then
log "发现旧隧道进程 ($port): $pids,正在强制终止..."
kill -9 $pids &> /dev/null
sleep 1 # 等待进程终止
fi
}
# 启动隧道(带旧进程清理)
start_tunnel() {
local tunnel_info=$1
local description=$2
local port=$(echo "$tunnel_info" | cut -d: -f1)
local remote_host=$(echo "$tunnel_info" | cut -d: -f2)
local remote_port=$(echo "$tunnel_info" | cut -d: -f3)
# 清理旧进程
kill_old_tunnel "$port"
# 启动新隧道
log "启动隧道: $description ($LOCAL_IP:$port -> $remote_host:$remote_port)"
nohup ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 \
-N -L "$LOCAL_IP:$port:$remote_host:$remote_port" \
"$SSH_USER@$SSH_SERVER" -i "$PRIVATE_KEY" >/dev/null 2>&1 &
# 检查启动结果
sleep 3
local new_pid=$(pgrep -f "ssh -N -L $LOCAL_IP:$port:" | head -n1)
if [[ -n "$new_pid" ]]; then
log "隧道启动成功,PID: $new_pid"
else
log "错误: 隧道 $description 启动失败"
fi
}
# 检查隧道状态(通过端口监听判断)
is_tunnel_alive() {
local port=$1
netstat -tulpn | grep -q ":$port "
return $?
}
# --------------------- 核心逻辑 --------------------- #
# 监控主循环
monitor_loop() {
log "------------------------- 监控循环开始 -------------------------"
for tunnel in "${TUNNELS[@]}"; do
local tunnel_info=$(echo "$tunnel" | awk '{print $1}')
local description=$(echo "$tunnel" | awk '{$1=""; print substr($0, 2)}')
local port=$(echo "$tunnel_info" | cut -d: -f1)
if ! is_tunnel_alive "$port"; then
log "警告: 隧道 $description 断开,触发重启..."
start_tunnel "$tunnel_info" "$description"
else
local pid=$(pgrep -f "ssh -N -L $LOCAL_IP:$port:" | head -n1)
log "隧道 $description 运行正常,PID: $pid"
fi
done
log "------------------------- 监控循环结束 -------------------------\n"
sleep 30 # 检查间隔(秒)
}
# --------------------- 命令行接口 --------------------- #
main() {
check_dependencies
# 创建日志文件(若不存在)
if [[ ! -f "$LOG_FILE" ]]; then
touch "$LOG_FILE"
chmod 600 "$LOG_FILE"
log "创建日志文件: $LOG_FILE"
fi
case "$1" in
start)
if pgrep -f "$0 monitor" &> /dev/null; then
log "错误: 监控进程已在运行"
exit 1
fi
# 初始化清理所有旧隧道
log "初始化: 清理所有旧隧道进程..."
for tunnel in "${TUNNELS[@]}"; do
kill_old_tunnel $(echo "$tunnel" | cut -d: -f1)
done
# 启动监控守护进程
nohup "$0" monitor >/dev/null 2>&1 &
log "监控守护进程启动,PID: $!"
;;
monitor)
log "监控进程启动,开始循环检查隧道状态..."
while true; do
monitor_loop
done
;;
stop)
log "停止所有隧道和监控进程..."
# 终止隧道进程
for tunnel in "${TUNNELS[@]}"; do
local port=$(echo "$tunnel" | cut -d: -f1)
kill_old_tunnel "$port"
done
# 终止监控进程
local monitor_pid=$(pgrep -f "$0 monitor")
if [[ -n "$monitor_pid" ]]; then
kill "$monitor_pid"
log "监控进程已终止,PID: $monitor_pid"
else
log "监控进程未运行"
fi
;;
status)
log "------------------------- 隧道状态 -------------------------"
for tunnel in "${TUNNELS[@]}"; do
local tunnel_info=$(echo "$tunnel" | awk '{print $1}')
local description=$(echo "$tunnel" | awk '{$1=""; print substr($0, 2)}')
local port=$(echo "$tunnel_info" | cut -d: -f1)
if is_tunnel_alive "$port"; then
local pid=$(pgrep -f "ssh -N -L $LOCAL_IP:$port:" | head -n1)
echo -e "✓ $description ($LOCAL_IP:$port) - 运行中,PID: $pid"
else
echo -e "✗ $description ($LOCAL_IP:$port) - 未运行"
fi
done
# 检查监控进程状态
local monitor_pid=$(pgrep -f "$0 monitor")
if [[ -n "$monitor_pid" ]]; then
echo -e "\n✓ 监控进程 - 运行中,PID: $monitor_pid"
else
echo -e "\n✗ 监控进程 - 未运行"
fi
;;
restart)
"$0" stop
sleep 2
"$0" start
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
}
# 执行主函数
main "${@:-status}"
- 赋予执行权限:
chmod +x ssh_tunnel_monitor.sh
- 启动监控:
./ssh_tunnel_monitor.sh start
- 检查状态:
./ssh_tunnel_monitor.sh status
- 停止监控:
./ssh_tunnel_monitor.sh stop
测试:
查看ssh进程: ps -ef | grep ssh
杀死ssh进程:kill -9
进程号
查看ssh进程: ps -ef | grep ssh
- 如果还连接不上,请查看防火墙状态
systemctl status firewalld
- 添加规则
firewall-cmd --add-port=端口号/tcp --permanent
firewall-cmd --reload 生效
- 查看规则
firewall-cmd --list-all
2.2使用账号密码进行ssh跳转
sshpass_tunnel_monitor.sh
#!/bin/bash
# SSH隧道监控和自动重启脚本(优化版)
# 功能:自动监控隧道状态,断开时强制终止旧进程并重启,避免进程堆积
# --------------------- 配置区域 --------------------- #
LOG_FILE="/root/logs/ssh_tunnel_monitor/ssh_tunnel_monitor.log" # 日志文件路径
SSH_PASSWORD='your_password' # SSH 密码
SSH_USER="root" # SSH用户名
SSH_SERVER="xxx.xxx.xx.xx" # SSH服务器IP
LOCAL_IP="192.168.25.202" # 本地绑定IP
# 隧道配置:格式为 "本地端口:目标主机:目标端口 描述"
TUNNELS=(
"3307:ip:port MySQL数据库隧道"
)
# --------------------- 工具函数 --------------------- #
# 日志记录函数
log() {
"sshpass_tunnel_monitor.sh" 200L, 6701B 1,1 顶端
if [[ -n "$monitor_pid" ]]; then
echo -e "\n✓ 监控进程 - 运行中,PID: $monitor_pid"
else
echo -e "\n✗ 监控进程 - 未运行"
fi
;;
restart)
"$0" stop
sleep 2
"$0" start
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
}
# 执行主函数
main "${@:-status}"
"sshpass_tunnel_monitor.sh" 200L, 6701B 200,1 底端
local monitor_pid=$(pgrep -f "$0 monitor")
if [[ -n "$monitor_pid" ]]; then
echo -e "\n✓ 监控进程 - 运行中,PID: $monitor_pid"
else
echo -e "\n✗ 监控进程 - 未运行"
fi
;;
restart)
"$0" stop
sleep 2
"$0" start
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
}
# 执行主函数
main "${@:-status}"
200,1 底端
log "------------------------- 隧道状态 -------------------------"
for tunnel in "${TUNNELS[@]}"; do
local tunnel_info=$(echo "$tunnel" | awk '{print $1}')
local description=$(echo "$tunnel" | awk '{$1=""; print substr($0, 2)}')
local port=$(echo "$tunnel_info" | cut -d: -f1)
if is_tunnel_alive "$port"; then
local pid=$(pgrep -f "ssh -N -L $LOCAL_IP:$port:" | head -n1)
echo -e "✓ $description ($LOCAL_IP:$port) - 运行中,PID: $pid"
else
echo -e "✗ $description ($LOCAL_IP:$port) - 未运行"
fi
done
# 检查监控进程状态
local monitor_pid=$(pgrep -f "$0 monitor")
if [[ -n "$monitor_pid" ]]; then
echo -e "\n✓ 监控进程 - 运行中,PID: $monitor_pid"
else
echo -e "\n✗ 监控进程 - 未运行"
fi
;;
restart)
"$0" stop
sleep 2
"$0" start
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
}
# 执行主函数
main "${@:-status}"
200,1 底端
status)
log "------------------------- 隧道状态 -------------------------"
for tunnel in "${TUNNELS[@]}"; do
local tunnel_info=$(echo "$tunnel" | awk '{print $1}')
local description=$(echo "$tunnel" | awk '{$1=""; print substr($0, 2)}')
local port=$(echo "$tunnel_info" | cut -d: -f1)
if is_tunnel_alive "$port"; then
local pid=$(pgrep -f "ssh -N -L $LOCAL_IP:$port:" | head -n1)
echo -e "✓ $description ($LOCAL_IP:$port) - 运行中,PID: $pid"
else
echo -e "✗ $description ($LOCAL_IP:$port) - 未运行"
fi
done
# 检查监控进程状态
local monitor_pid=$(pgrep -f "$0 monitor")
if [[ -n "$monitor_pid" ]]; then
echo -e "\n✓ 监控进程 - 运行中,PID: $monitor_pid"
else
echo -e "\n✗ 监控进程 - 未运行"
fi
;;
restart)
"$0" stop
sleep 2
"$0" start
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
}
# 执行主函数
main "${@:-status}"