mirror of
https://github.com/wikiZ/RedGuard
synced 2026-06-08 18:18:32 +00:00
215 lines
7.1 KiB
Go
215 lines
7.1 KiB
Go
/**
|
|
* @Author 风起
|
|
* @contact: onlyzaliks@gmail.com
|
|
* @File: ProxyHandler.go
|
|
* @Time: 2022/5/5 16:53
|
|
**/
|
|
|
|
package core
|
|
|
|
import (
|
|
"io"
|
|
"math/rand"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"RedGuard/lib"
|
|
|
|
"github.com/sleeyax/ja3rp/crypto/tls"
|
|
"github.com/sleeyax/ja3rp/net/http"
|
|
"github.com/sleeyax/ja3rp/net/http/httputil"
|
|
"github.com/wxnacy/wgo/arrays"
|
|
)
|
|
|
|
var (
|
|
ip string // HTTP remote IP
|
|
redirectURL string // Proxy redirect URL
|
|
_addressArray []string // By request list
|
|
_startUp sync.Mutex // mutex lock
|
|
_hostProxy = make(map[string]*httputil.ReverseProxy) // Used to cache httputil.ReverseProxy
|
|
)
|
|
|
|
type baseHandle struct{}
|
|
|
|
func NewProxy(proxyURL string, dropType bool, delHeader string) (*httputil.ReverseProxy, error) {
|
|
destinationURL, err := url.Parse(proxyURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
proxy := httputil.NewSingleHostReverseProxy(destinationURL)
|
|
// dropType Check whether the response to the request is changed
|
|
proxy.ModifyResponse = modifyResponse(dropType, delHeader) // Modifies the response to the request
|
|
return proxy, nil
|
|
}
|
|
|
|
func modifyResponse(drop bool, delHeader string) func(*http.Response) error {
|
|
return func(resp *http.Response) error {
|
|
defer func(Body io.ReadCloser) {
|
|
logger.Warningf("[RESPONSE] HTTP %s, length: %d", resp.Status, resp.ContentLength)
|
|
delHeaderList := strings.Split(delHeader, ",")
|
|
if delHeader != "*" && delHeaderList != nil {
|
|
// Delete the header field specified in the RG response type
|
|
for _, header := range delHeaderList {
|
|
resp.Header.Del(header)
|
|
}
|
|
}
|
|
if drop {
|
|
// DROP Request
|
|
logger.Alertf("[DROP] Source IP: %s", resp.Request.RemoteAddr)
|
|
_ = Body.Close() // Direct shutdown response
|
|
return
|
|
}
|
|
}(resp.Body)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// ProxyRequestHandler A reverse proxy processes HTTP requests
|
|
func (h *baseHandle) ServeHTTP(write http.ResponseWriter, req *http.Request) {
|
|
var (
|
|
host = &req.Host
|
|
cfg = lib.InitConfig() // config file object
|
|
// Obtain the domain name and target map
|
|
hostTarget = lib.JsonToMap(lib.ReadConfig(
|
|
"proxy",
|
|
"HostTarget",
|
|
cfg),
|
|
)
|
|
// Read the configuration file to check whether DROP is enabled
|
|
dropAction = lib.ReadConfig("proxy", "drop_action", cfg)
|
|
// Read the Edge Host Communication Domain
|
|
edgeHost = lib.ReadConfig("proxy", "EdgeHost", cfg)
|
|
// Read the Edge Host Proxy Target
|
|
edgeTarget = lib.ReadConfig("proxy", "EdgeTarget", cfg)
|
|
// Customize the header to be deleted
|
|
delHeader = lib.ReadConfig("proxy", "DelHeader", cfg)
|
|
)
|
|
var isDrop bool
|
|
var proxy *httputil.ReverseProxy
|
|
req.RemoteAddr = lib.ConvertIP(req.RemoteAddr)
|
|
// Determine the URL to be redirected to
|
|
redirectURL = lib.ReadConfig("proxy", "Redirect", cfg)
|
|
// Obtaining the real IP address
|
|
if req.Header.Get("X-Forwarded-For") != "" {
|
|
req.RemoteAddr = req.Header.Get("X-Forwarded-For")
|
|
}
|
|
// Check whether Edge HOST Domain is enabled
|
|
if edgeHost != "*" && edgeTarget != "*" && edgeHost != "" && edgeTarget != "" {
|
|
// Replace request HOST
|
|
hostTarget[edgeHost], req.Host = hostTarget[*host], edgeHost
|
|
delete(hostTarget, edgeTarget)
|
|
}
|
|
// Set the forwarding header XFF so that C2 obtains the online real IP address
|
|
req.Header.Set("X-Forwarded-For", req.RemoteAddr)
|
|
// Check whether the host is verified
|
|
if IPHash := lib.EncodeMD5(req.JA3); arrays.ContainsString(_addressArray, req.JA3) == -1 {
|
|
logger.Noticef("JA3 FingerPrint: %s", IPHash)
|
|
logger.Noticef("[REQUEST] Host:%s", req.Host)
|
|
logger.Noticef("[REQUEST] %s %s", req.Method, req.RequestURI)
|
|
logger.Noticef("[REQUEST] %s - %s", req.RemoteAddr, req.UserAgent())
|
|
// Request filtering method
|
|
if !ProxyFilterManger(req) {
|
|
goto LOOK // Redirect to the specified site
|
|
}
|
|
_addressArray = append(_addressArray, IPHash) // Add to the list after verification for the first time
|
|
}
|
|
// Fetch directly from cache
|
|
if fn, ok := _hostProxy[*host]; ok {
|
|
fn.ServeHTTP(write, req)
|
|
return
|
|
}
|
|
|
|
// Check whether the domain name is in the whitelist
|
|
if target, ok := hostTarget[*host]; ok {
|
|
proxy, err := NewProxy(target, false, delHeader)
|
|
if err != nil {
|
|
logger.Error("Proxy Exception")
|
|
}
|
|
_hostProxy[*host] = proxy // Into the cache
|
|
proxy.ServeHTTP(write, req)
|
|
return
|
|
}
|
|
|
|
LOOK:
|
|
req.URL.Path = "/" // Url rewriting
|
|
// condition is not met, the element is removed from the slice
|
|
// Output The URL of each request for this IP address
|
|
if len(_addressArray) > 0 {
|
|
_addressArray = _addressArray[:len(_addressArray)-1]
|
|
}
|
|
// dropAction Select the reverse proxy interception mode
|
|
switch dropAction {
|
|
// redirect
|
|
case "redirect":
|
|
http.Redirect(write, req, redirectURL, http.StatusTemporaryRedirect)
|
|
goto REDIRECT
|
|
// reset Turning off the HTTP response
|
|
case "reset":
|
|
isDrop = true
|
|
// proxy Hijacking target requests response information
|
|
case "proxy":
|
|
break
|
|
}
|
|
// Determine whether to redirect or intercept intercepted traffic
|
|
proxy, _ = NewProxy(redirectURL, isDrop, delHeader)
|
|
// Unauthorized access is redirected to the specified URL
|
|
proxy.ServeHTTP(write, req)
|
|
REDIRECT:
|
|
// REDIRECT Request
|
|
logger.Alertf("[%s] Source IP: %s -> Destination Site: %s", strings.ToUpper(dropAction), req.RemoteAddr, redirectURL)
|
|
}
|
|
|
|
// ProxyManger Initialize the reverse proxy and pass in the address of the real back-end service
|
|
// handle all requests to your server using the proxy
|
|
// @param action string reverse proxy listening port type
|
|
// @param port string reverse proxy listening port
|
|
// @param pattern string pattern associated with the listening port type
|
|
func ProxyManger(action, port, pattern string) {
|
|
var (
|
|
handle = &baseHandle{}
|
|
config = &tls.Config{} // Example Initialize TLS config
|
|
_isHasCert, _ = strconv.ParseBool(lib.ReadConfig("cert", "HasCert", lib.InitConfig()))
|
|
)
|
|
_startUp.Lock() // 我知道这可能是一个bug哈哈哈,但是它可能不影响什么,就不修了。
|
|
http.Handle(pattern, handle)
|
|
// Cancels the validity verification of the destination TLS certificate
|
|
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{
|
|
InsecureSkipVerify: true,
|
|
}
|
|
// Disable client connection caching to connection pools
|
|
http.DefaultTransport.(*http.Transport).DisableKeepAlives = true
|
|
rand.Seed(time.Now().UnixNano())
|
|
if !_isHasCert {
|
|
config = &tls.Config{
|
|
// JARM FingerPrint Random
|
|
CipherSuites: lib.MicsSlice([]uint16{
|
|
0x0005, 0x000a, 0x002f,
|
|
0x0035, 0x003c, 0x009c,
|
|
0x009d, 0xc011, 0xc012,
|
|
0xc013, 0xc014, 0xc027,
|
|
0xc02f, 0xc030, 0xcca8,
|
|
}, rand.Intn(2)+1),
|
|
}
|
|
}
|
|
server := &http.Server{
|
|
Addr: port, // proxy port
|
|
Handler: handle, // Cache structure
|
|
TLSConfig: config, // TLS Server Config
|
|
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 1),
|
|
}
|
|
logger.Warningf("Proxy Listen Port %s (%s)", port, action)
|
|
_startUp.Unlock()
|
|
if action == "HTTPS" {
|
|
// HTTPS reverse proxy
|
|
_ = server.ListenAndServeTLS(
|
|
"cert-rsa/ca.crt", // rsa cert crt
|
|
"cert-rsa/ca.key", // rsa cert key
|
|
)
|
|
} else {
|
|
_ = server.ListenAndServe() // HTTP reverse proxy
|
|
}
|
|
}
|