1 Star 0 Fork 0

zhuchance/kubernetes

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
https.go 10.58 KB
一键复制 编辑 原始数据 按行查看 历史
Andy Goldstein 提交于 2015-08-27 10:19 . Add goproxy test image
package goproxy
import (
"bufio"
"crypto/tls"
"errors"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync/atomic"
)
type ConnectActionLiteral int
const (
ConnectAccept = iota
ConnectReject
ConnectMitm
ConnectHijack
ConnectHTTPMitm
)
var (
OkConnect = &ConnectAction{Action: ConnectAccept, TLSConfig: TLSConfigFromCA(&GoproxyCa)}
MitmConnect = &ConnectAction{Action: ConnectMitm, TLSConfig: TLSConfigFromCA(&GoproxyCa)}
HTTPMitmConnect = &ConnectAction{Action: ConnectHTTPMitm, TLSConfig: TLSConfigFromCA(&GoproxyCa)}
RejectConnect = &ConnectAction{Action: ConnectReject, TLSConfig: TLSConfigFromCA(&GoproxyCa)}
)
type ConnectAction struct {
Action ConnectActionLiteral
Hijack func(req *http.Request, client net.Conn, ctx *ProxyCtx)
TLSConfig func(host string, ctx *ProxyCtx) (*tls.Config, error)
}
func stripPort(s string) string {
ix := strings.IndexRune(s, ':')
if ix == -1 {
return s
}
return s[:ix]
}
func (proxy *ProxyHttpServer) dial(network, addr string) (c net.Conn, err error) {
if proxy.Tr.Dial != nil {
return proxy.Tr.Dial(network, addr)
}
return net.Dial(network, addr)
}
func (proxy *ProxyHttpServer) connectDial(network, addr string) (c net.Conn, err error) {
if proxy.ConnectDial == nil {
return proxy.dial(network, addr)
}
return proxy.ConnectDial(network, addr)
}
func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request) {
ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy}
hij, ok := w.(http.Hijacker)
if !ok {
panic("httpserver does not support hijacking")
}
proxyClient, _, e := hij.Hijack()
if e != nil {
panic("Cannot hijack connection " + e.Error())
}
ctx.Logf("Running %d CONNECT handlers", len(proxy.httpsHandlers))
todo, host := OkConnect, r.URL.Host
for i, h := range proxy.httpsHandlers {
newtodo, newhost := h.HandleConnect(host, ctx)
// If found a result, break the loop immediately
if newtodo != nil {
todo, host = newtodo, newhost
ctx.Logf("on %dth handler: %v %s", i, todo, host)
break
}
}
switch todo.Action {
case ConnectAccept:
if !hasPort.MatchString(host) {
host += ":80"
}
targetSiteCon, err := proxy.connectDial("tcp", host)
if err != nil {
httpError(proxyClient, ctx, err)
return
}
ctx.Logf("Accepting CONNECT to %s", host)
proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
go copyAndClose(ctx, targetSiteCon, proxyClient)
go copyAndClose(ctx, proxyClient, targetSiteCon)
case ConnectHijack:
ctx.Logf("Hijacking CONNECT to %s", host)
proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
todo.Hijack(r, proxyClient, ctx)
case ConnectHTTPMitm:
proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
ctx.Logf("Assuming CONNECT is plain HTTP tunneling, mitm proxying it")
targetSiteCon, err := proxy.connectDial("tcp", host)
if err != nil {
ctx.Warnf("Error dialing to %s: %s", host, err.Error())
return
}
for {
client := bufio.NewReader(proxyClient)
remote := bufio.NewReader(targetSiteCon)
req, err := http.ReadRequest(client)
if err != nil && err != io.EOF {
ctx.Warnf("cannot read request of MITM HTTP client: %+#v", err)
}
if err != nil {
return
}
req, resp := proxy.filterRequest(req, ctx)
if resp == nil {
if err := req.Write(targetSiteCon); err != nil {
httpError(proxyClient, ctx, err)
return
}
resp, err = http.ReadResponse(remote, req)
if err != nil {
httpError(proxyClient, ctx, err)
return
}
}
resp = proxy.filterResponse(resp, ctx)
if err := resp.Write(proxyClient); err != nil {
httpError(proxyClient, ctx, err)
return
}
}
case ConnectMitm:
proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
ctx.Logf("Assuming CONNECT is TLS, mitm proxying it")
// this goes in a separate goroutine, so that the net/http server won't think we're
// still handling the request even after hijacking the connection. Those HTTP CONNECT
// request can take forever, and the server will be stuck when "closed".
// TODO: Allow Server.Close() mechanism to shut down this connection as nicely as possible
tlsConfig := defaultTLSConfig
if todo.TLSConfig != nil {
var err error
tlsConfig, err = todo.TLSConfig(host, ctx)
if err != nil {
httpError(proxyClient, ctx, err)
return
}
}
go func() {
//TODO: cache connections to the remote website
rawClientTls := tls.Server(proxyClient, tlsConfig)
if err := rawClientTls.Handshake(); err != nil {
ctx.Warnf("Cannot handshake client %v %v", r.Host, err)
return
}
defer rawClientTls.Close()
clientTlsReader := bufio.NewReader(rawClientTls)
for !isEof(clientTlsReader) {
req, err := http.ReadRequest(clientTlsReader)
if err != nil && err != io.EOF {
return
}
if err != nil {
ctx.Warnf("Cannot read TLS request from mitm'd client %v %v", r.Host, err)
return
}
req.RemoteAddr = r.RemoteAddr // since we're converting the request, need to carry over the original connecting IP as well
ctx.Logf("req %v", r.Host)
req.URL, err = url.Parse("https://" + r.Host + req.URL.String())
// Bug fix which goproxy fails to provide request
// information URL in the context when does HTTPS MITM
ctx.Req = req
req, resp := proxy.filterRequest(req, ctx)
if resp == nil {
if err != nil {
ctx.Warnf("Illegal URL %s", "https://"+r.Host+req.URL.Path)
return
}
removeProxyHeaders(ctx, req)
resp, err = ctx.RoundTrip(req)
if err != nil {
ctx.Warnf("Cannot read TLS response from mitm'd server %v", err)
return
}
ctx.Logf("resp %v", resp.Status)
}
resp = proxy.filterResponse(resp, ctx)
text := resp.Status
statusCode := strconv.Itoa(resp.StatusCode) + " "
if strings.HasPrefix(text, statusCode) {
text = text[len(statusCode):]
}
// always use 1.1 to support chunked encoding
if _, err := io.WriteString(rawClientTls, "HTTP/1.1"+" "+statusCode+text+"\r\n"); err != nil {
ctx.Warnf("Cannot write TLS response HTTP status from mitm'd client: %v", err)
return
}
// Since we don't know the length of resp, return chunked encoded response
// TODO: use a more reasonable scheme
resp.Header.Del("Content-Length")
resp.Header.Set("Transfer-Encoding", "chunked")
if err := resp.Header.Write(rawClientTls); err != nil {
ctx.Warnf("Cannot write TLS response header from mitm'd client: %v", err)
return
}
if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
ctx.Warnf("Cannot write TLS response header end from mitm'd client: %v", err)
return
}
chunked := newChunkedWriter(rawClientTls)
if _, err := io.Copy(chunked, resp.Body); err != nil {
ctx.Warnf("Cannot write TLS response body from mitm'd client: %v", err)
return
}
if err := chunked.Close(); err != nil {
ctx.Warnf("Cannot write TLS chunked EOF from mitm'd client: %v", err)
return
}
if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
ctx.Warnf("Cannot write TLS response chunked trailer from mitm'd client: %v", err)
return
}
}
ctx.Logf("Exiting on EOF")
}()
case ConnectReject:
if ctx.Resp != nil {
if err := ctx.Resp.Write(proxyClient); err != nil {
ctx.Warnf("Cannot write response that reject http CONNECT: %v", err)
}
}
proxyClient.Close()
}
}
func httpError(w io.WriteCloser, ctx *ProxyCtx, err error) {
if _, err := io.WriteString(w, "HTTP/1.1 502 Bad Gateway\r\n\r\n"); err != nil {
ctx.Warnf("Error responding to client: %s", err)
}
if err := w.Close(); err != nil {
ctx.Warnf("Error closing client connection: %s", err)
}
}
func copyAndClose(ctx *ProxyCtx, w, r net.Conn) {
connOk := true
if _, err := io.Copy(w, r); err != nil {
connOk = false
ctx.Warnf("Error copying to client: %s", err)
}
if err := r.Close(); err != nil && connOk {
ctx.Warnf("Error closing: %s", err)
}
}
func dialerFromEnv(proxy *ProxyHttpServer) func(network, addr string) (net.Conn, error) {
https_proxy := os.Getenv("HTTPS_PROXY")
if https_proxy == "" {
https_proxy = os.Getenv("https_proxy")
}
if https_proxy == "" {
return nil
}
return proxy.NewConnectDialToProxy(https_proxy)
}
func (proxy *ProxyHttpServer) NewConnectDialToProxy(https_proxy string) func(network, addr string) (net.Conn, error) {
u, err := url.Parse(https_proxy)
if err != nil {
return nil
}
if u.Scheme == "" || u.Scheme == "http" {
if strings.IndexRune(u.Host, ':') == -1 {
u.Host += ":80"
}
return func(network, addr string) (net.Conn, error) {
connectReq := &http.Request{
Method: "CONNECT",
URL: &url.URL{Opaque: addr},
Host: addr,
Header: make(http.Header),
}
c, err := proxy.dial(network, u.Host)
if err != nil {
return nil, err
}
connectReq.Write(c)
// Read response.
// Okay to use and discard buffered reader here, because
// TLS server will not speak until spoken to.
br := bufio.NewReader(c)
resp, err := http.ReadResponse(br, connectReq)
if err != nil {
c.Close()
return nil, err
}
if resp.StatusCode != 200 {
resp, _ := ioutil.ReadAll(resp.Body)
c.Close()
return nil, errors.New("proxy refused connection" + string(resp))
}
return c, nil
}
}
if u.Scheme == "https" {
if strings.IndexRune(u.Host, ':') == -1 {
u.Host += ":443"
}
return func(network, addr string) (net.Conn, error) {
c, err := proxy.dial(network, u.Host)
if err != nil {
return nil, err
}
c = tls.Client(c, proxy.Tr.TLSClientConfig)
connectReq := &http.Request{
Method: "CONNECT",
URL: &url.URL{Opaque: addr},
Host: addr,
Header: make(http.Header),
}
connectReq.Write(c)
// Read response.
// Okay to use and discard buffered reader here, because
// TLS server will not speak until spoken to.
br := bufio.NewReader(c)
resp, err := http.ReadResponse(br, connectReq)
if err != nil {
c.Close()
return nil, err
}
if resp.StatusCode != 200 {
body, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 500))
resp.Body.Close()
c.Close()
return nil, errors.New("proxy refused connection" + string(body))
}
return c, nil
}
}
return nil
}
func TLSConfigFromCA(ca *tls.Certificate) func(host string, ctx *ProxyCtx) (*tls.Config, error) {
return func(host string, ctx *ProxyCtx) (*tls.Config, error) {
config := *defaultTLSConfig
ctx.Logf("signing for %s", stripPort(host))
cert, err := signHost(*ca, []string{stripPort(host)})
if err != nil {
ctx.Warnf("Cannot sign host certificate with provided CA: %s", err)
return nil, err
}
config.Certificates = append(config.Certificates, cert)
return &config, nil
}
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/meoom/kubernetes.git
git@gitee.com:meoom/kubernetes.git
meoom
kubernetes
kubernetes
v1.2.0-alpha.7

搜索帮助

Cb406eda 1850385 E526c682 1850385