![隧道端口(服务)脱离外部无法访问的网络](https://linux22.com/image/1530653/%E9%9A%A7%E9%81%93%E7%AB%AF%E5%8F%A3%EF%BC%88%E6%9C%8D%E5%8A%A1%EF%BC%89%E8%84%B1%E7%A6%BB%E5%A4%96%E9%83%A8%E6%97%A0%E6%B3%95%E8%AE%BF%E9%97%AE%E7%9A%84%E7%BD%91%E7%BB%9C.png)
我正在协助管理一个私人网络,我想公开一些服务(例如,带有本地端口转发的 SSH 就很棒)。不幸的是,我可能无权在路由器中安装任何端口转发。不过,我可以做的是安装任意硬件(任何类型的计算机、Raspberry Pi 等)。为了解决这个问题,我想到从封闭网络到我控制的网络实现某种端口转发(例如将端口 22 转发到我的网络,然后从那里通过 SSH 访问“无法访问”的网络并进行一些本地端口转发,为 VPN 提供端口,等等)。然而,这对我来说似乎很容易出错,因为我需要实施一些 cron 作业来检查传出连接或建立一个连接。还有其他方法吗?我想象的情况在任何情况下都是常见的吗?谢谢你的帮助。
编辑
今天我读到了一些有关具有客户端 VPN 功能的路由器的文章 - 这可行吗?我想在某个我可以完全控制的地方实施 VPN,然后强制客户端 VPN 路由器连接,这样我就能够访问我想要远程访问的主机。
答案1
几年前我有过类似的要求。我用以下脚本解决了它。
#!/bin/bash
# This script is designed to be scheduled by cron as often as is required
# If STARTFILE exists, it will start/restart the connection
# If STOPFILE exists, it will stop the connection
# If PORTFILE exist, makes sure the tunnel is started, to survive restarts
#
# PORTFILE will always contain the port number on the remote host that the SSH connection is tunneled to
#
# This utilizes the control socket option of SSH to control the tunnel
# Base name, used by other variables
NAME="ssh_tunnel"
# Create this file to start the server
STARTFILE="/etc/$NAME/$NAME.start"
# This file will contain the port number on the remote server to connect to to access the tunnel
PORTFILE="/etc/$NAME/$NAME.port"
# Create this file to stop the server
STOPFILE="/etc/$NAME/$NAME.stop"
# The user and host to connect the tunnel to
REMOTE="user@hostname"
# The private key of the user on the remote server to create the tunnel to
KEYFILE="/etc/$NAME/.ssh/$NAME"
# The control socket of the SSH connection
SOCKET="/var/run/$NAME.socket"
# First port to try and listen on at remote host
LISTEN=9000
# Last port to try and listen on at remote host
MAXPORT=9999
SSH=$(which ssh)
# We need to run as root, otherwise it will fail
if [ "$(id -u)" != "0" ]; then
echo "Must be run as root!"
exit 1
fi
# Make directory if if doesn't exist
if [ ! -d "/etc/$NAME" ]; then
mkdir "/etc/$NAME"
fi
# Starts the tunnel and updates the control files
start_tunnel() {
# Remove port file, since it is outdated, if it exists
if [ -f ${PORTFILE} ]; then
rm -f ${PORTFILE}
fi
# Start tunnel and wait 2 seconds.. It the tunnel isn't up, then the port is busy (or the public key is foobar)
while true; do
${SSH} -M -S ${SOCKET} -2 -4 -C -f -N -i ${KEYFILE} -o CheckHostIP=no -o KeepAlive=yes -o StrictHostKeyChecking=no -o ExitOnForwardFailure=yes -o BatchMode=yes ${REMOTE} -R $LISTEN:localhost:22
sleep 2
check_tunnel && break
set LISTEN=LISTEN+1
if [ $LISTEN -eq $MAXPORT ]; then
# No ports available (or more likely, the public key is incorrect)
exit 1
fi
done
echo ${LISTEN} > ${PORTFILE}
# Remove startfile, since the process is now started
if [ -f ${STARTFILE} ]; then
rm -f ${STARTFILE}
fi
}
# Stops the tunnel and cleans up the control files
stop_tunnel() {
# Remove portfile and stopfile if they exist
if [ -f ${PORTFILE} ]; then
rm -f ${PORTFILE}
fi
if [ -f ${STOPFILE} ]; then
rm -f ${STOPFILE}
fi
${SSH} -S ${SOCKET} -O exit ${REMOTE} > /dev/null 2>&1
}
# Check if the tunnel is up
check_tunnel() {
if [ -e ${SOCKET} ]; then
(${SSH} -S ${SOCKET} -O check ${REMOTE} 2>&1 | grep -q "running") && return 0
fi
return 1
}
# Use a lock file so only one instance is running
(
flock -n 9 || exit 1
if [ -f ${STARTFILE} ]; then
# Restart if running, otherwise just start
check_tunnel && stop_tunnel
start_tunnel
elif [ -f ${STOPFILE} ]; then
# Stop if running
check_tunnel && stop_tunnel
elif [ -f ${PORTFILE} ]; then
# The tunnel should be running, might not be after a reboot, for example
check_tunnel || start_tunnel
fi
) 9>/var/run/$NAME.lock
该脚本设计为计划任务(在我的例子中,每分钟一次)。它检查变量中指定的文件是否存在STARTFILE
。这是通过 Web 界面创建的,但修改此脚本以满足您的需求应该相当容易。使用 Web 界面控制的原因是客户可能不希望他们的网络中存在永久后门。:) 不幸的是,我无法共享 Web 部件,因为这是一个更大项目的一部分。
无论如何,要使其正常工作,您必须配置REMOTE
和KEYFILE
变量,使其指向有效的用户/主机和私钥。其余的都是可选的。之后,只需创建STARTFILE
并运行脚本(或通过 cron 安排它),隧道就会启动。
假设您正在服务器上运行脚本A
,并且REMOTE
设置为。如果服务器上user@B
的内容为,那么您应该能够使用服务器上的任何有效用户通过登录到服务器。PORTFILE
A
9000
A
B:9000
A
我希望这一切都有意义。