go-shield/server/main.go
2024-02-16 23:37:45 +02:00

1469 lines
41 KiB
Go
Executable File

package main
import (
"categorization"
b64 "encoding/base64"
"encoding/json"
_ "expvar"
"fmt"
"github.com/facebookgo/pidfile"
"github.com/fasthttp/router"
"github.com/oschwald/geoip2-golang"
"github.com/techoner/gophp"
"github.com/valyala/fasthttp"
"handlers"
"github.com/sirupsen/logrus"
"log"
"log/syslog"
"net"
urlparse "net/url"
"os"
"regexp"
"runtime/debug"
"shields"
"storage"
"strconv"
"strings"
"time"
"ufdbguard"
)
var (
addr = "127.0.0.1"
port int = 3333
//CONN_TYPE = "tcp"
version = "1.0.11"
strContentType = []byte("Content-Type")
strApplicationJSON = []byte("application/json")
isDebug bool = false
timeOut int = 5
Selfsitename string
SelfSourceline string
SelfSquidUrgency bool
SelfKsrnEmergency bool
SelfEnableUfdbGuard bool
SelfExternalAclFirstRequest int
SelfTokenOutput string
SelfProtocol string
SelfVirtualUser string
SelfMac string
SelfLocalCache map[string]string
SelfmethodProto string
SelfUsername string
Selfipaddr string
SelfShieldMac string
SelfShieldipaddr string
SelfShieldUsername string
SelfVirtualUserCache map[string]string
SelfEnableStrongswanServer int
SelfEnableITChart bool
SelfWebfilterRuleName string
Selfcategory int
SelfcategoryName string
SelfWebPages []interface{}
SelfMaxItemsInMemory int
SelfprepareDataText []byte
SelfKSRNOnlyCategorization = true
SelfCacheTime int
SelfResolvedHosts map[string]bool
)
func init() {
// create pid file
pidfile.SetPidfilePath("/var/run/go-shield-server.pid")
err_pid := pidfile.Write()
if err_pid != nil {
panic("Could not write pid file")
}
log.SetFlags(log.LstdFlags | log.Lshortfile)
//log.SetOutput(os.Stderr) // it's default
if _slog, err := syslog.New(syslog.LOG_DEBUG, "go-shield-server"); err == nil {
log.SetOutput(_slog)
}
}
func main() {
f, err := os.OpenFile("/var/log/go-shield/server-stack.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
logrus.SetOutput(f)
defer func() {
if r := recover(); r != nil {
logrus.Errorf("Panic: %v,\n%s", r, debug.Stack())
os.Exit(1)
}
}()
isDebug = handlers.GetSocketInfoBool("Go_Shield_Server_Debug")
categorization.DebugCategorization = isDebug
handlers.DebugHandlers = isDebug
storage.DebugStorage = isDebug
addr = handlers.GetSocketInfoString("Go_Shield_Server_Addr")
port = handlers.GetSocketInfoInt("Go_Shield_Server_Port")
timeOut = handlers.GetSocketInfoInt("Go_Shield_Connector_TimeOut")
SelfSquidUrgency = handlers.GetSocketInfoBool("SquidUrgency")
SelfEnableUfdbGuard = handlers.GetSocketInfoBool("EnableUfdbGuard")
SelfKsrnEmergency = handlers.GetSocketInfoBool("KSRNEmergency")
SelfExternalAclFirstRequest = handlers.GetSocketInfoInt("ExternalAclFirstRequest")
SelfEnableStrongswanServer = handlers.GetSocketInfoInt("EnableStrongswanServer")
SelfEnableITChart = handlers.GetSocketInfoBool("EnableITChart")
SelfTokenOutput = "OK"
SelfProtocol = "GET"
SelfMaxItemsInMemory = handlers.GetSocketInfoInt("TheShieldMaxItemsInMemory")
SelfKSRNOnlyCategorization = handlers.GetSocketInfoBool("KSRNOnlyCategorization")
WebErrorPagesCompiled := handlers.GetSocketInfoString("WebErrorPagesCompiled")
SelfCacheTime = handlers.GetSocketInfoInt("TheShieldServiceCacheTime")
if SelfEnableUfdbGuard {
ufdbguard.DebugUfdbguard = isDebug
}
if SelfEnableITChart {
//itchart.DebugITChart = isDebug
}
shields.DebugShields = isDebug
SelfVirtualUserCache = make(map[string]string)
if addr == "" {
addr = "127.0.0.1"
}
if port == 0 {
port = 3333
}
if SelfCacheTime == 0 {
SelfCacheTime = 84600
}
if timeOut == 0 {
timeOut = 5
}
out, err := gophp.Unserialize([]byte(WebErrorPagesCompiled))
if err != nil {
if isDebug {
log.Printf("[BUILD_PAGE]: Loading Client Engine Web error pages failed to unserialize: ", err)
}
}
SelfWebPages = out.([]interface{})
log.Printf("Starting Go Shield Server => addr=%s port=%d debug=%t ", addr, port, isDebug)
/*TCP*/
//l, err := net.Listen(CONN_TYPE, addr+":"+port)
//if err != nil {
// log.Printf("Error listening:", err.Error())
// os.Exit(1)
//}
//// Close the listener when the application closes.
//defer l.Close()
//log.Printf("Listening on " + addr + ":" + port)
/*FASTHTTP*/
//log.Printf("Try connect to ListenAndServe: %s %d", addr, port)
//h := requestHandler
//h = fasthttp.CompressHandler(h)
r := router.New()
r.GET("/get-categories/{website}", getCategories)
r.GET("/get-version", getVersion)
r.GET("/category/{website}/{ids}", category)
r.GET("/geo/{source-ip}/{iso-code}", geo)
r.GET("/db/stats", dbStats)
r.GET("/db/flush", dbFlush)
r.GET("/db/capacity", dbCapacity)
r.GET("/db/len", dbLen)
r.GET("/external-acl-first/{line}", processRequest)
if err := fasthttp.ListenAndServe(addr+":"+strconv.Itoa(port), r.Handler); err != nil {
log.Fatalf("Error in ListenAndServe: %s", err)
}
}
/*FASTHTTP*/
func getVersion(ctx *fasthttp.RequestCtx) {
ctx.Response.Header.SetCanonical(strContentType, strApplicationJSON)
ctx.Response.SetStatusCode(200)
response := map[string]string{"version": version}
if err := json.NewEncoder(ctx).Encode(response); err != nil {
log.Fatal(err)
}
}
func getCategories(ctx *fasthttp.RequestCtx) {
catid, catname := categorization.GetCategories(ctx.UserValue("website").(string))
ctx.Response.Header.SetCanonical(strContentType, strApplicationJSON)
ctx.Response.SetStatusCode(200)
response := map[string]string{"category_id": strconv.Itoa(catid), "category_name": catname}
if err := json.NewEncoder(ctx).Encode(response); err != nil {
log.Fatal(err)
}
}
func category(ctx *fasthttp.RequestCtx) {
catid, _ := categorization.GetCategories(ctx.UserValue("website").(string))
//ctx.Response.Header.SetCanonical(strContentType, strApplicationJSON)
ctx.Response.SetStatusCode(200)
if catid == 0 {
ctx.WriteString("FALSE")
}
split := strings.Split(ctx.UserValue("ids").(string), "-")
results := false
for _, line := range split {
matches, _ := strconv.Atoi(line)
if catid == matches {
results = true
ctx.WriteString("TRUE")
break
}
}
if !results {
ctx.WriteString("FALSE")
}
}
func dbStats(ctx *fasthttp.RequestCtx) {
ctx.Response.SetStatusCode(200)
ctx.Response.Header.SetCanonical(strContentType, strApplicationJSON)
stats, _ := json.Marshal(storage.InMemoryCache.Stats())
ctx.Write(stats)
// defer conn.Close()
// stats, _ := json.Marshal(InMemoryCache.Stats())
// conn.Write(stats)
}
func dbFlush(ctx *fasthttp.RequestCtx) {
ctx.Response.SetStatusCode(200)
storage.InMemoryCache.Reset()
ctx.WriteString("OK")
}
func dbLen(ctx *fasthttp.RequestCtx) {
ctx.Response.SetStatusCode(200)
ctx.Response.Header.SetCanonical(strContentType, strApplicationJSON)
stats, _ := json.Marshal(storage.InMemoryCache.Len())
ctx.Write(stats)
// defer conn.Close()
// stats, _ := json.Marshal(InMemoryCache.Stats())
// conn.Write(stats)
}
func dbCapacity(ctx *fasthttp.RequestCtx) {
ctx.Response.SetStatusCode(200)
ctx.Response.Header.SetCanonical(strContentType, strApplicationJSON)
stats, _ := json.Marshal(storage.InMemoryCache.Capacity())
ctx.Write(stats)
// defer conn.Close()
// stats, _ := json.Marshal(InMemoryCache.Stats())
// conn.Write(stats)
}
func geo(ctx *fasthttp.RequestCtx) {
ctx.Response.SetStatusCode(200)
db, err := geoip2.Open("/usr/local/share/GeoIP/GeoLite2-City.mmdb")
if err != nil {
ctx.WriteString(err.Error())
return
}
defer db.Close()
// If you are using strings that may be invalid, check that ip is not nil
ip := net.ParseIP(ctx.UserValue("source-ip").(string))
record, err := db.City(ip)
if err != nil {
ctx.WriteString(err.Error())
return
}
//ctx.Response.Header.SetCanonical(strContentType, strApplicationJSON)
split := strings.Split(ctx.UserValue("iso-code").(string), "-")
results := false
for _, line := range split {
if line == record.Country.IsoCode {
results = true
ctx.WriteString("TRUE")
break
}
}
if !results {
ctx.WriteString("FALSE")
}
}
func processRequest(ctx *fasthttp.RequestCtx) {
//f, err := os.OpenFile("/var/log/go-shield/server-stack.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
//
//logrus.SetOutput(f)
defer func() {
if r := recover(); r != nil {
log.Printf("Panic: %v,\n%s", r, debug.Stack())
os.Exit(1)
}
}()
start := time.Now()
request := ctx.UserValue("line").(string)
decode, _ := b64.URLEncoding.DecodeString(request)
line := string(decode)
Selfsitename = ""
SelfSourceline = line
lparts := strings.Split(strings.TrimRight(line, "\n"), " ")
id := lparts[0]
if isDebug {
log.Printf("Receive <%s>", line)
}
if strings.Index(line, "webfilter:%20pass") > 10 {
if isDebug {
log.Printf("WEBFILTER = PASS")
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(id + " OK\n")
return
}
if strings.Index(line, "/squid-internal-dynamic/") > 10 {
if isDebug {
log.Printf("INTERNAL-DYNAMIC = PASS")
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(id + " OK\n")
return
}
if strings.Index(line, "/squid-internal-mgr/") > 10 {
if isDebug {
log.Printf("INTERNAL-DYNAMIC = PASS")
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(id + " OK\n")
return
}
if strings.Index(line, "cache_object:/") > 0 {
if isDebug {
log.Printf("INTERNAL-DYNAMIC = PASS")
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(id + " OK\n")
return
}
SelfTokenOutput = "OK"
parseArray := parseLine(line)
if len(parseArray) == 0 {
if isDebug {
log.Printf("Error parseLine array exception, arry is empty ")
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s first=ERROR\n", id, SelfTokenOutput))
return
}
logPrefix := ""
//CountOfRows := len(parseArray)
aclType, _ := strconv.Atoi(parseArray["acl"])
if aclType == 1 {
SelfTokenOutput = "OK"
}
var tokens []string
//Sni := ""
//UserCert := ""
//categoryOrder := true
//tokenscategoryAdded := false
//Rblpass := false
categoryName := ""
category := 0
method := "GET"
choose := ""
url := ""
results := ""
var blockit bool
var white bool
_ = white
//SaveCache := false
var action string
SelfVirtualUser = ""
var countryCode string
var itChart string
_ = itChart
var itChartInfo string
var errorMsg string
_ = itChartInfo
SelfMac = ""
xForward := ""
sitename := ""
clientHostname := ""
//ITChartInfo := ""
//CacheMessage := "MISS"
var cachedService int
_ = cachedService
proxyPort := 3128
proxyIP := "127.0.0.1"
var logQuery []string
countOfInternalCache := len(SelfLocalCache)
logQuery = append(logQuery, "Items in array: "+strconv.Itoa(countOfInternalCache))
//WebfilteringFound := false
//WebfilteringChecked := false
asACL := 0
askToShields := true //Todo Set askToShields
modeBack := false
SelfmethodProto = ""
SelfUsername = parseArray["username"]
ipaddr := parseArray["ipaddr"]
if _, ok := parseArray["mac"]; ok {
SelfMac = parseArray["mac"]
}
if _, ok := parseArray["forwardedfor"]; ok {
xForward = parseArray["forwardedfor"]
}
//if _, ok := parseArray["sni"]; ok {
// Sni = parseArray["sni"]
//}
if _, ok := parseArray["hostname"]; ok {
clientHostname = parseArray["hostname"]
}
if _, ok := parseArray["domain"]; ok {
sitename = parseArray["domain"]
}
if _, ok := parseArray["acl"]; ok {
asACL, _ = strconv.Atoi(parseArray["acl"])
}
if _, ok := parseArray["proto"]; ok {
method = parseArray["proto"]
}
url = parseArray["url"]
proxyPort, _ = strconv.Atoi(parseArray["myport"])
proxyIP = parseArray["myip"]
if len(sitename) == 0 {
if len(url) > 0 {
sitename = url
}
}
u, _ := urlparse.Parse(sitename)
sitename = u.Host
if len(sitename) == 0 {
sitename = u.Path
}
if sitename[0:4] == "www." {
sitename = sitename[4:]
}
sitename = strings.ToLower(sitename)
proxyUrl := url
//Sourceurl := url
//urlDomain := sitename
clientIp := ipaddr
Selfipaddr = ipaddr
clientMac := SelfMac
SelfVirtualUser = virtualUsers()
Selfsitename = sitename
SelfmethodProto = method
if SelfMac == "00:00:00:00:00:00" {
SelfMac = ""
}
logPrefix = fmt.Sprintf("%s %s %s %s %s", sitename, method, SelfMac, Selfipaddr, SelfUsername)
logQuery = append(logQuery, logPrefix)
shields.SelfShieldIpaddr = Selfipaddr
shields.SelfShieldMac = SelfMac
shields.SelfShieldUsername = SelfUsername
log.Printf("Executing shields.CountUsers()")
shields.CountUsers()
if len(SelfVirtualUser) > 0 {
SelfUsername = SelfVirtualUser
}
if xForward == "-" {
xForward = ""
}
if len(xForward) > 0 {
ipaddr = xForward
}
//TODO check if make sense
//if len(SelfUsername) < 3 {
// if len(UserCert) > 2 {
// SelfUsername = UserCert
// }
//}
if Selfsitename == "127.0.0.1" {
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s first=NONE\n", id, SelfTokenOutput))
return
}
if SelfSquidUrgency {
if isDebug {
log.Printf("WARNING... Emergency Enabled")
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s first=EMERGENCY webfilter=pass\n", id, SelfTokenOutput))
return
}
if asACL == 0 {
modeBack = true
askToShields = false
}
if modeBack {
if isDebug {
log.Printf("%s [DEBUG]: [BACK_MODE] Web-Filtering:%t | ItCharter:%t", Selfsitename, SelfEnableUfdbGuard, SelfEnableITChart)
}
if strings.Index(SelfSourceline, "shieldsblock:%20yes") > 10 {
SelfWebfilterRuleName = strconv.Itoa(0)
Selfcategory = 999999999
SelfcategoryName = "theshields"
re := regexp.MustCompile("cinfo:([0-9]+)-(.+?);")
matches := re.MatchString(SelfSourceline)
rs := re.FindStringSubmatch(SelfSourceline)
if matches {
Selfcategory, _ = strconv.Atoi(rs[1])
SelfcategoryName = rs[2]
}
redirect := buildErrorPage()
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s %s\n", id, SelfTokenOutput, redirect))
return
}
if SelfEnableUfdbGuard {
ufdbGuardClientLine := ufdbGuardClient(id, line, proxyUrl, Selfsitename, clientIp, clientHostname, clientMac, proxyIP, proxyPort)
//WebfilteringChecked = true
if isDebug {
log.Printf("%s [DEBUG] return <%s>", Selfsitename, ufdbGuardClientLine)
}
if len(ufdbGuardClientLine) > 0 {
log.Printf("%s [DEBUG] return <%s>", Selfsitename, ufdbGuardClientLine)
ctx.Response.SetStatusCode(200)
ctx.WriteString(ufdbGuardClientLine)
return
}
tokens = append(tokens, "webfilter=pass")
}
//if SelfEnableITChart {
// if isDebug {
// log.Printf("%s [DEBUG][ITCHART] Ask to itchart_client()", Selfsitename)
// }
// itChartLine := itChartClient(id)
// if len(itChartLine) > 0 {
// if isDebug {
// log.Printf("%s [DEBUG][ITCHART] return <%s>", Selfsitename, itChartLine)
// }
// ctx.Response.SetStatusCode(200)
// ctx.WriteString(itChartLine)
// return
// }
// tokens = append(tokens, "itchart=PASS")
//}
}
if isWhitelist(Selfsitename, SelfMac, Selfipaddr) {
if isDebug {
log.Printf("%s: %s[%s] WHITELISTED", Selfsitename, Selfipaddr, SelfMac)
}
category, categoryName = categorization.GetCategories(Selfsitename)
tokens = append(tokens, fmt.Sprintf("category=%d category-name=%s clog=cinfo:%d-%s; ", category, categoryName, category, categoryName))
tokens = append(tokens, "srn=WHITE rblpass=yes webfilter=pass")
SelfTokenOutput = strings.Join(tokens, " ")
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s \n", id, SelfTokenOutput))
return
}
if isBlacklisted(Selfsitename) {
if isDebug {
log.Printf("%s: %s[%s] BLACKLISTED", Selfsitename, Selfipaddr, SelfMac)
}
category, categoryName = categorization.GetCategories(Selfsitename)
tokens = append(tokens, fmt.Sprintf("category=%d category-name=%s clog=cinfo:%d-%s; ", category, categoryName, category, categoryName))
tokens = append(tokens, "srn=BLACK shieldsblock=yes")
SelfTokenOutput = strings.Join(tokens, " ")
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s \n", id, SelfTokenOutput))
return
}
choose = findKeyAccount()
prepareData := make(map[string]string)
prepareData["ACTION"] = "THESHIELDS"
prepareData["choose"] = choose
prepareData["USERNAME"] = SelfUsername
prepareData["ipaddr"] = ipaddr
prepareData["mac"] = SelfMac
prepareData["sitename"] = Selfsitename
prepareData["method"] = method
prepareData["LOG_QUERY"] = "0"
if SelfExternalAclFirstRequest == 1 {
logQuery = append(logQuery, "Log-query: Yes")
prepareData["LOG_QUERY"] = "1"
}
//TODO Check if make sense to keep
//SelfprepareDataText, _ = gophp.Serialize(prepareData)
//TODO Check if make sense to keep smd5
smd5 := storage.ShieldsFullCache(SelfUsername, ipaddr, SelfMac, Selfsitename, method)
if _, ok := SelfLocalCache[smd5]; ok {
if isDebug {
log.Printf("%s: HIT [%s] Client-array", Selfsitename, smd5)
}
results = SelfLocalCache[smd5]
}
if results == "" {
if value, _ := storage.Fetch(smd5); value != nil {
results = string(*value)
if results != "" {
if isDebug {
log.Printf("%s HIT [%s] Client-memcache", Selfsitename, smd5)
}
SelfLocalCache[smd5] = results
}
}
}
if results != "" {
askToShields = false
logQuery = append(logQuery, "Client sock time: -")
}
log.Printf("RESULTS ARE %s and TOKEN IS %t", results, SelfKSRNOnlyCategorization)
if results == "" {
if isDebug {
log.Printf("%s MISS ARRAY [%v] KSRNOnlyCategorization=%t", Selfsitename, smd5, SelfKSRNOnlyCategorization)
}
if SelfKSRNOnlyCategorization {
if len(SelfUsername) < 2 {
if len(SelfVirtualUser) > 0 {
SelfUsername = SelfVirtualUser
choose = findKeyAccount()
}
}
if len(SelfUsername) > 1 {
tokens = append(tokens, fmt.Sprintf("user=%s", urlparse.QueryEscape(SelfUsername)))
}
category, categoryName = categorization.GetCategories(sitename)
tokens = append(tokens, fmt.Sprintf("category=%d category-name=%s clog=cinfo:%d-%s; ", category, categoryName, category, categoryName))
SelfTokenOutput = strings.Join(tokens, " ")
elapsed := time.Since(start).Microseconds()
log.Printf("FINISH PARSING - took %d μs", elapsed)
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s\n", id, SelfTokenOutput))
return
}
}
if isDebug {
log.Printf("[DEBUG]:ASK_TO_SHIELDS [%t] MODE_BACK [%t]", askToShields, modeBack)
}
if askToShields {
results = processTheShields()
}
if len(results) < 5 {
if isDebug {
log.Printf("%s ERROR LEN ARRAY", logPrefix)
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s first=ERROR\n", id, SelfTokenOutput))
return
}
//TODO check SaveCache
if isDebug {
log.Printf("%s [DEBUG]: Receive [%s]", sitename, results)
}
out, err := gophp.Unserialize([]byte(results))
if err != nil {
if isDebug {
log.Printf("%s: [ERROR]: Unserialize error", sitename)
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s first=ERROR\n", id, SelfTokenOutput))
return
}
resultsArray := out.(map[string]interface{})
if resultsArray["error"] != nil {
errorMsg = resultsArray["error"].(string)
}
if category == 0 {
category = resultsArray["categoy_id"].(int)
}
if len(categoryName) == 0 {
if resultsArray["categoy_name"] != nil {
categoryName = resultsArray["categoy_name"].(string)
}
}
if resultsArray["ACTION"] != nil {
action = resultsArray["ACTION"].(string)
}
if resultsArray["VIRTUAL_USER"] != nil {
SelfVirtualUser = resultsArray["VIRTUAL_USER"].(string)
}
if resultsArray["COUNTRY_CODE"] != nil {
countryCode = resultsArray["COUNTRY_CODE"].(string)
}
if _, ok := resultsArray["ITCHART"]; ok {
itChart = resultsArray["ITCHART"].(string)
}
if _, ok := resultsArray["ITCHART_INFO"]; ok {
itChartInfo = resultsArray["ITCHART_INFO"].(string)
}
if _, ok := resultsArray["CACHED_SERVICE"]; ok {
cachedService = resultsArray["CACHED_SERVICE"].(int)
}
if _, ok := resultsArray["CACHED_TIME"]; ok {
logQuery = append(logQuery, fmt.Sprintf("Service cached time: %d", resultsArray["CACHED_TIME"].(int)))
}
if _, ok := resultsArray["SHIELD_TIMES"]; ok {
logQuery = append(logQuery, fmt.Sprintf("Time details: %d", resultsArray["SHIELD_TIMES"].(int)))
}
if _, ok := resultsArray["SHIELD_DURATION"]; ok {
logQuery = append(logQuery, fmt.Sprintf("The Shield duration: %d", resultsArray["SHIELD_DURATION"].(int)))
}
if _, ok := resultsArray["TOTAL_DURATION"]; ok {
logQuery = append(logQuery, fmt.Sprintf("The Shield Total: %d", resultsArray["TOTAL_DURATION"].(int)))
}
if action == "WHITELIST" {
tokens = append(tokens, "srn=WHITE rblpass=yes")
white = true
}
blockit = true
if isDebug {
log.Printf("%s The Shield, Answering [ -%s- ] [%s]", sitename, errorMsg, action)
}
if action == "WHITELIST" {
tokens = append(tokens, "srn=WHITE rblpass=yes")
blockit = false
}
if action == "PASS" {
tokens = append(tokens, "srn=PASS")
blockit = false
}
if action == "WHITE" {
tokens = append(tokens, "srn=WHITE rblpass=yes")
blockit = false
}
if len(action) == 0 {
if isDebug {
log.Printf("%s The Shield, ERROR OCCURED NO ACTION %s %s", sitename, errorMsg, action)
}
tokens = append(tokens, "srn=ERROR")
blockit = false
}
if blockit {
if isDebug {
log.Printf("%s The Shield, BLOCK [ -%s- ] [%s]", sitename, errorMsg, action)
}
tokens = append(tokens, "shieldsblock=yes")
tokens = append(tokens, fmt.Sprintf("TheShields:%s", action))
Selfcategory = category
}
if category > 0 {
categoryName = strings.Replace(categoryName, " ", "_", -1)
categoryName = strings.Replace(categoryName, "/", "_", -1)
tokens = append(tokens, fmt.Sprintf("category=%d category-name=%s clog=cinfo:%d-%s;", category, categoryName, category, categoryName))
logQuery = append(logQuery, fmt.Sprintf("Category: %s", categoryName))
} else {
tokens = append(tokens, "category=0 category-name=Unknown clog=cinfo:0-unknown;")
logQuery = append(logQuery, "Category: Unknown")
}
if isDebug {
log.Printf("[DEBUG]: [%s]:", sitename)
}
if len(SelfUsername) < 2 {
if SelfVirtualUser == "" {
SelfVirtualUser = ""
}
if len(SelfVirtualUser) > 0 {
SelfUsername = SelfVirtualUser
choose = findKeyAccount()
}
}
if len(SelfUsername) > 1 {
tokens = append(tokens, fmt.Sprintf("user=%s", urlparse.QueryEscape(SelfUsername)))
}
if len(countryCode) > 0 {
tokens = append(tokens, fmt.Sprintf("fromgeo=%s", countryCode))
}
if blockit {
SelfTokenOutput = strings.Join(tokens, ", ")
if isDebug {
log.Printf("%s: [DEBUG]: [DENIED]: FINAL[%s] (processing %s)", sitename, SelfTokenOutput, handlers.TimeTrack(start))
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s\n", id, SelfTokenOutput))
return
}
if isDebug {
log.Printf("[DEBUG]: [%s]: self.TOKEN_OUPUT [%s]", sitename, SelfTokenOutput)
}
SelfTokenOutput = strings.Join(tokens, ", ")
if isDebug {
log.Printf("%s: [DEBUG] FINAL[%s] (processing %s)", sitename, SelfTokenOutput, handlers.TimeTrack(start))
}
ctx.Response.SetStatusCode(200)
ctx.WriteString(fmt.Sprintf("%s OK %s\n", id, SelfTokenOutput))
return
//ctx.Response.SetStatusCode(200)
//ctx.WriteString(id + " OK first=ERROR\n")
////OLD OLD OLD ////
}
//func itChartClient(id string) string {
// if !SelfEnableITChart {
// if isDebug {
// log.Printf("%s: [DEBUG][ITCHART] return NONE", Selfsitename)
// }
//
// return ""
// }
// itChartRedirectURL := handlers.GetSocketInfoString("ITChartRedirectURL")
// if len(itChartRedirectURL) == 0 {
// if isDebug {
// log.Printf("%s: [DEBUG][ITCHART] return NONE", Selfsitename)
// log.Printf("[ERROR]: Redirect URL is not set, please add the redirect URL in configuration")
// }
// return ""
// }
// itChartRedirectURLArray := handlers.GetSocketInfoString("ITChartRedirectURLArray")
// if len(itChartRedirectURLArray) == 0 {
// if isDebug {
// log.Printf("%s: [DEBUG][ITCHART] return NONE", Selfsitename)
// log.Printf("[ERROR]: Redirect URL Array is not set, please add the redirect URL in configuration")
// }
// return ""
// }
// if isDebug {
// log.Printf("%s [ITCHART]: %s %s %s %s", Selfsitename, Selfipaddr, SelfMac, SelfUsername, SelfProtocol)
// }
// //TODO Check if make sense keep unserialzation
// if !itchart.ChartThis(Selfipaddr, SelfMac, SelfUsername, SelfProtocol, Selfsitename) {
// if isDebug {
// log.Printf("%s: [DEBUG][ITCHART] return NONE", Selfsitename)
// }
// return ""
// }
// var proto []string
// proto = append(proto, fmt.Sprintf("%d OK status=302", id))
// proto = append(proto, fmt.Sprintf("url=%s?Token=%s", itChartRedirectURL, itchart.SelfITChartMessage))
// proto = append(proto, "itchart=ASK\n")
// return strings.Join(proto, " ")
//
//}
func processTheShields() string {
SelfResolvedHosts = make(map[string]bool)
actionPass := []string{"WHITELIST", "PASS", "WHITE", "ERROR"}
if isDebug {
log.Printf("%s: [DEBUG]: [THE_SHIELD] Ask To the shield", Selfsitename)
}
category := 0
var categoryName string
var host string
action := "PASS"
ksrnPorn := shields.SelfKsrnPorn
disableAdvert := shields.SelfDisableAdvert
HatredAndDiscrimination := shields.SelfHatredAndDiscrimination
category, categoryName = categorization.GetCategories(Selfsitename)
if category > 0 {
if isDebug {
log.Printf("%s: [DEBUG]: [THE_SHIELD] Fix category answering [%d]", Selfsitename, category)
}
badCatz := []int{6, 7, 10, 72, 92, 105, 111, 135, 132, 109, 5, 143}
hatred := []int{130, 148, 149, 150, 140}
sPorn := []int{109, 132}
advert := []int{5, 143}
SelfResolvedHosts[Selfsitename] = true
for _, val := range sPorn { // Loop
if val == category {
if ksrnPorn {
action = "ARTICA"
}
break
}
}
for _, val := range advert { // Loop
if val == category {
if !disableAdvert {
action = "ARTICA"
}
break
}
}
for _, val := range hatred { // Loop
if val == category {
if HatredAndDiscrimination {
action = "ARTICA"
}
break
}
}
for _, val := range badCatz { // Loop
if val == category {
action = "ARTICA"
break
}
}
if isDebug {
log.Printf("%s: [DEBUG]: [THE_SHIELD] Shield result Scanner=%s", Selfsitename, action)
}
}
if _, ok := SelfResolvedHosts[Selfsitename]; ok {
// the key 'elliot' exists within the map
if SelfResolvedHosts[Selfsitename] {
host = "1"
}
if !SelfResolvedHosts[Selfsitename] {
if isDebug {
log.Printf("%s: [ERROR]: Unable to resolv host", Selfsitename)
}
if category == 0 {
category = 112
categoryName = categorization.CategoryIntToString(112)
}
resultsArray := make(map[string]interface{})
resultsArray["error"] = "UNKNOWN_HOST"
resultsArray["categoy_id"] = category
resultsArray["categoy_name"] = categoryName
resultsArray["ACTION"] = "PASS"
resultsArray["TOTAL_DURATION"] = 0
resultsArray["VIRTUAL_USER"] = SelfVirtualUser
resultsArray["COUNTRY_CODE"] = ""
resultsArray["HOSTIP"] = ""
Selfcategory = category
SelfcategoryName = categoryName
results, _ := gophp.Serialize(resultsArray)
return string(results)
}
}
if host == "" {
host = shields.GetHost(Selfsitename)
if isDebug {
log.Printf("%s: [DEBUG]: [THE_SHIELD] IP:<%s>", Selfsitename, host)
}
}
if host == "" {
SelfResolvedHosts[Selfsitename] = false
if isDebug {
log.Printf("%s: [ERROR]: Unable to resolv host", Selfsitename)
}
if category == 0 {
category = 112
categoryName = categorization.CategoryIntToString(112)
}
resultsArray := make(map[string]interface{})
resultsArray["error"] = "UNKNOWN_HOST"
resultsArray["categoy_id"] = category
resultsArray["categoy_name"] = categoryName
resultsArray["ACTION"] = "PASS"
resultsArray["TOTAL_DURATION"] = 0
resultsArray["VIRTUAL_USER"] = SelfVirtualUser
resultsArray["COUNTRY_CODE"] = ""
resultsArray["HOSTIP"] = ""
Selfcategory = category
SelfcategoryName = categoryName
results, _ := gophp.Serialize(resultsArray)
return string(results)
}
SelfResolvedHosts[Selfsitename] = true
if len(SelfResolvedHosts) > 5000 {
SelfResolvedHosts = make(map[string]bool)
}
shields.SelfShieldIpaddr = Selfipaddr
shields.SelfShieldUsername = SelfUsername
shields.SelfShieldMac = SelfMac
log.Printf("SHILDS OPERATE CAT %d", category)
if category == 0 {
log.Printf("SHILDS OPERATE")
shields.Operate(Selfsitename)
category = shields.SelfCategory
action = shields.SelfAction
categoryName = categorization.CategoryIntToString(category)
}
resultsArray := make(map[string]interface{})
resultsArray["error"] = shields.SelfError
resultsArray["categoy_id"] = category
resultsArray["categoy_name"] = categoryName
resultsArray["ACTION"] = action
resultsArray["TOTAL_DURATION"] = 0
resultsArray["VIRTUAL_USER"] = SelfVirtualUser
resultsArray["COUNTRY_CODE"] = ""
resultsArray["HOSTIP"] = host
Selfcategory = category
if isDebug {
log.Printf("%s: [DEBUG]: [LOCAL] ACTION=%s ERROR=%s", Selfsitename, action, shields.SelfError)
}
SelfcategoryName = categoryName
for _, val := range actionPass { // Loop
if val != action {
go shields.WriteStats(category, Selfsitename, action, 0)
if isDebug {
log.Printf("[THREAT_DETECTED]: site=%s addr=%s self.USERNAME=%s mac=%s category=%d/%s scanner=%s", Selfsitename, Selfipaddr, SelfUsername, SelfMac, category, categoryName, action)
}
//break
}
}
if isDebug {
log.Printf("Serializeing Results")
}
results, _ := gophp.Serialize(resultsArray)
if isDebug {
log.Printf("[SHIELDS RETURN]: results=%s", results)
}
return string(results)
}
func findKeyAccount() string {
if SelfUsername == "-" {
SelfUsername = ""
}
if Selfipaddr == "-" {
Selfipaddr = ""
}
if Selfipaddr == "127.0.0.1" {
Selfipaddr = ""
}
if SelfMac == "-" {
SelfMac = ""
}
if SelfMac == "00:00:00:00:00:00" {
SelfMac = ""
}
if len(SelfUsername) > 3 {
return SelfUsername
}
if len(SelfMac) > 3 {
return SelfMac
}
if len(Selfipaddr) > 3 {
return Selfipaddr
}
return ""
}
func isBlacklisted(sitename string) bool {
if len(sitename) < 3 {
return false
}
if categorization.AdminBlacklist(sitename, true) {
return true
}
return false
}
func isWhitelist(sitename string, mac string, ipaddr string) bool {
if sitename == "" {
return false
}
if mac == "" {
return false
}
if ipaddr == "" {
return false
}
if len(sitename) > 3 {
scache := fmt.Sprintf("DOMWHITE:%s", sitename)
sf := getCacheItem(scache)
if sf != "" {
return true
}
if categorization.AdminWhitelist(sitename, true) {
saveCachedItem(scache, "1")
return true
}
}
if len(mac) > 3 {
if categorization.AdminWhitelistMac(mac) {
return true
}
}
if len(ipaddr) > 3 {
if categorization.AdminWhitelistSrc(ipaddr) {
return true
}
}
return false
}
func saveCachedItem(smd5 string, sValue string) bool {
smd5 = fmt.Sprintf("SHIELD.serv.%s", smd5)
SelfLocalCache[smd5] = sValue
if len(SelfLocalCache) > SelfMaxItemsInMemory {
SelfLocalCache = map[string]string{}
}
storage.Append(smd5, sValue)
return true
}
func getCacheItem(md5 string) string {
//TODO Check if LocalCache Array is correct
if _, ok := SelfLocalCache[md5]; ok {
return SelfLocalCache[md5]
}
md5 = fmt.Sprintf("SHIELD.serv.%s", md5)
if val, err := storage.Fetch(md5); val != nil {
if len(*val) < 5 {
return ""
}
return *val
} else {
if isDebug {
log.Printf("Error Getting MD5 from memcache: %s", err)
}
//panic(err)
}
return ""
}
func buildErrorPage() string {
//ufdb := &ufdbguard.UFDBDefaultVars{}
var tokens []string
sitename := Selfsitename
category := 0
categoryName := ""
ruleID := 0
protocol := SelfProtocol
http := "http"
var protocolID int
if SelfEnableUfdbGuard {
category = Selfcategory
categoryName = SelfcategoryName
}
ruleID = ufdbguard.SelfRuleID
if category == 0 {
re := regexp.MustCompile("cinfo:([0-9]+)-(.+?);")
matches := re.MatchString(SelfSourceline)
rs := re.FindStringSubmatch(SelfSourceline)
if matches {
Selfcategory, _ = strconv.Atoi(rs[1])
SelfcategoryName = rs[2]
}
}
if protocol == "CONNECT" {
protocolID = 1
http = "https"
}
if protocol == "GET" {
protocolID = 2
}
if protocol == "POST" {
protocolID = 3
}
srcUrl := urlparse.QueryEscape(fmt.Sprintf("%s://%s", http, Selfsitename))
parameters := fmt.Sprintf("rule-id=%d&clientaddr=%s&clientname=%s&clientgroup=%s&targetgroup=%d&url=%s", ruleID, Selfipaddr, SelfUsername, SelfWebfilterRuleName, category, srcUrl)
lenOfRules := len(SelfWebPages)
finalredirectCode := 302
finalredirectType := 0
finalredirectKey := "url"
finalredirecturl := "http://articatech.net/block.html"
var ufdbgParameters string
if SelfEnableUfdbGuard {
ufdbgParameters = ufdbguard.SelfFinalRedirectUrl
}
if isDebug {
log.Printf("%s [DEBUG]: [BUILD_PAGE] final parameters <%s>", sitename, ufdbgParameters)
}
matched := false
parsed := ""
if lenOfRules > 0 {
for _, data := range SelfWebPages {
for k, v := range data.(map[string]interface{}) {
rCategory := 0
rRuleID := 0
rRedirectType := 0
rUrl := ""
rParsed := ""
rProto := 0
if k == "category" {
rCategory = v.(int)
}
if k == "ruleid" {
rRuleID = v.(int)
}
if k == "redirtype" {
rRedirectType = v.(int)
}
if k == "PARSED" {
if v.(map[string]interface{})["path"] == nil {
rParsed = ""
} else {
rParsed = v.(map[string]interface{})["path"].(string)
}
}
if k == "url" {
rUrl = v.(string)
}
if k == "protocol" {
rProto = v.(int)
}
if isDebug {
var slogs []string
slogs = append(slogs, fmt.Sprintf("Rule[%d]/%d", rRuleID, ruleID))
slogs = append(slogs, fmt.Sprintf("protocol[%d]/%d", rProto, protocolID))
slogs = append(slogs, fmt.Sprintf("category[%d]/%d", rCategory, category))
slogs = append(slogs, fmt.Sprintf("redirect[%s] type[%d]", rUrl, rRedirectType))
SlogsText := strings.Join(slogs, ", ")
log.Printf("%s [BUILD_PAGE]: index:%s must match %s", sitename, k, SlogsText)
}
if rRuleID == 0 {
if rCategory == 0 {
if rProto == 0 {
if isDebug {
log.Printf("%s [BUILD_PAGE]: !MATCHES! Detected url <%s>", sitename, rUrl)
}
finalredirecturl = rUrl
finalredirectType = rRedirectType
parsed = rParsed
matched = true
break
}
if rProto == protocolID {
if isDebug {
log.Printf("%s [BUILD_PAGE]: !MATCHES! Detected url <%s>", sitename, rUrl)
}
finalredirecturl = rUrl
finalredirectType = rRedirectType
parsed = rParsed
matched = true
break
}
}
}
if rRuleID == ruleID {
if rCategory == 0 {
if rProto == 0 {
if isDebug {
log.Printf("%s [BUILD_PAGE]: !MATCHES! Detected url <%s>", sitename, rUrl)
}
finalredirecturl = rUrl
finalredirectType = rRedirectType
parsed = rParsed
matched = true
break
}
if rProto == protocolID {
if isDebug {
log.Printf("%s [BUILD_PAGE]: !MATCHES! Detected url <%s>", sitename, rUrl)
}
finalredirecturl = rUrl
finalredirectType = rRedirectType
parsed = rParsed
matched = true
break
}
}
if rCategory == category {
if rProto == 0 {
if isDebug {
log.Printf("%s [BUILD_PAGE]: !MATCHES! Detected url <%s>", sitename, rUrl)
}
finalredirecturl = rUrl
finalredirectType = rRedirectType
parsed = rParsed
matched = true
break
}
if rProto == protocolID {
if isDebug {
log.Printf("%s [BUILD_PAGE]: !MATCHES! Detected url <%s>", sitename, rUrl)
}
finalredirecturl = rUrl
finalredirectType = rRedirectType
parsed = rParsed
matched = true
break
}
}
} else {
if isDebug {
log.Printf("%s [BUILD_PAGE]: False for %d is not %d", sitename, rRuleID, ruleID)
}
}
}
}
}
if isDebug {
log.Printf("parsed (%s)", parsed)
if !matched {
log.Printf("%s [BUILD_PAGE]: NO_MATCHES!! rule[%d] category[%d] Proto[%d] (%s)", sitename, ruleID, category, protocolID, protocol)
}
}
if finalredirectType == 0 {
finalredirectCode = 302
}
if finalredirectType == 1 {
finalredirectCode = 301
}
re := regexp.MustCompile("^(http|https:)")
matches := re.MatchString(finalredirecturl)
if matches {
finalredirecturl = fmt.Sprintf("%s?%s", finalredirecturl, parameters)
}
tmpStr := fmt.Sprintf("status=%d %s=%s", finalredirectCode, finalredirectKey, finalredirecturl)
if finalredirectType == 2 {
tmpStr = fmt.Sprintf("rewrite-url=%s", finalredirecturl)
}
if finalredirectType == 2 {
tmpStr = fmt.Sprintf("status=%d %s=%s", 302, "url", "http://artica.me")
}
if len(tmpStr) > 3 {
tokens = append(tokens, tmpStr)
}
tokens = append(tokens, fmt.Sprintf("category=%d category-name=%s clog=cinfo:%d-%s;", category, categoryName, category, categoryName))
if SelfEnableUfdbGuard {
tokens = append(tokens, ufdbguard.SelfWebfilteringToken)
}
final := strings.Join(tokens, " ")
return final
}
func virtualUsers() string {
ipStrongSwan := ""
if strings.Index(SelfSourceline, "user:%20") > 10 {
re := regexp.MustCompile("user:%20(.+?)%0D%0A")
matches := re.MatchString(SelfSourceline)
if matches {
rs := re.FindStringSubmatch(SelfSourceline)
SelfUsername = rs[1]
return SelfUsername
}
}
Key := fmt.Sprintf("%s.%s", SelfMac, Selfipaddr)
if len(SelfVirtualUserCache) > 5000 {
SelfVirtualUserCache = map[string]string{}
}
if _, ok := SelfVirtualUserCache[Key]; ok {
return SelfVirtualUserCache[Key]
}
if SelfEnableStrongswanServer == 1 {
ipStrongSwan = Selfipaddr
}
sresult := storage.UserAliases(SelfMac, Selfipaddr, ipStrongSwan, isDebug)
if sresult == "" {
SelfVirtualUserCache[Key] = ""
}
SelfVirtualUserCache[Key] = sresult
return sresult
}
func parseLine(line string) map[string]string {
results := map[string]string{}
lparts := strings.Split(strings.TrimRight(line, "\n"), " ")
if isDebug {
log.Printf("Parsing entity <%s>", lparts[1])
}
if lparts[1] == "MacToUid_acl" {
return parseLineAcl(line)
}
results["acl"] = strconv.Itoa(0)
results["url"] = strings.TrimSpace(lparts[1])
iphost := strings.Split(lparts[2], "/")
results["ipaddr"] = iphost[0]
results["hostname"] = iphost[1]
results["username"] = strings.TrimSpace(lparts[3])
if isDebug {
log.Printf("[WEBFILTERING] - - - parseLine - - - PROTO <%s>", strings.TrimSpace(lparts[4]))
}
results["proto"] = strings.TrimSpace(lparts[4])
SelfProtocol = results["proto"]
for _, lines := range lparts {
if strings.Index(lines, "=") > 2 {
chop := strings.Split(lines, "=")
results[chop[0]] = chop[1]
}
}
return results
}
func parseLineAcl(line string) map[string]string {
results := map[string]string{}
lparts := strings.Split(strings.TrimRight(line, "\n"), " ")
results["acl"] = strconv.Itoa(1)
results["username"] = strings.TrimSpace(lparts[2])
results["ipaddr"] = strings.TrimSpace(lparts[3])
results["mac"] = strings.TrimSpace(lparts[4])
results["forwardedfor"] = strings.TrimSpace(lparts[5])
xForward := strings.TrimSpace(lparts[5])
results["domain"] = strings.TrimSpace(lparts[6])
results["sni"] = strings.TrimSpace(lparts[7])
ssni := strings.TrimSpace(lparts[7])
//user_cert := strings.TrimSpace(lparts[8])
//notes := strings.TrimSpace(lparts[9])
//server_ip := strings.TrimSpace(lparts[10])
//server_fqdn := strings.TrimSpace(lparts[11])
results["proto"] = strings.TrimSpace(lparts[12])
SelfProtocol = results["proto"]
if len(xForward) > 3 {
results["ipaddr"] = xForward
}
if len(ssni) > 3 {
results["domain"] = ssni
}
results["myport"] = strconv.Itoa(3128)
results["myip"] = "127.0.0.1"
results["hostname"] = ""
results["url"] = strings.TrimSpace(lparts[6])
return results
}
func ufdbGuardClient(id string, line string, proxyUrl string, sitename string, clientIp string, clientHostname string, clientMac string, proxyIP string, proxyPort int) string {
isBreak := false
if strings.Index(line, "srn=WHITE") > 10 {
isBreak = true
}
if strings.Index(line, "rblpass=yes") > 10 {
isBreak = true
}
if strings.Index(line, "webfilter=pass") > 10 {
isBreak = true
}
category := 0
categoryName := ""
//var LogText string
if isBreak {
if isDebug {
log.Printf("%s [WEBFILTERING] Breakable!", sitename)
}
return ""
}
if isDebug {
log.Printf("%s [WEBFILTERING] rules with %s [%s] user=%s", sitename, proxyUrl, sitename, SelfUsername)
}
//LogText = fmt.Sprintf("sitename=\"%s\" src=\"%s\" host=\"%s\" user=\"%s\" mac=\"%s\" proxy=\"%s:%d\"", sitename, clientIp, clientHostname, SelfUsername, clientMac, proxyIP, proxyPort)
if ufdbguard.Process(proxyUrl, sitename, clientIp, clientHostname, SelfUsername, clientMac, proxyIP, proxyPort) {
if ufdbguard.SelfInactiveService {
//LogText = ""
if isDebug {
log.Printf("%s [WEBFILTERING] INACTIVE SERVICE!", sitename)
return ""
}
}
SelfWebfilterRuleName = ufdbguard.SelfWebfilterRuleName
if ufdbguard.SelfCategory == 0 {
category, categoryName = categorization.GetCategories(sitename)
}
if category == 0 {
category = ufdbguard.SelfCategory
categoryName = ufdbguard.SelfCategoryName
}
Selfcategory = category
SelfcategoryName = categoryName
redirect := buildErrorPage()
if isDebug {
log.Printf("%s [WEBFILTERING] OUT OF <%s>", sitename, fmt.Sprintf("%s %s %s \n", id, SelfTokenOutput, redirect))
}
return fmt.Sprintf("%s %s %s \n", id, SelfTokenOutput, redirect)
}
return ""
}