前言:
原来一直用的别人搭建的内网穿透,因为是免费的,所以网速,稳定性都特别差,所以打算自己搭建一个属于自己的内网穿透
1、准备
一台云服务器,一个域名,并且域名泛解析解析到云服务器,此处我用的服务器的操作系统为CentOS7(amd64)
2、安装环境
安装gcc和git(用于下载ngrok源码)
yum install gcc -y
yum install git -y
3、安装go语言环境
yum install -y mercurial git bzr subversion golang golang-pkg-windows-amd64 golang-pkg-windows-386
4、检查环境安装
git --version //( >= 1.7 )
go version
5、在服务器上搭建Ngrok服务
5.1.下载ngrok源码
git clone https://github.com/inconshreveable/ngrok.git
5.2.生成证书
cd ngrok
abc.com这里修改为自己的域名,命令一条一条复制即可
export NGROK_DOMAIN="abc.com"
openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pem
openssl genrsa -out device.key 2048
openssl req -new -key device.key -subj "/CN=$NGROK_DOMAIN" -out device.csr
openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 5000
5.3.将新生成的证书替换,执行下面命令后 “y” 回车 一行一行执行代码!
cp rootCA.pem assets/client/tls/ngrokroot.crt
cp device.crt assets/server/tls/snakeoil.crt
cp device.key assets/server/tls/snakeoil.key
6、编译生成ngrokd(服务端)
GOOS=linux GOARCH=amd64 make release-server
生成在~/ngrok/bin/目录中
7、编译生成ngrok(客户端)
GOOS=windows GOARCH=amd64 make release-client
生成在~/ngrok/bin/windows_amd64/目录中
8、用SSH Secure Shell Client工具
将~/ngrok/bin/windows_amd64/里的文件下载到本地Windows下,如D:\ngrok
9、在D:\ngrok中新建文件,改名为 ngrok.cfg
文件中输入:
server_addr: "abc.com:8083"
trust_host_root_certs: false
tunnels:
http:
subdomain: "www"
proto:
http: "80"
https:
subdomain: "www"
proto:
https: "443"
ssh:
remote_port: 2222
proto:
tcp: "22"
mstsc:
remote_port: 52222
proto:
tcp: "192.168.0.107:3389"
以上 8083 80 443 与 远程开启的端口一致,
10、开启远程服务
在ngrok目录中
cd ngrok
sudo ./bin/ngrokd -domain="abc.com" -httpAddr=":80" -httpsAddr=":443" -tunnelAddr=":8083" &
这里的端口号与config文件对应,视情况而定
[16:05:23 CST 2018/01/09][INFO] (ngrok/log.(*PrefixLogger).Info:83) [registry][tun] No affinity cache specified
[16:05:23 CST 2018/01/09][INFO] (ngrok/log.Info:112) Listening for public http connections on [::]:80
[16:05:23 CST 2018/01/09][INFO] (ngrok/log.Info:112) Listening for public https connections on [::]:443
[16:05:23 CST 2018/01/09][INFO] (ngrok/log.Info:112) Listening for control and proxy connections on [::]:8083
[16:05:23 CST 2018/01/09][INFO] (ngrok/log.(*PrefixLogger).Info:83) [metrics] Reporting every 30 seconds
显示此为成功开启
如果有关闭终端,相关程序就被杀死的情况
1.安装screen
yum install -y screen
2.输入screen,然后输入你要运行的命令
3.ctrl+A 然后按D,screen会关闭
4.查看正在运行的程序
screen -ls
现在关闭终端,在screen中的程序会继续后台运行
11、开启客户机服务
在Windows中D:\ngrok新建文件 改名 start.bat
输入:
ngrok -config=ngrok.cfg start http https ssh mstsc
直接双击运行
表示成功,
不成功的话看看自己的开启端口是否与config文件中对应
或查看服务器的远程端口是否在安全组中打开
!!!这很重要!!!
关于ngrok在远程开机自启问题
服务器后台开机启动运行ngrok服务端:
1.以下内容新建一个 start.sh 文件 放到 ~/ngrok/start.sh
~/ngrok/bin/ngrokd -domain="abc.com" -httpAddr=":80" -httpsAddr=":443" -tunnelAddr=":8083" &
给权限:
chmod 755 ~/ngrok/start.sh
2.新建ngrok启动脚本文件
sudo vi /etc/init.d/ngrok
文件内容:
#!/bin/sh
#chkconfig:2345 70 30
#description:ngrok
BEGIN INIT INFO
Provides: ngrok
Required-Start:
Required-Stop:
Default-Start: 2 3 4 5
Default-Stop: 0 1 6
Short-Description: Start or stop the ngrok Proxy.
END INIT INFO
ngrok_path=~/ngrok/
case "$1" in
start)
echo "start ngrok service.."
sh ${ngrok_path}/start.sh
;;
*)
exit 1
;;
esac
提示 : 运行sudo vi /etc/init.d/ngrok之后 !!直接按键盘 I 进入编辑模式,然后复制下面内容 然后 “esc” “:” “wq” “!” “回车” 意思是保存退出!
3.ngrok脚本文件 给权限
cd /etc/init.d
chmod 755 ngrok
4.添加启动服务 ngrok
chkconfig --add ngrok
5.测试服务是否能启动成功
service ngrok start
6.查看自启动的服务 是否有 nrgok !!
chkconfig
执行这个代码如果出现后面的就OK了!! ngrok 0:off 1:off 2:on 3:on 4:on 5:on 6:off
服务器ngrok的服务端开机自动启动成功了!!
7.在此附上python版源码
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# 建议Python 2.7.9 或 Python 3.4.2 以上运行
# 项目地址: https://github.com/hauntek/python-ngrok
# Version: v1.5
import socket
import ssl
import json
import struct
import random
import sys
import time
import logging
import threading
host = 'tunnel.qydev.com' # Ngrok服务器地址
port = 4443 # 端口
bufsize = 1024 # 吞吐量
Tunnels = list() # 全局渠道赋值
body = dict()
body['protocol'] = 'http'
body['hostname'] = 'www.xxx.com'
body['subdomain'] = ''
body['rport'] = 0
body['lhost'] = '127.0.0.1'
body['lport'] = 80
Tunnels.append(body) # 加入渠道队列
body = dict()
body['protocol'] = 'http'
body['hostname'] = ''
body['subdomain'] = 'xxx'
body['rport'] = 0
body['lhost'] = '127.0.0.1'
body['lport'] = 80
Tunnels.append(body) # 加入渠道队列
body = dict()
body['protocol'] = 'tcp'
body['hostname'] = ''
body['subdomain'] = ''
body['rport'] = 55499
body['lhost'] = '127.0.0.1'
body['lport'] = 22
Tunnels.append(body) # 加入渠道队列
reqIdaddr = dict()
localaddr = dict()
# 读取配置文件
if len(sys.argv) >= 2:
file_object = open(sys.argv[1])
try:
all_the_text = file_object.read()
config_object = json.loads(all_the_text)
host = config_object["server"]["host"] # Ngrok服务器地址
port = int(config_object["server"]["port"]) # 端口
bufsize = int(config_object["server"]["bufsize"]) # 吞吐量
Tunnels = list() # 重置渠道赋值
for Tunnel in config_object["client"]:
body = dict()
body['protocol'] = Tunnel["protocol"]
body['hostname'] = Tunnel["hostname"]
body['subdomain'] = Tunnel["subdomain"]
body['rport'] = int(Tunnel["rport"])
body['lhost'] = Tunnel["lhost"]
body['lport'] = int(Tunnel["lport"])
Tunnels.append(body) # 加入渠道队列
del all_the_text
del config_object
except Exception:
# logger = logging.getLogger('%s' % 'config')
# logger.error('The configuration file read failed')
# exit(1)
pass
finally:
file_object.close()
mainsocket = 0
ClientId = ''
pingtime = 0
def getloacladdr(Tunnels, Url):
protocol = Url[0:Url.find(':')]
hostname = Url[Url.find('//') + 2:]
subdomain = hostname[0:hostname.find('.')]
rport = Url[Url.rfind(':') + 1:]
for tunnelinfo in Tunnels:
if tunnelinfo.get('protocol') == protocol:
if tunnelinfo.get('protocol') in ['http', 'https']:
if tunnelinfo.get('hostname') == hostname:
return tunnelinfo
if tunnelinfo.get('subdomain') == subdomain:
return tunnelinfo
if tunnelinfo.get('protocol') == 'tcp':
if tunnelinfo.get('rport') == int(rport):
return tunnelinfo
return dict()
def dnsopen(host):
try:
ip = socket.gethostbyname(host)
except socket.error:
return False
return ip
def connectremote(host, port):
try:
host = socket.gethostbyname(host)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssl_client = ssl.wrap_socket(client, ssl_version=ssl.PROTOCOL_SSLv23)
ssl_client.connect((host, port))
ssl_client.setblocking(1)
logger = logging.getLogger('%s:%d' % ('Conn', ssl_client.fileno()))
logger.debug('New connection to: %s:%d' % (host, port))
except socket.error:
return False
return ssl_client
def connectlocal(localhost, localport):
try:
localhost = socket.gethostbyname(localhost)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((localhost, localport))
client.setblocking(1)
logger = logging.getLogger('%s:%d' % ('Conn', client.fileno()))
logger.debug('New connection to: %s:%d' % (localhost, localport))
except socket.error:
return False
return client
def NgrokAuth():
Payload = dict()
Payload['ClientId'] = ''
Payload['OS'] = 'darwin'
Payload['Arch'] = 'amd64'
Payload['Version'] = '2'
Payload['MmVersion'] = '1.7'
Payload['User'] = 'user'
Payload['Password'] = ''
body = dict()
body['Type'] = 'Auth'
body['Payload'] = Payload
buffer = json.dumps(body)
return(buffer)
def ReqTunnel(ReqId, Protocol, Hostname, Subdomain, RemotePort):
Payload = dict()
Payload['ReqId'] = ReqId
Payload['Protocol'] = Protocol
Payload['Hostname'] = Hostname
Payload['Subdomain'] = Subdomain
Payload['HttpAuth'] = ''
Payload['RemotePort'] = RemotePort
body = dict()
body['Type'] = 'ReqTunnel'
body['Payload'] = Payload
buffer = json.dumps(body)
return(buffer)
def RegProxy(ClientId):
Payload = dict()
Payload['ClientId'] = ClientId
body = dict()
body['Type'] = 'RegProxy'
body['Payload'] = Payload
buffer = json.dumps(body)
return(buffer)
def Ping():
Payload = dict()
body = dict()
body['Type'] = 'Ping'
body['Payload'] = Payload
buffer = json.dumps(body)
return(buffer)
def lentobyte(len):
return struct.pack(' 0:
if not recvbuf:
recvbuf = recvbut
else:
recvbuf += recvbut
if type == 1 or (type == 2 and linkstate == 1):
lenbyte = tolen(recvbuf[0:8])
if len(recvbuf) >= (8 + lenbyte):
buf = recvbuf[8:lenbyte + 8].decode('utf-8')
logger = logging.getLogger('%s:%d' % ('Recv', sock.fileno()))
logger.debug('Reading message with length: %d' % len(buf))
logger.debug('Read message: %s' % buf)
js = json.loads(buf)
if type == 1:
if js['Type'] == 'ReqProxy':
newsock = connectremote(host, port)
if newsock:
thread = threading.Thread(target = HKClient, args = (newsock, 0, 2))
thread.setDaemon(True)
thread.start()
if js['Type'] == 'AuthResp':
ClientId = js['Payload']['ClientId']
logger = logging.getLogger('%s' % 'client')
logger.info('Authenticated with server, client id: %s' % ClientId)
sendpack(sock, Ping())
pingtime = time.time()
for info in Tunnels:
reqid = getRandChar(8)
sendpack(sock, ReqTunnel(reqid, info['protocol'], info['hostname'], info['subdomain'], info['rport']))
reqIdaddr[reqid] = (info['lhost'], info['lport'])
if js['Type'] == 'NewTunnel':
if js['Payload']['Error'] != '':
logger = logging.getLogger('%s' % 'client')
logger.error('Server failed to allocate tunnel: %s' % js['Payload']['Error'])
time.sleep(30)
else:
logger = logging.getLogger('%s' % 'client')
logger.info('Tunnel established at %s' % js['Payload']['Url'])
localaddr[js['Payload']['Url']] = reqIdaddr[js['Payload']['ReqId']]
if type == 2:
if js['Type'] == 'StartProxy':
localhost, localport = localaddr[js['Payload']['Url']]
newsock = connectlocal(localhost, localport)
if newsock:
thread = threading.Thread(target = HKClient, args = (newsock, 0, 3, sock))
thread.setDaemon(True)
thread.start()
tosock = newsock
linkstate = 2
else:
body = 'Tunnel %s unavailableUnable to initiate connection to %s. This port is not yet available for web server.'
html = body % (js['Payload']['Url'], localhost + ':' + str(localport))
header = "HTTP/1.0 502 Bad Gateway" + "\r\n"
header += "Content-Type: text/html" + "\r\n"
header += "Content-Length: %d" + "\r\n"
header += "\r\n" + "%s"
buf = header % (len(html.encode('utf-8')), html)
sendbuf(sock, buf.encode('utf-8'))
if len(recvbuf) == (8 + lenbyte):
recvbuf = bytes()
else:
recvbuf = recvbuf[8 + lenbyte:]
if type == 3 or (type == 2 and linkstate == 2):
sendbuf(tosock, recvbuf)
recvbuf = bytes()
except socket.error:
break
if type == 1:
mainsocket = False
if type == 3:
try:
tosock.shutdown(socket.SHUT_WR)
except socket.error:
tosock.close()
logger = logging.getLogger('%s:%d' % ('Close', sock.fileno()))
logger.debug('Closing')
sock.close()
# 客户端程序初始化
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
logger = logging.getLogger('%s' % 'client')
logger.info('python-ngrok v1.5')
while True:
try:
# 检测控制连接是否连接.
if mainsocket == False:
ip = dnsopen(host)
if ip == False:
logger = logging.getLogger('%s' % 'client')
logger.info('update dns')
time.sleep(10)
continue
mainsocket = connectremote(ip, port)
if mainsocket == False:
logger = logging.getLogger('%s' % 'client')
logger.info('connect failed...!')
time.sleep(10)
continue
thread = threading.Thread(target = HKClient, args = (mainsocket, 0, 1))
thread.setDaemon(True)
thread.start()
# 发送心跳
if pingtime + 20 < time.time() and pingtime != 0:
sendpack(mainsocket, Ping())
pingtime = time.time()
time.sleep(1)
except socket.error:
pingtime = 0
except KeyboardInterrupt:
sys.exit()