diff --git a/changelog.md b/changelog.md index daf142c..0269b49 100644 --- a/changelog.md +++ b/changelog.md @@ -6,7 +6,8 @@ - New color slider with reset buttons - Fixed blinking modals issues - Added lazyloading to URL and Servapp pages images - - Added a button in the config page to easily download the docker backup + - Added a dangerous IP detector that stops sending HTTP response to IPs that are abusing various shields features + - Added a button in the servapp page to easily download the docker backup - Improve display or icons [fixes #121] - Refactored Mongo connection code [fixes #111] - Forward simultaneously TCP and UDP [fixes #122] diff --git a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx index 162d325..93310ee 100644 --- a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx +++ b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx @@ -70,6 +70,15 @@ const NavItem = ({ item, level }) => { transform: 'translateY(-5px)', display: 'inline-block', }}>Beta; + + if(item.title === "Monitoring") + item.title = <>{item.title} New!; return ( {
- - Description + + Description
{routeConfig.Description}
- URL + URL
- Target + Target
- Security + Security
+ Monitoring +
+ + +
diff --git a/client/src/pages/config/users/proxyman.jsx b/client/src/pages/config/users/proxyman.jsx index f0d212e..3003226 100644 --- a/client/src/pages/config/users/proxyman.jsx +++ b/client/src/pages/config/users/proxyman.jsx @@ -40,6 +40,7 @@ import {RouteActions, RouteMode, RouteSecurity} from '../../../components/routeC import { useNavigate } from 'react-router'; import NewRouteCreate from '../routes/newRoute'; import LazyLoad from 'react-lazyload'; +import MiniPlotComponent from '../../dashboard/components/mini-plot'; const stickyButton = { position: 'fixed', @@ -173,6 +174,14 @@ const ProxyManagement = () => {
{r.Description}
}, + { title: 'Network', screenMin: 'lg', clickable:false, field: (r) => +
+ +
+ }, { title: 'Origin', screenMin: 'md', clickable:true, search: (r) => r.Host + ' ' + r.PathPrefix, field: (r) => }, { title: 'Target', screenMin: 'md', search: (r) => r.Target, field: (r) => <> }, { title: 'Security', screenMin: 'lg', field: (r) => , diff --git a/client/src/pages/dashboard/components/mini-plot.jsx b/client/src/pages/dashboard/components/mini-plot.jsx index b1e3006..de94b32 100644 --- a/client/src/pages/dashboard/components/mini-plot.jsx +++ b/client/src/pages/dashboard/components/mini-plot.jsx @@ -30,7 +30,7 @@ import { FormaterForMetric, formatDate, toUTC } from './utils'; import * as API from '../../../api'; -const _MiniPlotComponent = ({metrics, labels}) => { +const _MiniPlotComponent = ({metrics, labels, noLabels, noBackground}) => { const theme = useTheme(); const { primary, secondary } = theme.palette.text; const [dataMetrics, setDataMetrics] = useState([]); @@ -131,7 +131,7 @@ const _MiniPlotComponent = ({metrics, labels}) => { show: false } }, - yaxis: dataMetrics.map((data) => ({ + yaxis: dataMetrics.length ? dataMetrics.map((data) => ({ labels: { show: false }, @@ -141,7 +141,17 @@ const _MiniPlotComponent = ({metrics, labels}) => { axisTicks: { show: false } - })), + })) : { + labels: { + show: false + }, + axisBorder: { + show: false + }, + axisTicks: { + show: false + } + }, tooltip: { enabled: false }, @@ -160,18 +170,18 @@ const _MiniPlotComponent = ({metrics, labels}) => { const formaters = dataMetrics.map((data) => FormaterForMetric(data)); return - {dataMetrics && dataMetrics.map((dataMetric, di) => + {!noLabels && dataMetrics && dataMetrics.map((dataMetric, di) => - {dataMetric.Values.length &&
{formaters[di](dataMetric.Values[dataMetric.Values.length - 1].Value)}
+ }}>{formaters[di](dataMetric.Values[dataMetric.Values.length - 1].Value)} : formaters[di](0) }
{ } -const MiniPlotComponent = ({ metrics, labels }) => { - const memoizedComponent = useMemo(() => <_MiniPlotComponent metrics={metrics} labels={labels} />, [metrics]); +const MiniPlotComponent = ({ metrics, labels, noLabels, noBackground }) => { + const memoizedComponent = useMemo(() => <_MiniPlotComponent noBackground={noBackground} noLabels={noLabels} metrics={metrics} labels={labels} />, [metrics]); return memoizedComponent; }; diff --git a/client/src/pages/dashboard/components/plot.jsx b/client/src/pages/dashboard/components/plot.jsx index 6040f94..ed5068a 100644 --- a/client/src/pages/dashboard/components/plot.jsx +++ b/client/src/pages/dashboard/components/plot.jsx @@ -95,6 +95,7 @@ const PlotComponent = ({ title, slot, data, SimpleDesign, withSelector, xAxis, z theme.palette.primary.main.replace('rgb(', 'rgba('), theme.palette.secondary.main.replace('rgb(', 'rgba('), theme.palette.error.main.replace('rgb(', 'rgba('), + theme.palette.warning.main.replace('rgb(', 'rgba('), ], xaxis: { categories: @@ -115,7 +116,7 @@ const PlotComponent = ({ title, slot, data, SimpleDesign, withSelector, xAxis, z max: zoom.xaxis && zoom.xaxis.max, }, yaxis: toProcess.map((thisdata, ida) => ({ - opposite: ida === 0, + opposite: ida % 2 === 1, labels: { style: { colors: [secondary], diff --git a/client/src/pages/dashboard/components/table.jsx b/client/src/pages/dashboard/components/table.jsx index 3399010..14d1922 100644 --- a/client/src/pages/dashboard/components/table.jsx +++ b/client/src/pages/dashboard/components/table.jsx @@ -56,10 +56,18 @@ function formatDate(now, time) { } function descendingComparator(a, b, orderBy) { - if (parseFloat(b[orderBy]) < parseFloat(a[orderBy])) { + let a1 = a[orderBy]; + let b1 = b[orderBy]; + + if(orderBy != 'name') { + a1 = parseFloat(a["__" + orderBy]); + b1 = parseFloat(b["__" + orderBy]); + } + + if (b1 < a1) { return -1; } - if (parseFloat(b[orderBy]) > parseFloat(a[orderBy])) { + if (b1 > a1) { return 1; } return 0; @@ -162,7 +170,8 @@ const TableComponent = ({ title, data, displayMax, render, xAxis, slot, zoom}) = return 0; } } else { - return item.Values[date] ? item.Values[date].Value : 0; + let realIndex = item.Values.length - 1 - date + return item.Values[realIndex] ? item.Values[realIndex].Value : 0; } }) .reduce((a, b) => { @@ -304,7 +313,7 @@ const TableComponent = ({ title, data, displayMax, render, xAxis, slot, zoom}) = > - {stableSort(rows, getComparator(order, "__" + orderBy)).map((row, index) => { + {stableSort(rows, getComparator(order, orderBy)).map((row, index) => { const isItemSelected = false // isSelected(row.trackingNo); const labelId = `enhanced-table-checkbox-${index}`; diff --git a/client/src/pages/dashboard/components/utils.jsx b/client/src/pages/dashboard/components/utils.jsx index ad3662c..ebd4f27 100644 --- a/client/src/pages/dashboard/components/utils.jsx +++ b/client/src/pages/dashboard/components/utils.jsx @@ -1,5 +1,5 @@ export const simplifyNumber = (num, unit) => { - if(!num) return 0; + if(typeof num == 'undefined' || num === null) return 0; num = Math.round(num * 100) / 100; diff --git a/client/src/pages/dashboard/proxyDashboard.jsx b/client/src/pages/dashboard/proxyDashboard.jsx index 81be379..7e35cf1 100644 --- a/client/src/pages/dashboard/proxyDashboard.jsx +++ b/client/src/pages/dashboard/proxyDashboard.jsx @@ -1,10 +1,12 @@ import { Grid, LinearProgress, + Tooltip, } from '@mui/material'; import PlotComponent from './components/plot'; import TableComponent from './components/table'; +import { InfoCircleOutlined } from '@ant-design/icons'; const ProxyDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => { console.log(metrics) @@ -25,7 +27,6 @@ const ProxyDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => { ]} /> - key.startsWith("cosmos.proxy.route.")).map((key) => metrics[key]) } /> @@ -37,7 +38,17 @@ const ProxyDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => { - + Reasons For Blocked Requests +
bots: Bots
+
geo: By Geolocation (blocked countries)
+
referer: By Referer
+
hostname: By Hostname (usually IP scanning threat)
+
ip-whitelists: By IP Whitelists (Including restricted to Constellation)
+
smart-shield: Smart Shield (various abuse metrics such as time, size, brute-force, concurrent requests, etc...). It does not include blocking for banned IP to save resources in case of potential attacks
+
}> + } data={ Object.keys(metrics).filter((key) => key.startsWith("cosmos.proxy.blocked.")).map((key) => metrics[key]) } /> diff --git a/client/src/pages/dashboard/resourceDashboard.jsx b/client/src/pages/dashboard/resourceDashboard.jsx index 11fc6cb..7fbb857 100644 --- a/client/src/pages/dashboard/resourceDashboard.jsx +++ b/client/src/pages/dashboard/resourceDashboard.jsx @@ -14,7 +14,7 @@ const ResourceDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => { - key.startsWith("cosmos.system.docker.cpu") || key.startsWith("cosmos.system.docker.ram")).map((key) => metrics[key]) } /> @@ -22,7 +22,7 @@ const ResourceDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => { - key.startsWith("cosmos.system.docker.net")).map((key) => metrics[key]) } /> diff --git a/client/src/pages/home/index.jsx b/client/src/pages/home/index.jsx index d6e06b2..da3113c 100644 --- a/client/src/pages/home/index.jsx +++ b/client/src/pages/home/index.jsx @@ -13,6 +13,7 @@ import IsLoggedIn from "../../isLoggedIn"; import { ServAppIcon } from "../../utils/servapp-icon"; import Chart from 'react-apexcharts'; import { useClientInfos } from "../../utils/hooks"; +import { FormaterForMetric, formatDate } from "../dashboard/components/utils"; export const HomeBackground = () => { @@ -90,6 +91,7 @@ const HomePage = () => { const isMd = useMediaQuery(theme.breakpoints.up('md')); const {role} = useClientInfos(); const isAdmin = role === "2"; + const [metrics, setMetrics] = useState(null); const blockStyle = { margin: 0, @@ -114,6 +116,43 @@ const HomePage = () => { }); } + function getMetrics() { + API.metrics.get([ + "cosmos.system.cpu.0", + "cosmos.system.ram", + "cosmos.system.netTx", + "cosmos.system.netRx", + "cosmos.proxy.all.success", + "cosmos.proxy.all.error", + ]).then((res) => { + setMetrics(prevMetrics => { + let finalMetrics = prevMetrics ? { ...prevMetrics } : {}; + if(res.data) { + res.data.forEach((metric) => { + finalMetrics[metric.Key] = metric; + }); + + return finalMetrics; + } + }); + }); + } + + useEffect(() => { + refreshStatus(); + + let interval = setInterval(() => { + if(isMd) + getMetrics(); + }, 10000); + + if(isMd) getMetrics(); + + return () => { + clearInterval(interval); + }; + }, []); + const refreshConfig = () => { if(isAdmin) { API.docker.list().then((res) => { @@ -185,11 +224,11 @@ const HomePage = () => { }, value: { formatter: function (val) { - return val + "%"; + return val + "%" }, color: "#111", - offsetY: 9, - fontSize: "24px", + offsetY: 7, + fontSize: "18px", show: true } } @@ -214,6 +253,39 @@ const HomePage = () => { labels: [] }; + const bigNb = { + fontSize: '24px', + fontWeight: "bold", + textAlign: "center", + color: isDark ? "white" : "black", + textShadow: "0px 0px 5px #000", + lineHeight: "97px", + } + + let latestCPU = metrics && metrics["cosmos.system.cpu.0"] && metrics["cosmos.system.cpu.0"].Values[metrics["cosmos.system.cpu.0"].Values.length - 1].Value; + + let formatRAM = metrics && FormaterForMetric(metrics["cosmos.system.ram"], false); + let latestRAMRaw = metrics && metrics["cosmos.system.ram"] && metrics["cosmos.system.ram"].Values[metrics["cosmos.system.ram"].Values.length - 1].Value; + let latestRAM = metrics && metrics["cosmos.system.ram"] && formatRAM(metrics["cosmos.system.ram"].Values[metrics["cosmos.system.ram"].Values.length - 1].Value); + let maxRAM = metrics && metrics["cosmos.system.ram"] && formatRAM(metrics["cosmos.system.ram"].Max); + let maxRAMRaw = metrics && metrics["cosmos.system.ram"] && metrics["cosmos.system.ram"].Max; + + let now = new Date(); + now = "day_" + formatDate(now); + + let formatNetwork = metrics && FormaterForMetric(metrics["cosmos.system.netTx"], false); + let latestNetworkRawTx = (metrics && metrics["cosmos.system.netTx"] && metrics["cosmos.system.netTx"].ValuesAggl[now].Value) || 0; + let latestNetworkTx = metrics && formatNetwork(latestNetworkRawTx); + let latestNetworkRawRx = (metrics && metrics["cosmos.system.netRx"] && metrics["cosmos.system.netRx"].ValuesAggl[now].Value) || 0; + let latestNetworkRx = metrics && formatNetwork(latestNetworkRawRx); + let latestNetworkSum = metrics && formatNetwork(latestNetworkRawTx + latestNetworkRawRx); + + let formatRequests = metrics && FormaterForMetric(metrics["cosmos.proxy.all.success"], false); + let latestRequestsRaw = (metrics && metrics["cosmos.proxy.all.success"] && metrics["cosmos.proxy.all.success"].ValuesAggl[now].Value) || 0; + let latestRequests = metrics && formatRequests(latestRequestsRaw); + let latestRequestsErrorRaw = (metrics && metrics["cosmos.proxy.all.error"] && metrics["cosmos.proxy.all.error"].ValuesAggl[now].Value) || 0; + let latestRequestsError = metrics && formatRequests(latestRequestsErrorRaw); + let latestRequestSum = metrics && formatRequests(latestRequestsRaw + latestRequestsErrorRaw); return @@ -276,72 +348,139 @@ const HomePage = () => { - {/* {(!isMd || !coStatus || !coStatus.resources || !coStatus.resources.cpu ) && (<> - - - -
CPU
-
--
-
--
-
-
-
- - - -
RAM
-
Available: --GB
-
Used: --GB
-
-
-
- )} - - {isMd && coStatus && coStatus.resources.cpu && (<> - - - -
- -
- -
CPU
-
{coStatus.CPU}
-
{coStatus.AVX ? "AVX Supported" : "No AVX Support"}
-
-
-
-
- - - -
- -
- + {coStatus && !coStatus.MonitoringDisabled && (<> + {isMd && !metrics && (<> + + + +
CPU
+
--
+
--
+
+
+
+ + +
RAM
-
Available: {Math.ceil((coStatus.resources.ram + coStatus.resources.ramFree) / 1024 / 1024 / 1024)}GB
-
Used: {Math.ceil(coStatus.resources.ram / 1024 / 1024 / 1024)}GB
+
Available: -- GB
+
Used: -- GB
+
+
+
+ + + +
+
+ - +
+
+ +
Network
+
Tx: -
+
Rx: -
+
-
-
-
- - )} */} + +
+ + + +
+
+ - +
+
+ +
Requests
+
Success: -
+
Error: -
+
+
+
+
+ )} + + {isMd && metrics && (<> + + + +
+ +
+ +
CPU
+
{coStatus.CPU}
+
{coStatus.AVX ? "AVX Supported" : "No AVX Support"}
+
+
+
+
+ + + +
+ +
+ +
RAM
+
Available: {maxRAM}
+
Used: {latestRAM}
+
+
+
+
+ + + +
+
+ {latestNetworkSum} +
+
+ +
Network
+
Tx: {latestNetworkTx}
+
Rx: {latestNetworkRx}
+
+
+
+
+ + + +
+
+ {latestRequestSum} +
+
+ +
Requests
+
Success: {latestRequests}
+
Error: {latestRequestsError}
+
+
+
+
+ + )} + )} {config && servApps && routes.map((route) => { let skip = route.Mode == "REDIRECT"; diff --git a/package.json b/package.json index 89429cc..7ab15e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.12.0-unstable32", + "version": "0.12.0-unstable33", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/httpServer.go b/src/httpServer.go index 28df340..4ef24e5 100644 --- a/src/httpServer.go +++ b/src/httpServer.go @@ -275,7 +275,8 @@ func InitServer() *mux.Router { utils.Log("Initialising HTTP(S) Router and all routes") router := mux.NewRouter().StrictSlash(true) - router.HandleFunc("/logo", SendLogo) + + router.Use(utils.BlockBannedIPs) router.Use(middleware.Logger) @@ -283,6 +284,8 @@ func InitServer() *mux.Router { router.Use(utils.BlockByCountryMiddleware(config.BlockedCountries, config.CountryBlacklistIsWhitelist)) } + router.HandleFunc("/logo", SendLogo) + srapi := router.PathPrefix("/cosmos").Subrouter() srapi.HandleFunc("/api/dns", GetDNSRoute) diff --git a/src/proxy/routerGen.go b/src/proxy/routerGen.go index f7748ea..e93c98f 100644 --- a/src/proxy/routerGen.go +++ b/src/proxy/routerGen.go @@ -87,6 +87,14 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt destination = utils.Restrictions(route.RestrictToConstellation, route.WhitelistInboundIPs)(destination) + if route.BlockCommonBots { + destination = BotDetectionMiddleware(destination) + } + + if route.BlockAPIAbuse { + destination = utils.BlockPostWithoutReferer(destination) + } + destination = SmartShieldMiddleware(route.Name, route)(destination) originCORS := route.CORSOrigin @@ -133,14 +141,6 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt destination = utils.BandwithLimiterMiddleware(route.MaxBandwith)(destination) } - if route.BlockCommonBots { - destination = BotDetectionMiddleware(destination) - } - - if route.BlockAPIAbuse { - destination = utils.BlockPostWithoutReferer(destination) - } - if !route.DisableHeaderHardening { destination = utils.SetSecurityHeaders(destination) } diff --git a/src/proxy/shield.go b/src/proxy/shield.go index c5905b7..e90d441 100644 --- a/src/proxy/shield.go +++ b/src/proxy/shield.go @@ -165,6 +165,7 @@ func (shield *smartShieldState) isAllowedToReqest(shieldID string, policy utils. banType: PERM, time: time.Now(), }) + utils.Warn("User " + ClientID + " has been banned permanently: "+ fmt.Sprintf("%+v", userConsumed)) return false } else if nbStrikes >= 3 { @@ -355,6 +356,7 @@ func SmartShieldMiddleware(shieldID string, route utils.ProxyRouteConfig) func(h if !isPrivileged(r, policy) && !shield.isAllowedToReqest(shieldID, policy, userConsumed) { lastBan := shield.GetLastBan(policy, userConsumed) go metrics.PushShieldMetrics("smart-shield") + utils.IncrementIPAbuseCounter(clientID) utils.Log("SmartShield: User is blocked due to abuse: " + fmt.Sprintf("%+v", lastBan)) http.Error(w, "Too many requests", http.StatusTooManyRequests) return diff --git a/src/status.go b/src/status.go index ca7a3ec..2058991 100644 --- a/src/status.go +++ b/src/status.go @@ -63,6 +63,7 @@ func StatusRoute(w http.ResponseWriter, req *http.Request) { "CPU": runtime.GOARCH, "AVX": cpu.X86.HasAVX, "LetsEncryptErrors": utils.LetsEncryptErrors, + "MonitoringDisabled": utils.GetMainConfig().MonitoringDisabled, }, }) } else { diff --git a/src/utils/middleware.go b/src/utils/middleware.go index 11cf288..836d0c3 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -7,6 +7,8 @@ import ( "net" "strings" "fmt" + "sync" + "sync/atomic" "github.com/mxk/go-flowrate/flowrate" "github.com/oschwald/geoip2-golang" @@ -16,6 +18,66 @@ import ( var PushShieldMetrics func(string) +type safeInt struct { + val int64 +} + +var BannedIPs = sync.Map{} + +// Close connection right away if banned (save resources) + +func IncrementIPAbuseCounter(ip string) { + // Load or store a new *safeInt + actual, _ := BannedIPs.LoadOrStore(ip, &safeInt{}) + counter := actual.(*safeInt) + + // Increment the counter using atomic for concurrent access + atomic.AddInt64(&counter.val, 1) +} + +func getIPAbuseCounter(ip string) int64 { + // Load the *safeInt + actual, ok := BannedIPs.Load(ip) + if !ok { + return 0 + } + counter := actual.(*safeInt) + + // Load the value using atomic for concurrent access + return atomic.LoadInt64(&counter.val) +} + +func BlockBannedIPs(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + if hj, ok := w.(http.Hijacker); ok { + conn, _, err := hj.Hijack() + if err == nil { + conn.Close() + } + } + return + } + + nbAbuse := getIPAbuseCounter(ip) + + // Debug("IP " + ip + " has " + fmt.Sprintf("%d", nbAbuse) + " abuse(s)") + + if nbAbuse > 1000 { + if hj, ok := w.(http.Hijacker); ok { + conn, _, err := hj.Hijack() + if err == nil { + conn.Close() + } + } + return + } + + next.ServeHTTP(w, r) + }) +} + func MiddlewareTimeout(timeout time.Duration) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { @@ -151,6 +213,7 @@ func BlockByCountryMiddleware(blockedCountries []string, CountryBlacklistIsWhite if blocked { PushShieldMetrics("geo") + IncrementIPAbuseCounter(ip) http.Error(w, "Access denied", http.StatusForbidden) return } @@ -183,6 +246,12 @@ func BlockPostWithoutReferer(next http.Handler) http.Handler { PushShieldMetrics("referer") Error("Blocked POST request without Referer header", nil) http.Error(w, "Bad Request: Invalid request.", http.StatusBadRequest) + + ip, _, _ := net.SplitHostPort(r.RemoteAddr) + if ip != "" { + IncrementIPAbuseCounter(ip) + } + return } } @@ -221,6 +290,12 @@ func EnsureHostname(next http.Handler) http.Handler { Error("Invalid Hostname " + r.Host + " for request. Expecting one of " + fmt.Sprintf("%v", hostnames), nil) w.WriteHeader(http.StatusBadRequest) http.Error(w, "Bad Request: Invalid hostname. Use your domain instead of your IP to access your server. Check logs if more details are needed.", http.StatusBadRequest) + + ip, _, _ := net.SplitHostPort(r.RemoteAddr) + if ip != "" { + IncrementIPAbuseCounter(ip) + } + return } @@ -312,12 +387,14 @@ func Restrictions(RestrictToConstellation bool, WhitelistInboundIPs []string) fu if(!isInConstellation) { if(!isUsingWhiteList) { PushShieldMetrics("ip-whitelists") + IncrementIPAbuseCounter(ip) Error("Request from " + ip + " is blocked because of restrictions", nil) Debug("Blocked by RestrictToConstellation isInConstellation isUsingWhiteList") http.Error(w, "Access denied", http.StatusForbidden) return } else if (!isInWhitelist) { PushShieldMetrics("ip-whitelists") + IncrementIPAbuseCounter(ip) Error("Request from " + ip + " is blocked because of restrictions", nil) Debug("Blocked by RestrictToConstellation isInConstellation isInWhitelist") http.Error(w, "Access denied", http.StatusForbidden) @@ -326,6 +403,7 @@ func Restrictions(RestrictToConstellation bool, WhitelistInboundIPs []string) fu } } else if(isUsingWhiteList && !isInWhitelist) { PushShieldMetrics("ip-whitelists") + IncrementIPAbuseCounter(ip) Error("Request from " + ip + " is blocked because of restrictions", nil) Debug("Blocked by RestrictToConstellation isInConstellation isUsingWhiteList isInWhitelist") http.Error(w, "Access denied", http.StatusForbidden)