目 录CONTENT

文章目录

Centos搭建Ngrok实现内网穿透

geekrabbit
2019-05-11 / 0 评论 / 0 点赞 / 733 阅读 / 10,511 字 / 正在检测是否收录...
温馨提示:
创作不易,转载请注明出处

前言:

原来一直用的别人搭建的内网穿透,因为是免费的,所以网速,稳定性都特别差,所以打算自己搭建一个属于自己的内网穿透

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()
0
博主关闭了所有页面的评论