Updates v2.6.1

+ Added reverse proxy TLS skip verification
+ Added basic auth
+ Edit proxy settings
+ Whitelist
+ TCP Proxy (experimental)
+ Info (Utilities page)
This commit is contained in:
Toby Chui
2023-05-31 22:22:47 +08:00
parent 5952a1b55f
commit 20fd8e9a49
42 changed files with 87636 additions and 1165 deletions

View File

@@ -25,6 +25,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
/*
General Access Check
*/
//Check if this ip is in blacklist
clientIpAddr := geodb.GetRequesterIP(r)
if h.Parent.Option.GeodbStore.IsBlacklisted(clientIpAddr) {
@@ -40,6 +41,20 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
//Check if this ip is in whitelist
if !h.Parent.Option.GeodbStore.IsWhitelisted(clientIpAddr) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusForbidden)
template, err := os.ReadFile("./web/forbidden.html")
if err != nil {
w.Write([]byte("403 - Forbidden"))
} else {
w.Write(template)
}
h.logRequest(r, false, 403, "whitelist", "")
return
}
/*
Redirection Routing
*/

View File

@@ -242,10 +242,44 @@ func (router *Router) AddVirtualDirectoryProxyService(options *VdirOptions) erro
router.ProxyEndpoints.Store(options.RootName, &endpointObject)
log.Println("Adding Proxy Rule: ", options.RootName+" to "+domain)
log.Println("Registered Proxy Rule: ", options.RootName+" to "+domain)
return nil
}
/*
Load routing from RP
*/
func (router *Router) LoadProxy(ptype string, key string) (*ProxyEndpoint, error) {
if ptype == "vdir" {
proxy, ok := router.ProxyEndpoints.Load(key)
if !ok {
return nil, errors.New("target proxy not found")
}
return proxy.(*ProxyEndpoint), nil
} else if ptype == "subd" {
proxy, ok := router.SubdomainEndpoint.Load(key)
if !ok {
return nil, errors.New("target proxy not found")
}
return proxy.(*ProxyEndpoint), nil
}
return nil, errors.New("unsupported ptype")
}
/*
Save routing from RP
*/
func (router *Router) SaveProxy(ptype string, key string, newConfig *ProxyEndpoint) {
if ptype == "vdir" {
router.ProxyEndpoints.Store(key, newConfig)
} else if ptype == "subd" {
router.SubdomainEndpoint.Store(key, newConfig)
}
}
/*
Remove routing from RP
*/

View File

@@ -75,7 +75,7 @@ func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request,
u, _ = url.Parse("wss://" + wsRedirectionEndpoint + requestURL)
}
h.logRequest(r, true, 101, "subdomain-websocket", target.Domain)
wspHandler := websocketproxy.NewProxy(u)
wspHandler := websocketproxy.NewProxy(u, target.SkipCertValidations)
wspHandler.ServeHTTP(w, r)
return
}
@@ -128,7 +128,7 @@ func (h *ProxyHandler) proxyRequest(w http.ResponseWriter, r *http.Request, targ
u, _ = url.Parse("wss://" + wsRedirectionEndpoint + r.URL.String())
}
h.logRequest(r, true, 101, "vdir-websocket", target.Domain)
wspHandler := websocketproxy.NewProxy(u)
wspHandler := websocketproxy.NewProxy(u, target.SkipCertValidations)
wspHandler.ServeHTTP(w, r)
return
}

View File

@@ -3,7 +3,6 @@ package geodb
import (
_ "embed"
"log"
"net"
"net/http"
"strings"
@@ -11,14 +10,23 @@ import (
)
//go:embed geoipv4.csv
var geoipv4 []byte //Original embedded csv file
var geoipv4 []byte //Geodb dataset for ipv4
//go:embed geoipv6.csv
var geoipv6 []byte //Geodb dataset for ipv6
type Store struct {
Enabled bool
geodb [][]string //Parsed geodb list
BlacklistEnabled bool
WhitelistEnabled bool
geodb [][]string //Parsed geodb list
geodbIpv6 [][]string //Parsed geodb list for ipv6
geotrie *trie
geotrieIpv6 *trie
//geoipCache sync.Map
geotrie *trie
sysdb *database.Database
sysdb *database.Database
}
type CountryInfo struct {
@@ -32,7 +40,13 @@ func NewGeoDb(sysdb *database.Database) (*Store, error) {
return nil, err
}
parsedGeoDataIpv6, err := parseCSV(geoipv6)
if err != nil {
return nil, err
}
blacklistEnabled := false
whitelistEnabled := false
if sysdb != nil {
err = sysdb.NewTable("blacklist-cn")
if err != nil {
@@ -44,27 +58,46 @@ func NewGeoDb(sysdb *database.Database) (*Store, error) {
return nil, err
}
err = sysdb.NewTable("blacklist")
err = sysdb.NewTable("whitelist-cn")
if err != nil {
return nil, err
}
sysdb.Read("blacklist", "enabled", &blacklistEnabled)
err = sysdb.NewTable("whitelist-ip")
if err != nil {
return nil, err
}
err = sysdb.NewTable("blackwhitelist")
if err != nil {
return nil, err
}
sysdb.Read("blackwhitelist", "blacklistEnabled", &blacklistEnabled)
sysdb.Read("blackwhitelist", "whitelistEnabled", &whitelistEnabled)
} else {
log.Println("Database pointer set to nil: Entering debug mode")
}
return &Store{
Enabled: blacklistEnabled,
geodb: parsedGeoData,
//geoipCache: sync.Map{},
geotrie: constrctTrieTree(parsedGeoData),
sysdb: sysdb,
BlacklistEnabled: blacklistEnabled,
WhitelistEnabled: whitelistEnabled,
geodb: parsedGeoData,
geotrie: constrctTrieTree(parsedGeoData),
geodbIpv6: parsedGeoDataIpv6,
geotrieIpv6: constrctTrieTree(parsedGeoDataIpv6),
sysdb: sysdb,
}, nil
}
func (s *Store) ToggleBlacklist(enabled bool) {
s.sysdb.Write("blacklist", "enabled", enabled)
s.Enabled = enabled
s.sysdb.Write("blackwhitelist", "blacklistEnabled", enabled)
s.BlacklistEnabled = enabled
}
func (s *Store) ToggleWhitelist(enabled bool) {
s.sysdb.Write("blackwhitelist", "whitelistEnabled", enabled)
s.WhitelistEnabled = enabled
}
func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error) {
@@ -79,6 +112,10 @@ func (s *Store) Close() {
}
/*
Country code based black / white list
*/
func (s *Store) AddCountryCodeToBlackList(countryCode string) {
countryCode = strings.ToLower(countryCode)
s.sysdb.Write("blacklist-cn", countryCode, true)
@@ -89,6 +126,16 @@ func (s *Store) RemoveCountryCodeFromBlackList(countryCode string) {
s.sysdb.Delete("blacklist-cn", countryCode)
}
func (s *Store) AddCountryCodeToWhitelist(countryCode string) {
countryCode = strings.ToLower(countryCode)
s.sysdb.Write("whitelist-cn", countryCode, true)
}
func (s *Store) RemoveCountryCodeFromWhitelist(countryCode string) {
countryCode = strings.ToLower(countryCode)
s.sysdb.Delete("whitelist-cn", countryCode)
}
func (s *Store) IsCountryCodeBlacklisted(countryCode string) bool {
countryCode = strings.ToLower(countryCode)
var isBlacklisted bool = false
@@ -96,6 +143,13 @@ func (s *Store) IsCountryCodeBlacklisted(countryCode string) bool {
return isBlacklisted
}
func (s *Store) IsCountryCodeWhitelisted(countryCode string) bool {
countryCode = strings.ToLower(countryCode)
var isWhitelisted bool = false
s.sysdb.Read("whitelist-cn", countryCode, &isWhitelisted)
return isWhitelisted
}
func (s *Store) GetAllBlacklistedCountryCode() []string {
bannedCountryCodes := []string{}
entries, err := s.sysdb.ListTable("blacklist-cn")
@@ -110,6 +164,24 @@ func (s *Store) GetAllBlacklistedCountryCode() []string {
return bannedCountryCodes
}
func (s *Store) GetAllWhitelistedCountryCode() []string {
whitelistedCountryCode := []string{}
entries, err := s.sysdb.ListTable("whitelist-cn")
if err != nil {
return whitelistedCountryCode
}
for _, keypairs := range entries {
ip := string(keypairs[0])
whitelistedCountryCode = append(whitelistedCountryCode, ip)
}
return whitelistedCountryCode
}
/*
IP based black / whitelist
*/
func (s *Store) AddIPToBlackList(ipAddr string) {
s.sysdb.Write("blacklist-ip", ipAddr, true)
}
@@ -118,6 +190,14 @@ func (s *Store) RemoveIPFromBlackList(ipAddr string) {
s.sysdb.Delete("blacklist-ip", ipAddr)
}
func (s *Store) AddIPToWhiteList(ipAddr string) {
s.sysdb.Write("whitelist-ip", ipAddr, true)
}
func (s *Store) RemoveIPFromWhiteList(ipAddr string) {
s.sysdb.Delete("whitelist-ip", ipAddr)
}
func (s *Store) IsIPBlacklisted(ipAddr string) bool {
var isBlacklisted bool = false
s.sysdb.Read("blacklist-ip", ipAddr, &isBlacklisted)
@@ -142,6 +222,30 @@ func (s *Store) IsIPBlacklisted(ipAddr string) bool {
return false
}
func (s *Store) IsIPWhitelisted(ipAddr string) bool {
var isBlacklisted bool = false
s.sysdb.Read("whitelist-ip", ipAddr, &isBlacklisted)
if isBlacklisted {
return true
}
//Check for IP wildcard and CIRD rules
AllBlacklistedIps := s.GetAllBlacklistedIp()
for _, blacklistRule := range AllBlacklistedIps {
wildcardMatch := MatchIpWildcard(ipAddr, blacklistRule)
if wildcardMatch {
return true
}
cidrMatch := MatchIpCIDR(ipAddr, blacklistRule)
if cidrMatch {
return true
}
}
return false
}
func (s *Store) GetAllBlacklistedIp() []string {
bannedIps := []string{}
entries, err := s.sysdb.ListTable("blacklist-ip")
@@ -157,9 +261,27 @@ func (s *Store) GetAllBlacklistedIp() []string {
return bannedIps
}
// Check if a IP address is blacklisted, in either country or IP blacklist
func (s *Store) GetAllWhitelistedIp() []string {
whitelistedIp := []string{}
entries, err := s.sysdb.ListTable("whitelist-ip")
if err != nil {
return whitelistedIp
}
for _, keypairs := range entries {
ip := string(keypairs[0])
whitelistedIp = append(whitelistedIp, ip)
}
return whitelistedIp
}
/*
Check if a IP address is blacklisted, in either country or IP blacklist
IsBlacklisted default return is false (allow access)
*/
func (s *Store) IsBlacklisted(ipAddr string) bool {
if !s.Enabled {
if !s.BlacklistEnabled {
//Blacklist not enabled. Always return false
return false
}
@@ -185,6 +307,40 @@ func (s *Store) IsBlacklisted(ipAddr string) bool {
return false
}
/*
IsWhitelisted check if a given IP address is in the current
server's white list.
Note that the Whitelist default result is true even
when encountered error
*/
func (s *Store) IsWhitelisted(ipAddr string) bool {
if !s.WhitelistEnabled {
//Whitelist not enabled. Always return true (allow access)
return true
}
if ipAddr == "" {
//Unable to get the target IP address, assume ok
return true
}
countryCode, err := s.ResolveCountryCodeFromIP(ipAddr)
if err != nil {
return true
}
if s.IsCountryCodeWhitelisted(countryCode.CountryIsoCode) {
return true
}
if s.IsIPWhitelisted(ipAddr) {
return true
}
return false
}
func (s *Store) GetRequesterCountryISOCode(r *http.Request) string {
ipAddr := GetRequesterIP(r)
if ipAddr == "" {
@@ -197,54 +353,3 @@ func (s *Store) GetRequesterCountryISOCode(r *http.Request) string {
return countryCode.CountryIsoCode
}
// Utilities function
func GetRequesterIP(r *http.Request) string {
ip := r.Header.Get("X-Forwarded-For")
if ip == "" {
ip = r.Header.Get("X-Real-IP")
if ip == "" {
ip = strings.Split(r.RemoteAddr, ":")[0]
}
}
return ip
}
// Match the IP address with a wildcard string
func MatchIpWildcard(ipAddress, wildcard string) bool {
// Split IP address and wildcard into octets
ipOctets := strings.Split(ipAddress, ".")
wildcardOctets := strings.Split(wildcard, ".")
// Check that both have 4 octets
if len(ipOctets) != 4 || len(wildcardOctets) != 4 {
return false
}
// Check each octet to see if it matches the wildcard or is an exact match
for i := 0; i < 4; i++ {
if wildcardOctets[i] == "*" {
continue
}
if ipOctets[i] != wildcardOctets[i] {
return false
}
}
return true
}
// Match ip address with CIDR
func MatchIpCIDR(ip string, cidr string) bool {
// parse the CIDR string
_, cidrnet, err := net.ParseCIDR(cidr)
if err != nil {
return false
}
// parse the IP address
ipAddr := net.ParseIP(ip)
// check if the IP address is within the CIDR range
return cidrnet.Contains(ipAddr)
}

84470
src/mod/geodb/geoipv6.csv Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,13 @@ func (s *Store) search(ip string) string {
*/
//Search in geotrie tree
cc := s.geotrie.search(ip)
cc := ""
if IsIPv6(ip) {
cc = s.geotrieIpv6.search(ip)
} else {
cc = s.geotrie.search(ip)
}
/*
if cc != "" {
s.geoipCache.Store(ip, cc)

115
src/mod/geodb/netutils.go Normal file
View File

@@ -0,0 +1,115 @@
package geodb
import (
"net"
"net/http"
"strings"
)
// Utilities function
func GetRequesterIP(r *http.Request) string {
ip := r.Header.Get("X-Real-Ip")
if ip == "" {
ip = r.Header.Get("X-Forwarded-For")
}
if ip == "" {
ip = r.RemoteAddr
}
/*
Possible shits that might be extracted by this code
127.0.0.1:61001
[15c4:cbb4:cc98:4291:ffc1:3a46:06a1:51a7]:61002
127.0.0.1
158.250.160.114,109.21.249.211
[15c4:cbb4:cc98:4291:ffc1:3a46:06a1:51a7],109.21.249.211
We need to extract just the first ip address
*/
requesterRawIp := ip
if strings.Contains(requesterRawIp, ",") {
//Trim off all the forwarder IPs
requesterRawIp = strings.Split(requesterRawIp, ",")[0]
}
//Trim away the port number
reqHost, _, err := net.SplitHostPort(requesterRawIp)
if err == nil {
requesterRawIp = reqHost
}
if strings.HasPrefix(requesterRawIp, "[") && strings.HasSuffix(requesterRawIp, "]") {
//e.g. [15c4:cbb4:cc98:4291:ffc1:3a46:06a1:51a7]
requesterRawIp = requesterRawIp[1 : len(requesterRawIp)-1]
}
return requesterRawIp
}
// Match the IP address with a wildcard string
func MatchIpWildcard(ipAddress, wildcard string) bool {
// Split IP address and wildcard into octets
ipOctets := strings.Split(ipAddress, ".")
wildcardOctets := strings.Split(wildcard, ".")
// Check that both have 4 octets
if len(ipOctets) != 4 || len(wildcardOctets) != 4 {
return false
}
// Check each octet to see if it matches the wildcard or is an exact match
for i := 0; i < 4; i++ {
if wildcardOctets[i] == "*" {
continue
}
if ipOctets[i] != wildcardOctets[i] {
return false
}
}
return true
}
// Match ip address with CIDR
func MatchIpCIDR(ip string, cidr string) bool {
// parse the CIDR string
_, cidrnet, err := net.ParseCIDR(cidr)
if err != nil {
return false
}
// parse the IP address
ipAddr := net.ParseIP(ip)
// check if the IP address is within the CIDR range
return cidrnet.Contains(ipAddr)
}
// Check if a ip is private IP range
func IsPrivateIP(ipStr string) bool {
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
return ip.IsPrivate()
}
// Check if an Ip string is ipv6
func IsIPv6(ipStr string) bool {
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
return ip.To4() == nil && ip.To16() != nil
}
// Check if an Ip string is ipv6
func IsIPv4(ipStr string) bool {
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
return ip.To4() != nil
}

View File

@@ -24,6 +24,10 @@ func ipToBitString(ip string) string {
// Convert the IP address to a 4-byte slice
ipBytes := parsedIP.To4()
if ipBytes == nil {
//This is an IPv6 address
ipBytes = parsedIP.To16()
}
// Convert each byte in the IP address to its 8-bit binary representation
var result []string
@@ -36,23 +40,38 @@ func ipToBitString(ip string) string {
}
func bitStringToIp(bitString string) string {
// Split the bit string into four 8-bit segments
segments := []string{
bitString[:8],
bitString[8:16],
bitString[16:24],
bitString[24:32],
// Check if the bit string represents an IPv4 or IPv6 address
isIPv4 := len(bitString) == 32
// Split the bit string into 8-bit segments
segments := make([]string, 0)
if isIPv4 {
for i := 0; i < 4; i++ {
segments = append(segments, bitString[i*8:(i+1)*8])
}
} else {
for i := 0; i < 16; i++ {
segments = append(segments, bitString[i*8:(i+1)*8])
}
}
// Convert each segment to its decimal equivalent
var decimalSegments []int
for _, s := range segments {
i, _ := strconv.ParseInt(s, 2, 64)
decimalSegments = append(decimalSegments, int(i))
decimalSegments := make([]int, len(segments))
for i, s := range segments {
val, _ := strconv.ParseInt(s, 2, 64)
decimalSegments[i] = int(val)
}
// Join the decimal segments with dots to form the IP address string
return fmt.Sprintf("%d.%d.%d.%d", decimalSegments[0], decimalSegments[1], decimalSegments[2], decimalSegments[3])
// Construct the IP address string based on the type (IPv4 or IPv6)
if isIPv4 {
return fmt.Sprintf("%d.%d.%d.%d", decimalSegments[0], decimalSegments[1], decimalSegments[2], decimalSegments[3])
} else {
ip := make(net.IP, net.IPv6len)
for i := 0; i < net.IPv6len; i++ {
ip[i] = byte(decimalSegments[i])
}
return ip.String()
}
}
// inititlaizing a new trie
@@ -93,17 +112,11 @@ func isReservedIP(ip string) bool {
if parsedIP.IsLinkLocalUnicast() || parsedIP.IsLinkLocalMulticast() {
return true
}
// Check if the IP address is in the private address ranges
privateRanges := []*net.IPNet{
{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)},
{IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)},
{IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)},
}
for _, r := range privateRanges {
if r.Contains(parsedIP) {
return true
}
if parsedIP.IsPrivate() {
return true
}
// If the IP address is not a reserved address, return false
return false
}

View File

@@ -29,9 +29,9 @@ import (
*/
/*
Bianry embedding
Bianry embedding
Make sure when compile, gotty binary exists in static.gotty
Make sure when compile, gotty binary exists in static.gotty
*/
var (
//go:embed gotty/*
@@ -61,7 +61,7 @@ func NewSSHProxyManager() *Manager {
}
}
//Get the next free port in the list
// Get the next free port in the list
func (m *Manager) GetNextPort() int {
nextPort := m.StartingPort
occupiedPort := make(map[int]bool)
@@ -96,7 +96,7 @@ func (m *Manager) HandleHttpByInstanceId(instanceId string, w http.ResponseWrite
r.Header.Set("A-Upgrade", "websocket")
requestURL = strings.TrimPrefix(requestURL, "/")
u, _ := url.Parse("ws://127.0.0.1:" + strconv.Itoa(targetInstance.AssignedPort) + "/" + requestURL)
wspHandler := websocketproxy.NewProxy(u)
wspHandler := websocketproxy.NewProxy(u, false)
wspHandler.ServeHTTP(w, r)
return
}
@@ -168,7 +168,7 @@ func (m *Manager) NewSSHProxy(binaryRoot string) (*Instance, error) {
return &thisInstance, nil
}
//Create a new Connection to target address
// Create a new Connection to target address
func (i *Instance) CreateNewConnection(listenPort int, username string, remoteIpAddr string, remotePort int) error {
//Create a gotty instance
connAddr := remoteIpAddr

View File

@@ -145,7 +145,9 @@ func (c *Collector) RecordRequest(ri RequestInfo) {
//Filter out CF forwarded requests
if strings.Contains(ri.IpAddr, ",") {
ips := strings.Split(strings.TrimSpace(ri.IpAddr), ",")
if len(ips) >= 1 {
if len(ips) >= 1 && IsValidIPAddress(strings.TrimPrefix(ips[0], "[")) {
//Example when forwarded from CF: 158.250.160.114,109.21.249.211
//Or IPv6 [15c4:cbb4:cc98:4291:ffc1:3a46:06a1:51a7],109.21.249.211
ri.IpAddr = ips[0]
}
}

View File

@@ -2,6 +2,7 @@ package statistic
import (
"fmt"
"net"
"time"
)
@@ -26,3 +27,19 @@ func IsBeforeToday(dateString string) bool {
today := time.Now().UTC().Truncate(24 * time.Hour)
return date.Before(today) || dateString == time.Now().Format(layout)
}
// Check if the IP string is a valid ip address
func IsValidIPAddress(ip string) bool {
// Check if the string is a valid IPv4 address
if parsedIP := net.ParseIP(ip); parsedIP != nil && parsedIP.To4() != nil {
return true
}
// Check if the string is a valid IPv6 address
if parsedIP := net.ParseIP(ip); parsedIP != nil && parsedIP.To16() != nil {
return true
}
// If the string is neither a valid IPv4 nor IPv6 address, return false
return false
}

View File

@@ -157,6 +157,11 @@ func (c *ProxyRelayConfig) Start() error {
return nil
}
// Stop a running proxy if running
func (c *ProxyRelayConfig) IsRunning() bool {
return c.Running || c.stopChan != nil
}
// Stop a running proxy if running
func (c *ProxyRelayConfig) Stop() {
if c.Running || c.stopChan != nil {

View File

@@ -122,6 +122,77 @@ func (m *Manager) HandleListConfigs(w http.ResponseWriter, r *http.Request) {
utils.SendJSONResponse(w, string(js))
}
func (m *Manager) HandleStartProxy(w http.ResponseWriter, r *http.Request) {
uuid, err := utils.PostPara(r, "uuid")
if err != nil {
utils.SendErrorResponse(w, "invalid uuid given")
return
}
targetProxyConfig, err := m.GetConfigByUUID(uuid)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
err = targetProxyConfig.Start()
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
utils.SendOK(w)
}
func (m *Manager) HandleStopProxy(w http.ResponseWriter, r *http.Request) {
uuid, err := utils.PostPara(r, "uuid")
if err != nil {
utils.SendErrorResponse(w, "invalid uuid given")
return
}
targetProxyConfig, err := m.GetConfigByUUID(uuid)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
if !targetProxyConfig.IsRunning() {
utils.SendErrorResponse(w, "target proxy service is not running")
return
}
targetProxyConfig.Stop()
utils.SendOK(w)
}
func (m *Manager) HandleRemoveProxy(w http.ResponseWriter, r *http.Request) {
uuid, err := utils.PostPara(r, "uuid")
if err != nil {
utils.SendErrorResponse(w, "invalid uuid given")
return
}
targetProxyConfig, err := m.GetConfigByUUID(uuid)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
if targetProxyConfig.IsRunning() {
utils.SendErrorResponse(w, "Service is running")
return
}
err = m.RemoveConfig(targetProxyConfig.UUID)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
utils.SendOK(w)
}
func (m *Manager) HandleGetProxyStatus(w http.ResponseWriter, r *http.Request) {
uuid, err := utils.GetPara(r, "uuid")
if err != nil {

View File

@@ -132,10 +132,12 @@ func (m *Manager) EditConfig(configUUID string, newName string, newPortA string,
foundConfig.Timeout = newTimeout
}
err = foundConfig.ValidateConfigs()
if err != nil {
return err
}
/*
err = foundConfig.ValidateConfigs()
if err != nil {
return err
}
*/
m.SaveConfigToDatabase()

View File

@@ -1,34 +1,22 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
MIIDuTCCAqCgAwIBAgIBADANBgkqhkiG9w0BAQ0FADB2MQswCQYDVQQGEwJoazES
MBAGA1UECAwJSG9uZyBLb25nMRQwEgYDVQQKDAtpbXVzbGFiLmNvbTEZMBcGA1UE
AwwQWm9yYXh5IFNlbGYtaG9zdDEQMA4GA1UEBwwHSU1VU0xBQjEQMA4GA1UECwwH
SU1VU0xBQjAeFw0yMzA1MjcxMDQyNDJaFw0zODA1MjgxMDQyNDJaMHYxCzAJBgNV
BAYTAmhrMRIwEAYDVQQIDAlIb25nIEtvbmcxFDASBgNVBAoMC2ltdXNsYWIuY29t
MRkwFwYDVQQDDBBab3JheHkgU2VsZi1ob3N0MRAwDgYDVQQHDAdJTVVTTEFCMRAw
DgYDVQQLDAdJTVVTTEFCMIIBIzANBgkqhkiG9w0BAQEFAAOCARAAMIIBCwKCAQIA
xav3Qq4DBooHsGW9m+r0dgjI832grX2c0Z6MJQQoE7B6wfpUI0OyfRugTXyXoiRZ
gLxuROgiCUmp8FaLbl7RsvbImMbCPo3D/RbCT1aJCNXLZ0a7yvcDYc6woQW4nUyk
ohHfT2otcu+OYS6aYRZuXGsKTAqPSwEXRMtr89wkPgZPsrCD27LFHBOmIcVABDvF
KRuiwHWSHhFfU5n1AZLyYeYoLNQ9fZPvzPpkMD+HMKi4MMwr/vLE0DwU5jSfVFq+
cd68zVihp9N/T77yah5EIH9CYm4m8Acs4bfL8DALxnaSN3KmGw6J35rOXrJvJLdh
t42PDROmQrXN8uG8wGkBiBkCAwEAAaNQME4wHQYDVR0OBBYEFLhXihE+1K6MoL0P
Nx5htfuSatpiMB8GA1UdIwQYMBaAFLhXihE+1K6MoL0PNx5htfuSatpiMAwGA1Ud
EwQFMAMBAf8wDQYJKoZIhvcNAQENBQADggECAMCn0ed1bfLefGvoQJV/q+X9p61U
HunSFJAAhp0N2Q3tq/zjIu0kJX7N0JBciEw2c0ZmqJIqR8V8Im/h/4XuuOR+53hg
opOSPo39ww7mpxyBlQm63v1nXcNQcvw4U0JqXQ4Kyv8cgX7DIuyjRWHQpc5+6joy
L5Nz5hzQbgpnPdHQEMorfnm8q6bWg/291IAV3ZA9Z6T5gn4YuyjeUdDczQtpT6nu
1iTNPqtO6R3aeTVT+OSJT9sH2MHfDAsf371HBM6MzM/5QBc/62Bgau7NUjNKeSEA
EtUBil8wBHwT7vOtqbyNk5FHEfoCpYsQtP7AtEo10izKCQpDXPftfiJefkOY
-----END CERTIFICATE-----

View File

@@ -1,52 +1,28 @@
-----BEGIN PRIVATE KEY-----
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
+iSj2pW0Rbdy7oOcFRF/iM2GwFYc1Q==
-----END PRIVATE KEY-----
MIIEwQIBADANBgkqhkiG9w0BAQEFAASCBKswggSnAgEAAoIBAgDFq/dCrgMGigew
Zb2b6vR2CMjzfaCtfZzRnowlBCgTsHrB+lQjQ7J9G6BNfJeiJFmAvG5E6CIJSanw
VotuXtGy9siYxsI+jcP9FsJPVokI1ctnRrvK9wNhzrChBbidTKSiEd9Pai1y745h
LpphFm5cawpMCo9LARdEy2vz3CQ+Bk+ysIPbssUcE6YhxUAEO8UpG6LAdZIeEV9T
mfUBkvJh5igs1D19k+/M+mQwP4cwqLgwzCv+8sTQPBTmNJ9UWr5x3rzNWKGn039P
vvJqHkQgf0JibibwByzht8vwMAvGdpI3cqYbDonfms5esm8kt2G3jY8NE6ZCtc3y
4bzAaQGIGQIDAQABAoIBARA+w8FdH66H5X3fvqdztceFjU5FgtD/Q8YOa6IXJ1wG
4u/SLNwBEkgp3xC/Lo8KwbhMxBsxoKp2vVqdIjRd4on8shusKgaODA9esXVnvTdW
qrLAI2rYxhRhsi5pk/SJefY/1cRnC3koquDdvZ5BA6zgtyXssD4PxuPGfAa8jtXy
GIPUDj/Na+pFf3u6iKGGFA7xmKA+Jx3xL77zRdiG5bS70uTUJIpbJ9fdFDTEwyb+
5hy6gmiPZ96bg3LnCl23jBx1RqvZxMxm6nHkEtMStoqczCkDIqypq0GaDD3Op5P9
TDVnrz37FQn4PWrq2VoqoKNcigcloBd620dL3p8jVcihAoGBD5yXW+uCGWDEufD0
Dvqd5pWD4pXlIP5E6Br9KFaDV0hHEyiJrXk6BzUL0EB7w+8kFafHW2gnvIpS99Tk
mI37v/8qGqOerrsru/KtbwXR524LsSFBMXoJ+KtEanlZu+qBvhXsRA2ov/dqO+mb
XEpJbcXGMcFuzeTRoss1JpFUcOKlAoGBDKlqhldAjzpWigd29hjK8saoN3nO7XW4
O4QjRfnU2M/4pcOZrvu3DyPRfF5et2KAfp6pyYDwFfYWpSzzwblyZQTYCaGT5MCP
e3V5ee0dCjdIkB92XGv9xzZLLWClMcoLEEV2knRVY8LdbDAQHxFgOdCkOIWNfw07
+BmM72YHHhllAoGBDptOqrxQ/3mg1vBxCUrHTiT6PphMx2/f/OKzlnhLbvC7P1ug
ZWSVPIUPRovuwMYRFwnh5s4uz6MEOclBENNXhq4xMLeCEq4hHzrRtpzVZhl6awJY
QviSN83Wt2BO6xlgxv8wDgRRrTrKdL//knwW89QlugvnplC/K/fBBRLY1L3ZAoGB
BOg3r57rF1c9qLrh4NiU9ugE05MynhbscqxwWzNKyUg4jk2zJvzI4mY4TuHoBVx4
fhoRpVWCNpCsEBHO2np7mij5bSogvhvev7M0hAtgINByH+EBpyn3LZieJBT7kMND
7GdvX60UVthzpfUumkvKpj11F66yutWvMyT72OAKzCB9AoGBBHixLZSz89STQNNT
rYcSDW79Lj18Z6/HBhLwbteMfuYun6HUssh2CKR7awFa/UOmYySiCAV97m38hjDB
JC5eMEskRGGrejddtUGjIhNX1hanAkhlnbRwVZc97XvXjryDGBZtaTN/2x4lD59t
mKYLZqGfZ+fMnaWoxLrCnn0cjIBK
-----END PRIVATE KEY-----

View File

@@ -2,6 +2,7 @@
package websocketproxy
import (
"crypto/tls"
"fmt"
"io"
"log"
@@ -46,16 +47,19 @@ type WebsocketProxy struct {
// If nil, DefaultDialer is used.
Dialer *websocket.Dialer
Verbal bool
Verbal bool
SkipTlsValidation bool
}
// ProxyHandler returns a new http.Handler interface that reverse proxies the
// request to the given target.
func ProxyHandler(target *url.URL) http.Handler { return NewProxy(target) }
func ProxyHandler(target *url.URL, skipTlsValidation bool) http.Handler {
return NewProxy(target, skipTlsValidation)
}
// NewProxy returns a new Websocket reverse proxy that rewrites the
// URL's to the scheme, host and base path provider in target.
func NewProxy(target *url.URL) *WebsocketProxy {
func NewProxy(target *url.URL, skipTlsValidation bool) *WebsocketProxy {
backend := func(r *http.Request) *url.URL {
// Shallow copy
u := *target
@@ -64,7 +68,7 @@ func NewProxy(target *url.URL) *WebsocketProxy {
u.RawQuery = r.URL.RawQuery
return &u
}
return &WebsocketProxy{Backend: backend, Verbal: false}
return &WebsocketProxy{Backend: backend, Verbal: false, SkipTlsValidation: skipTlsValidation}
}
// ServeHTTP implements the http.Handler that proxies WebSocket connections.
@@ -84,7 +88,15 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
dialer := w.Dialer
if w.Dialer == nil {
dialer = DefaultDialer
if w.SkipTlsValidation {
//Disable TLS secure check if target allow skip verification
bypassDialer := websocket.DefaultDialer
bypassDialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
dialer = bypassDialer
} else {
//Just use the default dialer come with gorilla websocket
dialer = DefaultDialer
}
}
// Pass headers from the incoming request to the dialer to forward them to

View File

@@ -28,7 +28,7 @@ func TestProxy(t *testing.T) {
}
u, _ := url.Parse(backendURL)
proxy := NewProxy(u)
proxy := NewProxy(u, false)
proxy.Upgrader = upgrader
mux := http.NewServeMux()
@@ -46,7 +46,7 @@ func TestProxy(t *testing.T) {
mux2 := http.NewServeMux()
mux2.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Don't upgrade if original host header isn't preserved
if r.Host != "127.0.0.1:7777" {
if r.Host != "127.0.0.1:7777" {
log.Printf("Host header set incorrectly. Expecting 127.0.0.1:7777 got %s", r.Host)
return
}