One Love
00:00 / 03:25
https://mojotv.cn/go/hardware-footprint-gui-proxy
Go进阶32:HTTP-Reverse-Proxy反向代理Nginx硬件指纹校验
1. 解决了什么需求
- 只容许特定机器访问公网某网站,其他客户端浏览器访问都返回404
- 使用Golang获取机器的唯一指纹
- 使用Golang开发完成一个签到chrome的GUI客户端
- 使用Golang开发一个HTTP协议代理,同时附加上机器指纹
2. 代码实现
2.1 获取硬件指纹
因为每台电脑的网卡mac地址重复概率非常低,而且一般电脑上都包含多个网卡,这样就会多个mac地址(蓝牙mac地址,wlan网卡地址,有线网卡地址), 所以获取多个mac地址在进行md5/SHA-256,就可以得到机器的fingerprint指纹.
2.1.1 硬件信息指纹:全部MAC地址SHA-256
import (
"crypto/sha256"
"encoding/hex"
)
func getMacAddrSha256() string {
netInterfaces, err := net.Interfaces()
if err != nil {
fmt.Printf("fail to get net interfaces: %v", err)
return ""
}
var macAddrs []string
for _, netInterface := range netInterfaces {
macAddr := netInterface.HardwareAddr.String()
if len(macAddr) == 0 {
continue
}
macAddrs = append(macAddrs, macAddr)
}
str := strings.Join(macAddrs, "_")
h := sha256.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
2.1.2 硬件信息指纹:全部MAC地址MD5
import (
"crypto/md5"
"encoding/hex"
)
func getMacAddrMd5() string {
netInterfaces, err := net.Interfaces()
if err != nil {
fmt.Printf("fail to get net interfaces: %v", err)
return ""
}
var macAddrs []string
for _, netInterface := range netInterfaces {
macAddr := netInterface.HardwareAddr.String()
if len(macAddr) == 0 {
continue
}
macAddrs = append(macAddrs, macAddr)
}
str := strings.Join(macAddrs, "_")
h := md5.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
2.2 设置Golang标准库HTTP-Reverse(反向代理)
import (
"net"
"net/http"
"net/http/httputil"
)
func runHttpReverseProxy() string{
proxy := &httputil.ReverseProxy{
Transport: roundTripper(rt),
Director: func(req *http.Request) {
req.URL.Scheme = "http"
req.URL.Host = remoteHostOrIp // 填写远端,需要被代理的地址或者域名
req.Header.Set("user-agent", getMacAddrMd5()) // 修改请求header, 把机器的mac指纹设置成user-agent, nginx 通过user-agent 验证机器合法性
},
}
//开启本地可用的端口作为代理端口
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
// 打印可用的代理地址
log.Println(ln.Addr())
//是否在开一个goroutine 根据您自己的业务需求确定
go http.Serve(ln, proxy)
//获取代理端口
return ln.Addr()
}
func rt(req *http.Request) (*http.Response, error) {
log.Printf("request received. url=%s", req.URL)
req.Header.Set("Authorization", "golang.tech.mojotv.cn") //也可以在这里对http/request 通讯进篡改
defer log.Printf("request complete. url=%s", req.URL)
return http.DefaultTransport.RoundTrip(req)
}
// roundTripper makes func signature a http.RoundTripper
type roundTripper func(*http.Request) (*http.Response, error)
func (f roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }
2.3 打开GUI或者浏览器
2.3.1 调用浏览器反向代理
详细内容参照我的另外一篇文章: Go进阶22:Go调用浏览访问链接
import (
"os/exec"
)
func browserOpen(url string) error {
var cmd string
var args []string
switch runtime.GOOS {
case "windows":
cmd = "cmd"
args = []string{"/c", "start"}
case "darwin":
cmd = "open"
default: // "linux", "freebsd", "openbsd", "netbsd"
cmd = "xdg-open"
}
args = append(args, url)
return exec.Command(cmd, args...).Start()
}
2.3.2 打开GUI
github.com/zserge/lorca 用法可以参照我的Golang GUI教程 使用golang Lora开发一个图像界面GUI应用
import (
"github.com/zserge/lorca"
)
func main() {
//也可以使用getMacAddrSha256()
log.Println(getMacAddrMd5())
// Create and bind Go object to the UI
// Load HTML.
// You may also use `data:text/html,<base64>` approach to load initial HTML,
// e.g: ui.Load("data:text/html," + url.PathEscape(html))
proxy := &httputil.ReverseProxy{
Transport: roundTripper(rt),
Director: func(req *http.Request) {
req.URL.Scheme = "http"
req.URL.Host = remoteHostOrIp
req.Header.Set("user-agent", getMacAddrMd5()) // <--- I set it here first
},
}
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
go func() {
log.Fatal("run proxy failed: ", http.Serve(ln, proxy))
}()
time.Sleep(time.Second * 2)
firstUrl := fmt.Sprintf("http://%s/#/login", ln.Addr())
ui, err := lorca.New(firstUrl, "", 1280, 960)
if err != nil {
log.Fatal(err)
}
defer ui.Close()
// A simple way to know when UI is ready (uses body.onload event in JS)
ui.Bind("start", func() {
log.Println("UI is ready")
})
// Wait until the interrupt signal arrives or browser window is closed
sigc := make(chan os.Signal)
signal.Notify(sigc, os.Interrupt)
select {
case <-sigc:
case <-ui.Done():
}
log.Println("exiting...")
}
2.4 完整代码
https://github.com/mojocn/httpproxyapp/blob/master/main.go
package main
import (
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/zserge/lorca"
"log"
"net"
"net/http"
"net/http/httputil"
"os"
"os/exec"
"os/signal"
"runtime"
"strings"
"time"
)
const remoteHostOrIp = "120.163.249.4"
//https://github.com/golang/go/issues/28168
func main2() {
proxy := &httputil.ReverseProxy{
Transport: roundTripper(rt),
Director: func(req *http.Request) {
req.URL.Scheme = "http"
req.URL.Host = remoteHostOrIp
req.Header.Set("user-agent", getMacAddrMd5()) // <--- I set it here first
},
}
log.Fatal(http.ListenAndServe("127.0.0.1:8888", proxy))
}
func rt(req *http.Request) (*http.Response, error) {
log.Printf("request received. url=%s", req.URL)
req.Header.Set("Host", "dev.tech.mojotv.cn") // <--- I set it here as well
defer log.Printf("request complete. url=%s", req.URL)
return http.DefaultTransport.RoundTrip(req)
}
// roundTripper makes func signature a http.RoundTripper
type roundTripper func(*http.Request) (*http.Response, error)
func (f roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }
func mustRunProxy() net.Addr {
proxy := &httputil.ReverseProxy{
Transport: roundTripper(rt),
Director: func(req *http.Request) {
req.URL.Scheme = "http"
req.URL.Host = remoteHostOrIp
req.