参考网址: https://www.vediotalk.com/archives/336
参考网址: https://studygolang.com/articles/15122
参考网址: https://github.com/prikevs/ngrok/commit/aa9b88d4e070069db6d8f88aa82526bcbcd1d0b6
go 和 git 的下载: 链接:https://pan.baidu.com/s/1o23GK9VlNRXhg_IDJyGofQ
提取码:wbyj
复制这段内容后打开百度网盘手机App,操作更方便哦
容易出莫名其妙的问题
tar -C /usr/local -xzf go1.9.2.linux-amd64.tar.gz
mkdir $HOME/go
echo 'export GOROOT=/usr/local/go'>> ~/.bashrc
echo 'export GOPATH=$HOME/go'>> ~/.bashrc
echo 'export PATH=$PATH:$GOROOT/bin'>> ~/.bashrc
source $HOME/.bashrc
tar -zxvf git-2.9.0.tar.gz
cd git-2.9.0
./configure --prefix=/usr/local/git
make && make install
export PATH="/usr/local/git/bin:$PATH" >> ~/.bashrc
source $HOME/.bashrc
git --version
cd /usr/local
git clone https://github.com/inconshreveable/ngrok.git
# ngrok
# 修改为自己解析的域名
export NGROK_DOMAIN="ngrok.chenzhi.website"
export GOPATH="/usr/local/ngrok/"
source ~/.bashrc
cd /usr/local/ngrok
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
# 查看一下
ls -l
-rw-r--r-- 1 root root 1679 Dec 30 21:37 rootCA.key
-rw-r--r-- 1 root root 1139 Dec 30 21:37 rootCA.pem
-rw-r--r-- 1 root root 17 Dec 30 21:37 rootCA.srl
-rw-r--r-- 1 root root 1021 Dec 30 21:37 server.crt
-rw-r--r-- 1 root root 911 Dec 30 21:37 server.csr
-rw-r--r-- 1 root root 1679 Dec 30 21:37 server.key
yes
cp rootCA.pem assets/client/tls/ngrokroot.crt
cp device.crt assets/server/tls/snakeoil.crt
cp device.key assets/server/tls/snakeoil.key
/root/go1.4/bin
的错误rm -r /root/go1.4
cp /usr/local/go /root/go1.4 -r
cd /usr/local/go/src/
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./make.bash
cd /usr/local/ngrok
GOOS=windows GOARCH=amd64 make release-server release-client
cd /usr/local/go/src/
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 ./make.bash
cd /usr/local/ngrok
GOOS=darwin GOARCH=amd64 make release-server release-client
cd /usr/local/go/src/
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 ./make.bash
cd /usr/local/ngrok
GOOS=linux GOARCH=amd64 make release-server release-client
cd /usr/local/go/src/
GOOS=linux GOARCH=arm CGO_ENABLED=0 ./make.bash
cd /usr/local/ngrok
GOOS=linux GOARCH=arm make release-server release-client
/usr/local/ngrok/bin/ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":80"
/usr/local/ngrok/bin/ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":80" -httpsAddr=":443" -tunnelAddr=":4443"
nohup /usr/local/ngrok/bin/ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":80" &
setsid /usr/local/ngrok/bin/ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":80" -httpsAddr=":443" -tunnelAddr=":4443"
# 得到 PID
ps -aux | grep ngrokd
# kill PID
kill xxxx
(2)参数说明:3 个端口可随意配置(不能和现有服务端口冲突,阿里云服务器需打开对应端口)
# -domain 访问 ngrok 是所设置的服务地址生成证书时那个域名
# -httpAddr http 协议端口 默认为 80
# -httpsAddr https 协议端口 默认为 443 (可配置 https 证书)
# -tunnelAddr 通道端口,默认 4443
(3)注意:
所有涉及的端口,都需在阿里云(腾讯云)上设置规则,即打开端口
server_addr: "ngrok.chenzhi.website:4443" # 远程地址
trust_host_root_certs: false
tunnels: # 隧道
ssh: # 隧道名称
remote_port: 9022 # 绑定到远程地址的那个端口
proto:
tcp: 22 # 本地的 TCP 22端口
ngrok.cfg
server_addr: "ngrok.yanlongfei.top:4443" //注意域名一定要和服务器端一致
trust_host_root_certs: false
tunnels:
web:
subdomain: zhiyong47 # 域名前缀 zhiyong47.ngrok.yanlongfei.top
remote_port: 9024
proto:
http: 80 # 本地的 HTTP 80 端口
https:
subdomain: "ssl"
proto:
https: 443
open:
remote_port: 5555 # 映射的端口
proto:
tcp: 1194
ssh:
remote_port: 2222
proto:
tcp: 22
./ngrok -config=ngrok.cfg start http #启动 web 服务
./ngrok -config=ngrok.cfg start tcp #启动 tcp 服务
./ngrok -config=ngrok.cfg start http tcp #同时启动两个服务
./ngrok -config=ngrok.cfg start ssh #同时启动 shh
./ngrok -config=ngrok.cfg start-all #启动所有服务
# 在上面每一条命令加
setsid ******************
# 比如
setsid ./ngrok -config=ngrok.cfg start ssh
ngrok/src/ngrok/server/control.go
为package server
import (
"fmt"
"io"
"ngrok/conn"
"ngrok/msg"
"ngrok/util"
"ngrok/version"
"runtime/debug"
"strings"
"time"
"os"
"bufio"
)
const (
pingTimeoutInterval = 30 * time.Second
connReapInterval = 10 * time.Second
controlWriteTimeout = 10 * time.Second
proxyStaleDuration = 60 * time.Second
proxyMaxPoolSize = 10
)
type Control struct {
// auth message
auth *msg.Auth
// actual connection
conn conn.Conn
// put a message in this channel to send it over
// conn to the client
out chan (msg.Message)
// read from this channel to get the next message sent
// to us over conn by the client
in chan (msg.Message)
// the last time we received a ping from the client - for heartbeats
lastPing time.Time
// all of the tunnels this control connection handles
tunnels []*Tunnel
// proxy connections
proxies chan conn.Conn
// identifier
id string
// synchronizer for controlled shutdown of writer()
writerShutdown *util.Shutdown
// synchronizer for controlled shutdown of reader()
readerShutdown *util.Shutdown
// synchronizer for controlled shutdown of manager()
managerShutdown *util.Shutdown
// synchronizer for controller shutdown of entire Control
shutdown *util.Shutdown
}
func NewControl(ctlConn conn.Conn, authMsg *msg.Auth) {
var err error
// create the object
c := &Control{
auth: authMsg,
conn: ctlConn,
out: make(chan msg.Message),
in: make(chan msg.Message),
proxies: make(chan conn.Conn, 10),
lastPing: time.Now(),
writerShutdown: util.NewShutdown(),
readerShutdown: util.NewShutdown(),
managerShutdown: util.NewShutdown(),
shutdown: util.NewShutdown(),
}
failAuth := func(e error) {
_ = msg.WriteMsg(ctlConn, &msg.AuthResp{Error: e.Error()})
ctlConn.Close()
}
readLine := func(token string, filename string) (bool, error) {
if token == "" {
return false, nil;
}
f, err := os.Open(filename)
if err != nil {
return false, err
}
buf := bufio.NewReader(f)
for {
line, err := buf.ReadString('\n')
line = strings.TrimSpace(line)
if line == token {
return true, nil
}
if err != nil {
if err == io.EOF {
return false, nil
}
return false, err
}
}
return false, nil
}
// register the clientid
c.id = authMsg.ClientId
if c.id == "" {
// it's a new session, assign an ID
if c.id, err = util.SecureRandId(16); err != nil {
failAuth(err)
return
}
}
// set logging prefix
ctlConn.SetType("ctl")
ctlConn.AddLogPrefix(c.id)
if authMsg.Version != version.Proto {
failAuth(fmt.Errorf("Incompatible versions. Server %s, client %s. Download a new version at http://ngrok.com", version.MajorMinor(), authMsg.Version))
return
}
authd, err := readLine(authMsg.User, "authtokens.txt")
if authd != true {
failAuth(fmt.Errorf("authtoken %s invalid", "is"));
return
}
// register the control
if replaced := controlRegistry.Add(c.id, c); replaced != nil {
replaced.shutdown.WaitComplete()
}
// start the writer first so that the following messages get sent
go c.writer()
// Respond to authentication
c.out <- &msg.AuthResp{
Version: version.Proto,
MmVersion: version.MajorMinor(),
ClientId: c.id,
}
// As a performance optimization, ask for a proxy connection up front
c.out <- &msg.ReqProxy{}
// manage the connection
go c.manager()
go c.reader()
go c.stopper()
}
// Register a new tunnel on this control connection
func (c *Control) registerTunnel(rawTunnelReq *msg.ReqTunnel) {
for _, proto := range strings.Split(rawTunnelReq.Protocol, "+") {
tunnelReq := *rawTunnelReq
tunnelReq.Protocol = proto
c.conn.Debug("Registering new tunnel")
t, err := NewTunnel(&tunnelReq, c)
if err != nil {
c.out <- &msg.NewTunnel{Error: err.Error()}
if len(c.tunnels) == 0 {
c.shutdown.Begin()
}
// we're done
return
}
// add it to the list of tunnels
c.tunnels = append(c.tunnels, t)
// acknowledge success
c.out <- &msg.NewTunnel{
Url: t.url,
Protocol: proto,
ReqId: rawTunnelReq.ReqId,
}
rawTunnelReq.Hostname = strings.Replace(t.url, proto + "://", "", 1)
}
}
func (c *Control) manager() {
// don't crash on panics
defer func() {
if err := recover(); err != nil {
c.conn.Info("Control::manager failed with error %v: %s", err, debug.Stack())
}
}()
// kill everything if the control manager stops
defer c.shutdown.Begin()
// notify that manager() has shutdown
defer c.managerShutdown.Complete()
// reaping timer for detecting heartbeat failure
reap := time.NewTicker(connReapInterval)
defer reap.Stop()
for {
select {
case <-reap.C:
if time.Since(c.lastPing) > pingTimeoutInterval {
c.conn.Info("Lost heartbeat")
c.shutdown.Begin()
}
case mRaw, ok := <-c.in:
// c.in closes to indicate shutdown
if !ok {
return
}
switch m := mRaw.(type) {
case *msg.ReqTunnel:
c.registerTunnel(m)
case *msg.Ping:
c.lastPing = time.Now()
c.out <- &msg.Pong{}
}
}
}
}
func (c *Control) writer() {
defer func() {
if err := recover(); err != nil {
c.conn.Info("Control::writer failed with error %v: %s", err, debug.Stack())
}
}()
// kill everything if the writer() stops
defer c.shutdown.Begin()
// notify that we've flushed all messages
defer c.writerShutdown.Complete()
// write messages to the control channel
for m := range c.out {
c.conn.SetWriteDeadline(time.Now().Add(controlWriteTimeout))
if err := msg.WriteMsg(c.conn, m); err != nil {
panic(err)
}
}
}
func (c *Control) reader() {
defer func() {
if err := recover(); err != nil {
c.conn.Warn("Control::reader failed with error %v: %s", err, debug.Stack())
}
}()
// kill everything if the reader stops
defer c.shutdown.Begin()
// notify that we're done
defer c.readerShutdown.Complete()
// read messages from the control channel
for {
if msg, err := msg.ReadMsg(c.conn); err != nil {
if err == io.EOF {
c.conn.Info("EOF")
return
} else {
panic(err)
}
} else {
// this can also panic during shutdown
c.in <- msg
}
}
}
func (c *Control) stopper() {
defer func() {
if r := recover(); r != nil {
c.conn.Error("Failed to shut down control: %v", r)
}
}()
// wait until we're instructed to shutdown
c.shutdown.WaitBegin()
// remove ourself from the control registry
controlRegistry.Del(c.id)
// shutdown manager() so that we have no more work to do
close(c.in)
c.managerShutdown.WaitComplete()
// shutdown writer()
close(c.out)
c.writerShutdown.WaitComplete()
// close connection fully
c.conn.Close()
// shutdown all of the tunnels
for _, t := range c.tunnels {
t.Shutdown()
}
// shutdown all of the proxy connections
close(c.proxies)
for p := range c.proxies {
p.Close()
}
c.shutdown.Complete()
c.conn.Info("Shutdown complete")
}
func (c *Control) RegisterProxy(conn conn.Conn) {
conn.AddLogPrefix(c.id)
conn.SetDeadline(time.Now().Add(proxyStaleDuration))
select {
case c.proxies <- conn:
conn.Info("Registered")
default:
conn.Info("Proxies buffer is full, discarding.")
conn.Close()
}
}
// Remove a proxy connection from the pool and return it
// If not proxy connections are in the pool, request one
// and wait until it is available
// Returns an error if we couldn't get a proxy because it took too long
// or the tunnel is closing
func (c *Control) GetProxy() (proxyConn conn.Conn, err error) {
var ok bool
// get a proxy connection from the pool
select {
case proxyConn, ok = <-c.proxies:
if !ok {
err = fmt.Errorf("No proxy connections available, control is closing")
return
}
default:
// no proxy available in the pool, ask for one over the control channel
c.conn.Debug("No proxy in pool, requesting proxy from control . . .")
if err = util.PanicToError(func() {
c.out <- &msg.ReqProxy{}
}); err != nil {
return
}
select {
case proxyConn, ok = <-c.proxies:
if !ok {
err = fmt.Errorf("No proxy connections available, control is closing")
return
}
case <-time.After(pingTimeoutInterval):
err = fmt.Errorf("Timeout trying to get proxy connection")
return
}
}
return
}
// Called when this control is replaced by another control
// this can happen if the network drops out and the client reconnects
// before the old tunnel has lost its heartbeat
func (c *Control) Replaced(replacement *Control) {
c.conn.Info("Replaced by control: %s", replacement.conn.Id())
// set the control id to empty string so that when stopper()
// calls registry.Del it won't delete the replacement
c.id = ""
// tell the old one to shutdown
c.shutdown.Begin()
}
bin
中创建 authtokens.txt
[root@VM-8-5-centos ngrok]# tree bin
bin
|-- authtokens.txt
|-- go-bindata
|-- linux_amd64_linux
| |-- ngrok
| `-- ngrokd
|-- linux_arm
| |-- ngrok
| `-- ngrokd
|-- ngrok
|-- ngrokd
|-- nohup.out
`-- windows_amd64
|-- ngrokd.exe
`-- ngrok.exe
3 directories, 11 files
[root@VM-8-5-centos ngrok]# cat bin/authtokens.txt
username:passwd
ngrok.cfg
, 关键是添加第 3
行server_addr: ngrok.testdomain.com:4443
trust_host_root_certs: true
auth_token: username:password # 关键是添加这一行
tunnels:
kian:
subdomain: kian
proto:
http: "80"
https: "8080"