Contents

使用正向代理远程访问 k8s 服务

Contents

HTTP

原理:部署一个僵尸 Pod 正向代理 k8s 服务,通过暴露 NodePort 对外提供访问

func getRsp(request *http.Request) (*http.Response, error) {
	transport := http.DefaultTransport
	outReq := new(http.Request)
	*outReq = *request
	// 构建roundTrip
	rsp, err := transport.RoundTrip(outReq)
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

func copyHeader(writer http.ResponseWriter, header map[string][]string) {
	for key, value := range header {
		for _, v := range value {
			writer.Header().Add(key, v)
		}
	}
}
func copyRspBody(writer http.ResponseWriter, rsp *http.Response) {
	// 写入http statusCode 状态码
	writer.WriteHeader(rsp.StatusCode)
	io.Copy(writer, rsp.Body) // 写入body到writer中
}

func main() {
	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		log.Println(fmt.Sprintf("收到请求: method:%s "+
			"Host:%s Path:%s", request.Method, request.Host, request.URL.Path))
		// 第一步 获取响应
		rsp, err := getRsp(request)
		if err != nil {
			log.Println(err)
			writer.WriteHeader(http.StatusBadGateway)
			return
		}
		defer rsp.Body.Close()
		// 拷贝响应头
		copyHeader(writer, rsp.Header)
		// 拷贝响应内容
		copyRspBody(writer, rsp)
	})

	http.ListenAndServe(":8080", nil)
}

远程访问时携带 proxy 配置去请求,由正向代理去访问真实的服务并将结果返回

func main() {
	proxy, _ := url.Parse("http://ip:NodePort")

	req, err := http.NewRequest("GET", "http://serviceName", nil)
	if err != nil {
		log.Fatal(err)
	}
	
	client := &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyURL(proxy),	// 代理
		},
		Timeout: time.Second * 3,
	}
	
	rsp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer rsp.Body.Close()
	b, _ := io.ReadAll(rsp.Body)
	
	fmt.Println(string(b))
}

TCP

使用 socks5 协议,客户端连代理服务并发送 socks 协议版本以及认证方式,完成客户端和服务端的安全认证,代理服务器访问目标服务并转发流量给客户端,实现了 socks5 的第三方库:go-socks5

func main() {
	conf := &socks5.Config{}
	server, err := socks5.New(conf)
	if err != nil {
		panic(err)
	}
	if err := server.ListenAndServe("tcp", "0.0.0.0:9000"); err != nil {
		panic(err)
	}
}

客户端连接 redis 示例:

func main() {
    // 使用SOCKS5创建拨号器来进行代理连接
	dialer, err := proxy.SOCKS5("tcp", "ip:NodePort", nil, proxy.Direct)
	if err != nil {
		fmt.Fprintln(os.Stderr, "proxy connection error:", err)
		os.Exit(1)
	}

	rdb := redis.NewClient(&redis.Options{
		Addr:     "redis:6379",
		Password: "",
		DB:       0,
		Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
			return dialer.Dial(network, addr)
		},
	})

	ctx := context.Background()
	rdb.Set(ctx, "name", "txl", time.Second*10)
	fmt.Println(rdb.Get(ctx, "name"))
}