89 lines
2.4 KiB
Go
89 lines
2.4 KiB
Go
package forward
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func newReverseProxy(target, kind string) (*httputil.ReverseProxy, error) {
|
|
targetURL, err := url.Parse(target)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
proxy := httputil.NewSingleHostReverseProxy(targetURL)
|
|
proxy.Transport = &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // 政务网自签证书
|
|
}
|
|
proxy.Director = func(req *http.Request) {
|
|
req.URL.Scheme = targetURL.Scheme
|
|
req.URL.Host = targetURL.Host
|
|
req.URL.Path = targetURL.Path
|
|
req.URL.RawPath = targetURL.RawPath
|
|
req.URL.RawQuery = targetURL.RawQuery
|
|
req.Host = targetURL.Host
|
|
filtered := make(http.Header)
|
|
copyAllowedHeaders(filtered, req.Header, kind)
|
|
req.Header = filtered
|
|
}
|
|
return proxy, nil
|
|
}
|
|
|
|
func handleForward(w http.ResponseWriter, r *http.Request, kind string, proxy *httputil.ReverseProxy, target, allowIPs, forwardSecret string) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
ip := clientIP(r)
|
|
if allowIPs != "" && !ipAllowed(ip, allowIPs) {
|
|
appLogf("%s | %s | %s | forbidden | not in ALLOW_IPS", time.Now().Format(time.RFC3339), ip, kind)
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
|
return
|
|
}
|
|
if forwardSecret != "" && r.Header.Get("X-Forward-Token") != forwardSecret {
|
|
appLogf("%s | %s | %s | forbidden | bad forward token", time.Now().Format(time.RFC3339), ip, kind)
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
|
return
|
|
}
|
|
start := time.Now()
|
|
sw := &statusRecorder{ResponseWriter: w, status: http.StatusOK}
|
|
proxy.ServeHTTP(sw, r)
|
|
ms := time.Since(start).Milliseconds()
|
|
line := fmt.Sprintf("%s | %s | %s | http=%d | %dms | %s", time.Now().Format(time.RFC3339), ip, kind, sw.status, ms, target)
|
|
appLogf(line)
|
|
log.Printf("forward %s %s http=%d %dms", ip, kind, sw.status, ms)
|
|
}
|
|
|
|
type statusRecorder struct {
|
|
http.ResponseWriter
|
|
status int
|
|
}
|
|
|
|
func (w *statusRecorder) WriteHeader(code int) {
|
|
w.status = code
|
|
w.ResponseWriter.WriteHeader(code)
|
|
}
|
|
|
|
func clientIP(r *http.Request) string {
|
|
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err != nil {
|
|
return r.RemoteAddr
|
|
}
|
|
return ip
|
|
}
|
|
|
|
func ipAllowed(ip, allow string) bool {
|
|
for _, part := range strings.Split(allow, ",") {
|
|
if strings.TrimSpace(part) == ip {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|