From bb6e4e2568b8cfa0e6e8960ce90623a5def92d90 Mon Sep 17 00:00:00 2001 From: bagas Date: Sat, 8 Feb 2025 16:53:32 +0700 Subject: [PATCH] feat: http/1.x translation to http/2.0 --- certs/cert.pem | 26 ++++++++ certs/privkey.pem | 28 ++++++++ go.mod | 6 +- go.sum | 4 ++ http/https.go | 156 ++++++++++++++++++++++++++++++++++----------- public/output.css | 19 ------ server/server.go | 2 +- session/handler.go | 32 ++++++++++ 8 files changed, 216 insertions(+), 57 deletions(-) create mode 100644 certs/privkey.pem diff --git a/certs/cert.pem b/certs/cert.pem index e69de29..0e2136d 100644 --- a/certs/cert.pem +++ b/certs/cert.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEWzCCA0OgAwIBAgIUDYTdEDHwVznxV/qnn0/WVlHNMZAwDQYJKoZIhvcNAQEL +BQAwgZYxCzAJBgNVBAYTAklEMQ8wDQYDVQQIDAZCYW50ZW4xGjAYBgNVBAcMEVRh +bmdlcmFuZyBTZWxhdGFuMRIwEAYDVQQKDAlGb3NzeSBMVFMxDjAMBgNVBAsMBUZv +c3N5MRQwEgYDVQQDDAtmb3NzeS5teS5pZDEgMB4GCSqGSIb3DQEJARYRYmFnYXNA +Zm9zc3kubXkuaWQwHhcNMjUwMjA3MTYyMTU1WhcNMjYwMjA3MTYyMTU1WjCBljEL +MAkGA1UEBhMCSUQxDzANBgNVBAgMBkJhbnRlbjEaMBgGA1UEBwwRVGFuZ2VyYW5n +IFNlbGF0YW4xEjAQBgNVBAoMCUZvc3N5IExUUzEOMAwGA1UECwwFRm9zc3kxFDAS +BgNVBAMMC2Zvc3N5Lm15LmlkMSAwHgYJKoZIhvcNAQkBFhFiYWdhc0Bmb3NzeS5t +eS5pZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMU3pCA0eP+VR2CA ++p3p0BgKw33xCKQmQx52jnqNbvwW4NMMDc3mS6a+wb6QB6v0WLGMI1g22TtJPatx +7dcy4PCSOCV9xJ2Yfq5HlQgDHYoyE+juy4/pGlMjo45thJ0yI8zOSzaz2esosP22 +XkfFwj7oVMXXPIY6UovcAlGU+DFtwVrNa76/esUwJs+7M3tBubjkpcal0wXR+SIX +3fmw5v0YzKV8qLGMGOvX6+OyLQCx4r9gB0d+WOrT6EfrPfAzo07NKjzWG9aWl1rk +Q+h0i38hke2MTFxId7za57L+NlXRjLo3ESbBF0hjYquTQOG3jx/UvWs+I1NEfsdu +vj8beiMCAwEAAaOBnjCBmzAfBgNVHSMEGDAWgBT6nj69+I+GSdgScjfOqnVFzX7G +aTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAs +BgNVHREEJTAjgglsb2NhbGhvc3SCCTEyNy4wLjAuMYILKi5sb2NhbGhvc3QwHQYD +VR0OBBYEFD9ci20pAUTRLVWxvVXjfE2GKwSAMA0GCSqGSIb3DQEBCwUAA4IBAQAT +rkjU+GzQy9B3/nd/79N7ozK80ygzmnRnj3ou/bbqUHOIYQQgKM1cuN6zh57ovRh/ +u45s6pZUOUVN59POFUCvqUiOgsDkY/auXGbKtzzqzoZABuvm85ySd6zurOOx8tA5 +e7ArX1ppy3LgzSb+cXANvzYC9bCwp70w2YylMFhwHBAp5TXRVqsqG6jujD8GMLoS +zDSDx8M6o8gqWmPQve7Saim9mgLJUvvCYBzvowuNjzZrJfAeGoIfLV127xCQiylm +fzUQ1Ac6udldWm9scA32nteSQMWg2d1nW3RG1nRondp13WUkgGQ490/c97D3MKLt +D1HB5dLIYkRVHVhCKHT1 +-----END CERTIFICATE----- diff --git a/certs/privkey.pem b/certs/privkey.pem new file mode 100644 index 0000000..bedf787 --- /dev/null +++ b/certs/privkey.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDFN6QgNHj/lUdg +gPqd6dAYCsN98QikJkMedo56jW78FuDTDA3N5kumvsG+kAer9FixjCNYNtk7ST2r +ce3XMuDwkjglfcSdmH6uR5UIAx2KMhPo7suP6RpTI6OObYSdMiPMzks2s9nrKLD9 +tl5HxcI+6FTF1zyGOlKL3AJRlPgxbcFazWu+v3rFMCbPuzN7Qbm45KXGpdMF0fki +F935sOb9GMylfKixjBjr1+vjsi0AseK/YAdHfljq0+hH6z3wM6NOzSo81hvWlpda +5EPodIt/IZHtjExcSHe82uey/jZV0Yy6NxEmwRdIY2Krk0Dht48f1L1rPiNTRH7H +br4/G3ojAgMBAAECggEAATiJ23ZhS1++squ87jsgAfR+TYP8Kpv408u/43Ig5KgC +ZnwPUWqvP2e0TLwyhMKwXxHMt2nITxQlSnyCXU/5nk07BYxkqhiwEni4Xo86YK+H +rQXEnKFaSHdF6dNNIo+VZiark4adS9XgJp0fsn0LnON7GhBt72MvPW6auxH1HJIC +rcjnzJu/KzTVrId9QNsEDl9cNRlHXuPSfohdq2o39PBKGDeDEDTvP9wHtasEF6oe +uj1OH5fAxiXptT0Ln0Y8QeFe8R8Odul5mUXCgMOkKmHZPDxTq9ldCuPjrz15JfnS +T3MecaWdvChpIP2WybjLstJy9qXTl0fhRkpJWpaVbQKBgQDu9puq64vZn0jak7ns +bFbKPemFw7tiiwwnqyzqCTRddw1vETJN/MgpRWvc7d1Cvzs2+Z2kn0Cgpdw/30Ej +VRHiE/d1rj+u33F6vGBUBccRue1t0UdFMFF/YnlNDRRnljQgx4N3mezzNE2RikR8 +jVp+jvWTSTgf3y4Hip1ffCSNtQKBgQDTRx1huSzUOed/36YLdnxP+AlV3L+c/EKU +GItKZk2bqRlqgs6wcsNlVjveb9o9j6M5pg/lh5HaqJyhj8DYVnEdmXfcQJiUfIPh +5802asIXQVBmL9rTMR34wsr/lzzK3k0KORqFcUdf2fDI/UQNpVUJvWQQY8fl2sVo +zmp88JAPdwKBgQCLC6fsvn5ztMF5nffTX/7oUzosgYXpgyshcfMCgzSbJgkFFaaF +xo7ZpPFsbmQO0KMuC/T0s02xrJEKAWgvnPJ48FFPgoK/yHiJiE8s1OfOordK7Tlh +QwpI6w3WDcRPuhC++hi/YSuFIGv6QdA0ATQk7B5tA2/K69wmuztzMhM6+QKBgH5E +LwQbRfZj0L20bKjHHA4y32loLz/j5upZLM2/DDyuN9lW6a28OJiUi90pHdXSxSsL +2s5DUmDKiiloH0lrh9i3wlFobYe4Tp0xCoyuCucZCrK3gODcptvnlqhfu15GsuYc +MIR1qcFYH7YO3qAFIiha/rVo3Ku7LmWvjyayInaLAoGAZ/dELEAcyhImf1HvtmBT +qNg4uI6t/2fvHdoAeQkkGjEDWFTGEaMp0cJEbETPD68TwNh5dL/xUJibTwMbK+m5 +rdIA2oTZMkFtWubIvMCrD/J6E+8Pzl+eK+0C0axO/I29S4veORNGWvCtqdFICDgZ +CluJ0NZFeMH8g8tgfI9HnHc= +-----END PRIVATE KEY----- diff --git a/go.mod b/go.mod index d00f01c..26c83ca 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,10 @@ require ( github.com/a-h/templ v0.3.833 github.com/joho/godotenv v1.5.1 golang.org/x/crypto v0.32.0 + golang.org/x/net v0.33.0 ) -require golang.org/x/sys v0.29.0 // indirect +require ( + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect +) diff --git a/go.sum b/go.sum index aadc42e..83328d3 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,11 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= diff --git a/http/https.go b/http/https.go index 3011ecf..0385f17 100644 --- a/http/https.go +++ b/http/https.go @@ -1,61 +1,44 @@ package httpServer import ( - "bufio" "crypto/tls" "fmt" + "golang.org/x/net/http2" "io" "log" - "net" "net/http" + "os" "strings" "tunnel_pls/session" "tunnel_pls/utils" + indexView "tunnel_pls/view/index" ) func ListenTLS(config *tls.Config) { - server, err := tls.Listen("tcp", ":443", config) - if err != nil { - return + server := &http.Server{ + Addr: ":443", + TLSConfig: config, + Handler: http.HandlerFunc(handleRequestTLS), } - if err != nil { - log.Fatal(err) - return - } + http2.ConfigureServer(server, &http2.Server{}) - defer server.Close() - log.Println("Listening on :443") - for { - conn, err := server.Accept() - if err != nil { - log.Fatal(err) - return - } - - go handleRequestTLS(conn) - } + fmt.Println("Listening on :8443 (HTTP/2 over TLS)") + log.Fatal(server.ListenAndServeTLS("", "")) } -func handleRequestTLS(conn net.Conn) { - defer conn.Close() - var rawRequest string - - reader := bufio.NewReader(conn) - r, err := http.ReadRequest(reader) +func handleRequestTLS(w http.ResponseWriter, r *http.Request) { + _, err := io.ReadAll(r.Body) if err != nil { - fmt.Println("Error reading request:", err) + http.Error(w, "Failed to read request", http.StatusInternalServerError) return } + defer r.Body.Close() - writer := &tcpResponseWriter{ - conn: conn, - header: make(http.Header), - status: http.StatusOK, - } + var rawRequest string if r.Host == utils.Getenv("domain") { - router.ServeHTTP(writer, r) + TLSRouter().ServeHTTP(w, r) return } @@ -71,7 +54,7 @@ func handleRequestTLS(conn net.Conn) { return } - rawRequest += fmt.Sprintf("%s %s %s\r\n", r.Method, r.URL.RequestURI(), r.Proto) + rawRequest += fmt.Sprintf("%s %s %s\r\n", r.Method, r.URL.RequestURI(), "HTTP/1.1") rawRequest += fmt.Sprintf("Host: %s\r\n", r.Host) for k, v := range r.Header { @@ -90,6 +73,107 @@ func handleRequestTLS(conn net.Conn) { payload := []byte(rawRequest) - host, originPort := session.ParseAddr(conn.RemoteAddr().String()) - sshSession.GetForwardedConnection(conn, host, sshSession.Connection, payload, originPort, 80, r.RequestURI, r.Method, r.Proto) + host, originPort := session.ParseAddr(r.RemoteAddr) + response := sshSession.GetForwardedConnectionTLS(host, sshSession.Connection, payload, originPort, 80, r.RequestURI, r.Method, r.Proto) + + forbiddenHeaders := map[string]bool{ + "connection": true, + "transfer-encoding": true, + "upgrade": true, + "keep-alive": true, + } + + for k, v := range response.Header { + k = strings.ToLower(k) + + if forbiddenHeaders[k] { + continue + } + + if k == ":status" || k == ":method" || k == ":path" || k == ":authority" { + continue + } + + w.Header().Set(k, v[0]) + } + + io.Copy(w, response.Body) + return } + +func TLSRouter() *http.ServeMux { + handler := http.NewServeMux() + handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + indexView.Main("Main Page", utils.Getenv("domain")).Render(r.Context(), w) + return + }) + + handler.HandleFunc("/public/output.css", func(w http.ResponseWriter, r *http.Request) { + open, err := os.Open("public/output.css") + if err != nil { + return + } + defer open.Close() + w.Header().Set("Content-Type", "text/css; charset=utf-8") + io.Copy(w, open) + return + }) + return handler +} + +//func handleRequestTLS(conn net.Conn) { +// defer conn.Close() +// var rawRequest string +// +// reader := bufio.NewReader(conn) +// r, err := http.ReadRequest(reader) +// if err != nil { +// fmt.Println("Error reading request:", err) +// return +// } +// +// writer := &tcpResponseWriter{ +// conn: conn, +// header: make(http.Header), +// status: http.StatusOK, +// } +// +// if r.Host == utils.Getenv("domain") { +// router.ServeHTTP(writer, r) +// return +// } +// +// slug := strings.Split(r.Host, ".")[0] +// if slug == "" { +// fmt.Println("Error parsing slug: ", r.Host) +// return +// } +// +// sshSession, ok := session.Clients[slug] +// if !ok { +// fmt.Println("Error finding ssh session: ", slug) +// return +// } +// +// rawRequest += fmt.Sprintf("%s %s %s\r\n", r.Method, r.URL.RequestURI(), r.Proto) +// rawRequest += fmt.Sprintf("Host: %s\r\n", r.Host) +// +// for k, v := range r.Header { +// rawRequest += fmt.Sprintf("%s: %s\r\n", k, v[0]) +// } +// rawRequest += "\r\n" +// +// if r.Body != nil { +// body, err := io.ReadAll(r.Body) +// if err != nil { +// log.Println("Error reading request body:", err) +// } else { +// rawRequest += string(body) +// } +// } +// +// payload := []byte(rawRequest) +// +// host, originPort := session.ParseAddr(conn.RemoteAddr().String()) +// sshSession.GetForwardedConnection(conn, host, sshSession.Connection, payload, originPort, 80, r.RequestURI, r.Method, r.Proto) +//} diff --git a/public/output.css b/public/output.css index ff2adb7..7cfe1e7 100644 --- a/public/output.css +++ b/public/output.css @@ -703,15 +703,6 @@ video { border-radius: 0.5rem; } -.border { - border-width: 1px; -} - -.border-white { - --tw-border-opacity: 1; - border-color: rgb(255 255 255 / var(--tw-border-opacity, 1)); -} - .bg-blue-500 { --tw-bg-opacity: 1; background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1)); @@ -843,16 +834,6 @@ video { background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1)); } -.hover\:bg-white:hover { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1)); -} - -.hover\:text-gray-900:hover { - --tw-text-opacity: 1; - color: rgb(17 24 39 / var(--tw-text-opacity, 1)); -} - .hover\:text-white:hover { --tw-text-opacity: 1; color: rgb(255 255 255 / var(--tw-text-opacity, 1)); diff --git a/server/server.go b/server/server.go index 57c8278..1b20409 100644 --- a/server/server.go +++ b/server/server.go @@ -30,7 +30,7 @@ func NewServer(config ssh.ServerConfig) *Server { log.Fatal("Failed to load key pair:", err) } - tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}} + tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}, NextProtos: []string{"h2"}} go httpServer.ListenTLS(tlsConfig) } return &Server{ diff --git a/session/handler.go b/session/handler.go index 8ca274d..c861c30 100644 --- a/session/handler.go +++ b/session/handler.go @@ -10,6 +10,7 @@ import ( "io" "log" "net" + "net/http" "strconv" "time" "tunnel_pls/proto" @@ -293,6 +294,37 @@ func (s *Session) GetForwardedConnection(conn net.Conn, host string, sshConn *ss }() } +func (s *Session) GetForwardedConnectionTLS(host string, sshConn *ssh.ServerConn, payload []byte, originPort, port uint32, path, method, proto string) *http.Response { + channelPayload := createForwardedTCPIPPayload(host, originPort, port) + channel, reqs, err := sshConn.OpenChannel("forwarded-tcpip", channelPayload) + if err != nil { + log.Printf("Failed to open forwarded-tcpip channel: %v", err) + return nil + } + defer channel.Close() + + initalPayload := bytes.NewReader(payload) + io.Copy(channel, initalPayload) + + go func() { + for req := range reqs { + req.Reply(false, nil) + } + }() + + reader := bufio.NewReader(channel) + _, err = reader.Peek(1) + if err == io.EOF { + s.ConnChannels[0].Write([]byte("Could not forward request to the tunnel addr\r\n")) + return nil + } else { + s.ConnChannels[0].Write([]byte(fmt.Sprintf("\033[32m %s -- [%s] \"%s %s %s\" \r\n \033[0m", host, time.Now().Format("02/Jan/2006 15:04:05"), method, path, proto))) + response, _ := http.ReadResponse(reader, nil) + return response + } + +} + func writeSSHString(buffer *bytes.Buffer, str string) { binary.Write(buffer, binary.BigEndian, uint32(len(str))) buffer.WriteString(str)