mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-08-07 05:38:30 +02:00
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:
@@ -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
84470
src/mod/geodb/geoipv6.csv
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
115
src/mod/geodb/netutils.go
Normal 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
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user