[release] v0.12.0-unstable33
This commit is contained in:
parent
6a5f9b74fe
commit
5b8622ff57
|
@ -6,7 +6,8 @@
|
||||||
- New color slider with reset buttons
|
- New color slider with reset buttons
|
||||||
- Fixed blinking modals issues
|
- Fixed blinking modals issues
|
||||||
- Added lazyloading to URL and Servapp pages images
|
- 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]
|
- Improve display or icons [fixes #121]
|
||||||
- Refactored Mongo connection code [fixes #111]
|
- Refactored Mongo connection code [fixes #111]
|
||||||
- Forward simultaneously TCP and UDP [fixes #122]
|
- Forward simultaneously TCP and UDP [fixes #122]
|
||||||
|
|
|
@ -71,6 +71,15 @@ const NavItem = ({ item, level }) => {
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
}}>Beta</span></>;
|
}}>Beta</span></>;
|
||||||
|
|
||||||
|
if(item.title === "Monitoring")
|
||||||
|
item.title = <>{item.title} <span style={{
|
||||||
|
color: 'gray',
|
||||||
|
fontSize: '11px',
|
||||||
|
textDecoration: 'italic',
|
||||||
|
transform: 'translateY(-5px)',
|
||||||
|
display: 'inline-block',
|
||||||
|
}}>New!</span></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
{...listItemProps}
|
{...listItemProps}
|
||||||
|
|
|
@ -6,11 +6,12 @@ import HostChip from '../../../components/hostChip';
|
||||||
import { RouteMode, RouteSecurity } from '../../../components/routeComponents';
|
import { RouteMode, RouteSecurity } from '../../../components/routeComponents';
|
||||||
import { getFaviconURL } from '../../../utils/routes';
|
import { getFaviconURL } from '../../../utils/routes';
|
||||||
import * as API from '../../../api';
|
import * as API from '../../../api';
|
||||||
import { CheckOutlined, ClockCircleOutlined, DashboardOutlined, DeleteOutlined, DownOutlined, LockOutlined, UpOutlined } from "@ant-design/icons";
|
import { CheckOutlined, ClockCircleOutlined, ContainerOutlined, DashboardOutlined, DeleteOutlined, DownOutlined, InfoCircleFilled, InfoCircleOutlined, LockOutlined, NodeExpandOutlined, SafetyCertificateOutlined, UpOutlined } from "@ant-design/icons";
|
||||||
import IsLoggedIn from '../../../isLoggedIn';
|
import IsLoggedIn from '../../../isLoggedIn';
|
||||||
import { redirectToLocal } from '../../../utils/indexs';
|
import { redirectToLocal } from '../../../utils/indexs';
|
||||||
import { CosmosCheckbox } from '../users/formShortcuts';
|
import { CosmosCheckbox } from '../users/formShortcuts';
|
||||||
import { Field } from 'formik';
|
import { Field } from 'formik';
|
||||||
|
import MiniPlotComponent from '../../dashboard/components/mini-plot';
|
||||||
|
|
||||||
const info = {
|
const info = {
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
@ -40,15 +41,32 @@ const RouteOverview = ({ routeConfig }) => {
|
||||||
<div>
|
<div>
|
||||||
<img className="loading-image" alt="" src={getFaviconURL(routeConfig)} width="128px" />
|
<img className="loading-image" alt="" src={getFaviconURL(routeConfig)} width="128px" />
|
||||||
</div>
|
</div>
|
||||||
<Stack spacing={2} >
|
<Stack spacing={2} style={{ width: '100%' }}>
|
||||||
<strong>Description</strong>
|
<strong><ContainerOutlined />Description</strong>
|
||||||
<div style={info}>{routeConfig.Description}</div>
|
<div style={info}>{routeConfig.Description}</div>
|
||||||
<strong>URL</strong>
|
<strong><NodeExpandOutlined /> URL</strong>
|
||||||
<div><HostChip route={routeConfig} /></div>
|
<div><HostChip route={routeConfig} /></div>
|
||||||
<strong>Target</strong>
|
<strong><InfoCircleOutlined /> Target</strong>
|
||||||
<div><RouteMode route={routeConfig} /> <Chip label={routeConfig.Target} /></div>
|
<div><RouteMode route={routeConfig} /> <Chip label={routeConfig.Target} /></div>
|
||||||
<strong>Security</strong>
|
<strong><SafetyCertificateOutlined/> Security</strong>
|
||||||
<div><RouteSecurity route={routeConfig} /></div>
|
<div><RouteSecurity route={routeConfig} /></div>
|
||||||
|
<strong><DashboardOutlined/> Monitoring</strong>
|
||||||
|
<div>
|
||||||
|
<MiniPlotComponent metrics={[
|
||||||
|
"cosmos.proxy.route.success." + routeConfig.Name,
|
||||||
|
"cosmos.proxy.route.error." + routeConfig.Name,
|
||||||
|
]} labels={{
|
||||||
|
["cosmos.proxy.route.error." + routeConfig.Name]: "Error",
|
||||||
|
["cosmos.proxy.route.success." + routeConfig.Name]: "Succ."
|
||||||
|
}}/>
|
||||||
|
<MiniPlotComponent metrics={[
|
||||||
|
"cosmos.proxy.route.bytes." + routeConfig.Name,
|
||||||
|
"cosmos.proxy.route.time." + routeConfig.Name,
|
||||||
|
]} labels={{
|
||||||
|
["cosmos.proxy.route.bytes." + routeConfig.Name]: "Bytes",
|
||||||
|
["cosmos.proxy.route.time." + routeConfig.Name]: "Time"
|
||||||
|
}}/>
|
||||||
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</MainCard>
|
</MainCard>
|
||||||
|
|
|
@ -40,6 +40,7 @@ import {RouteActions, RouteMode, RouteSecurity} from '../../../components/routeC
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import NewRouteCreate from '../routes/newRoute';
|
import NewRouteCreate from '../routes/newRoute';
|
||||||
import LazyLoad from 'react-lazyload';
|
import LazyLoad from 'react-lazyload';
|
||||||
|
import MiniPlotComponent from '../../dashboard/components/mini-plot';
|
||||||
|
|
||||||
const stickyButton = {
|
const stickyButton = {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
|
@ -173,6 +174,14 @@ const ProxyManagement = () => {
|
||||||
<div style={{display:'inline-block', textDecoration: 'inherit', fontSize: '90%', opacity: '90%'}}>{r.Description}</div>
|
<div style={{display:'inline-block', textDecoration: 'inherit', fontSize: '90%', opacity: '90%'}}>{r.Description}</div>
|
||||||
</>
|
</>
|
||||||
},
|
},
|
||||||
|
{ title: 'Network', screenMin: 'lg', clickable:false, field: (r) =>
|
||||||
|
<div style={{width: '450px', marginLeft: '-60px', marginBottom: '10px'}}>
|
||||||
|
<MiniPlotComponent metrics={[
|
||||||
|
"cosmos.proxy.route.bytes." + r.Name,
|
||||||
|
"cosmos.proxy.route.time." + r.Name,
|
||||||
|
]} noLabels noBackground/>
|
||||||
|
</div>
|
||||||
|
},
|
||||||
{ title: 'Origin', screenMin: 'md', clickable:true, search: (r) => r.Host + ' ' + r.PathPrefix, field: (r) => <HostChip route={r} /> },
|
{ title: 'Origin', screenMin: 'md', clickable:true, search: (r) => r.Host + ' ' + r.PathPrefix, field: (r) => <HostChip route={r} /> },
|
||||||
{ title: 'Target', screenMin: 'md', search: (r) => r.Target, field: (r) => <><RouteMode route={r} /> <Chip label={r.Target} /></> },
|
{ title: 'Target', screenMin: 'md', search: (r) => r.Target, field: (r) => <><RouteMode route={r} /> <Chip label={r.Target} /></> },
|
||||||
{ title: 'Security', screenMin: 'lg', field: (r) => <RouteSecurity route={r} />,
|
{ title: 'Security', screenMin: 'lg', field: (r) => <RouteSecurity route={r} />,
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { FormaterForMetric, formatDate, toUTC } from './utils';
|
||||||
|
|
||||||
import * as API from '../../../api';
|
import * as API from '../../../api';
|
||||||
|
|
||||||
const _MiniPlotComponent = ({metrics, labels}) => {
|
const _MiniPlotComponent = ({metrics, labels, noLabels, noBackground}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { primary, secondary } = theme.palette.text;
|
const { primary, secondary } = theme.palette.text;
|
||||||
const [dataMetrics, setDataMetrics] = useState([]);
|
const [dataMetrics, setDataMetrics] = useState([]);
|
||||||
|
@ -131,7 +131,7 @@ const _MiniPlotComponent = ({metrics, labels}) => {
|
||||||
show: false
|
show: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
yaxis: dataMetrics.map((data) => ({
|
yaxis: dataMetrics.length ? dataMetrics.map((data) => ({
|
||||||
labels: {
|
labels: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
|
@ -141,7 +141,17 @@ const _MiniPlotComponent = ({metrics, labels}) => {
|
||||||
axisTicks: {
|
axisTicks: {
|
||||||
show: false
|
show: false
|
||||||
}
|
}
|
||||||
})),
|
})) : {
|
||||||
|
labels: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisBorder: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTicks: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
|
@ -160,18 +170,18 @@ const _MiniPlotComponent = ({metrics, labels}) => {
|
||||||
const formaters = dataMetrics.map((data) => FormaterForMetric(data));
|
const formaters = dataMetrics.map((data) => FormaterForMetric(data));
|
||||||
|
|
||||||
return <Stack direction='row' spacing={3}
|
return <Stack direction='row' spacing={3}
|
||||||
alignItems='center' sx={{padding: '0px 20px', width: '100%', backgroundColor: 'rgba(0,0,0,0.1)'}}
|
alignItems='center' sx={{padding: '0px 20px', width: '100%', backgroundColor: noBackground ? '' : 'rgba(0,0,0,0.1)'}}
|
||||||
justifyContent={'space-around'}>
|
justifyContent={'space-around'}>
|
||||||
|
|
||||||
{dataMetrics && dataMetrics.map((dataMetric, di) =>
|
{!noLabels && dataMetrics && dataMetrics.map((dataMetric, di) =>
|
||||||
<Stack direction='column' justifyContent={'center'} alignItems={'center'} spacing={0} style={{
|
<Stack direction='column' justifyContent={'center'} alignItems={'center'} spacing={0} style={{
|
||||||
width: '60px',
|
width: '60px',
|
||||||
}}>
|
}}>
|
||||||
{dataMetric.Values.length && <div style={{
|
{dataMetric.Values.length ? <div style={{
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
fontSize: '110%',
|
fontSize: '110%',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
}}>{formaters[di](dataMetric.Values[dataMetric.Values.length - 1].Value)}</div>
|
}}>{formaters[di](dataMetric.Values[dataMetric.Values.length - 1].Value)}</div> : formaters[di](0)
|
||||||
}
|
}
|
||||||
<div>
|
<div>
|
||||||
<div style={{
|
<div style={{
|
||||||
|
@ -195,8 +205,8 @@ const _MiniPlotComponent = ({metrics, labels}) => {
|
||||||
</Stack>
|
</Stack>
|
||||||
}
|
}
|
||||||
|
|
||||||
const MiniPlotComponent = ({ metrics, labels }) => {
|
const MiniPlotComponent = ({ metrics, labels, noLabels, noBackground }) => {
|
||||||
const memoizedComponent = useMemo(() => <_MiniPlotComponent metrics={metrics} labels={labels} />, [metrics]);
|
const memoizedComponent = useMemo(() => <_MiniPlotComponent noBackground={noBackground} noLabels={noLabels} metrics={metrics} labels={labels} />, [metrics]);
|
||||||
return memoizedComponent;
|
return memoizedComponent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,7 @@ const PlotComponent = ({ title, slot, data, SimpleDesign, withSelector, xAxis, z
|
||||||
theme.palette.primary.main.replace('rgb(', 'rgba('),
|
theme.palette.primary.main.replace('rgb(', 'rgba('),
|
||||||
theme.palette.secondary.main.replace('rgb(', 'rgba('),
|
theme.palette.secondary.main.replace('rgb(', 'rgba('),
|
||||||
theme.palette.error.main.replace('rgb(', 'rgba('),
|
theme.palette.error.main.replace('rgb(', 'rgba('),
|
||||||
|
theme.palette.warning.main.replace('rgb(', 'rgba('),
|
||||||
],
|
],
|
||||||
xaxis: {
|
xaxis: {
|
||||||
categories:
|
categories:
|
||||||
|
@ -115,7 +116,7 @@ const PlotComponent = ({ title, slot, data, SimpleDesign, withSelector, xAxis, z
|
||||||
max: zoom.xaxis && zoom.xaxis.max,
|
max: zoom.xaxis && zoom.xaxis.max,
|
||||||
},
|
},
|
||||||
yaxis: toProcess.map((thisdata, ida) => ({
|
yaxis: toProcess.map((thisdata, ida) => ({
|
||||||
opposite: ida === 0,
|
opposite: ida % 2 === 1,
|
||||||
labels: {
|
labels: {
|
||||||
style: {
|
style: {
|
||||||
colors: [secondary],
|
colors: [secondary],
|
||||||
|
|
|
@ -56,10 +56,18 @@ function formatDate(now, time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function descendingComparator(a, b, orderBy) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
if (parseFloat(b[orderBy]) > parseFloat(a[orderBy])) {
|
if (b1 > a1) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -162,7 +170,8 @@ const TableComponent = ({ title, data, displayMax, render, xAxis, slot, zoom}) =
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} 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) => {
|
.reduce((a, b) => {
|
||||||
|
@ -304,7 +313,7 @@ const TableComponent = ({ title, data, displayMax, render, xAxis, slot, zoom}) =
|
||||||
>
|
>
|
||||||
<OrderTableHead headCells={headCells} order={order} orderBy={orderBy} setOrderBy={setOrderBy} setOrder={setOrder} />
|
<OrderTableHead headCells={headCells} order={order} orderBy={orderBy} setOrderBy={setOrderBy} setOrder={setOrder} />
|
||||||
<TableBody style={{height:'409px', overflow: 'auto'}}>
|
<TableBody style={{height:'409px', overflow: 'auto'}}>
|
||||||
{stableSort(rows, getComparator(order, "__" + orderBy)).map((row, index) => {
|
{stableSort(rows, getComparator(order, orderBy)).map((row, index) => {
|
||||||
const isItemSelected = false // isSelected(row.trackingNo);
|
const isItemSelected = false // isSelected(row.trackingNo);
|
||||||
const labelId = `enhanced-table-checkbox-${index}`;
|
const labelId = `enhanced-table-checkbox-${index}`;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export const simplifyNumber = (num, unit) => {
|
export const simplifyNumber = (num, unit) => {
|
||||||
if(!num) return 0;
|
if(typeof num == 'undefined' || num === null) return 0;
|
||||||
|
|
||||||
num = Math.round(num * 100) / 100;
|
num = Math.round(num * 100) / 100;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import {
|
import {
|
||||||
Grid,
|
Grid,
|
||||||
LinearProgress,
|
LinearProgress,
|
||||||
|
Tooltip,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
import PlotComponent from './components/plot';
|
import PlotComponent from './components/plot';
|
||||||
import TableComponent from './components/table';
|
import TableComponent from './components/table';
|
||||||
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
const ProxyDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => {
|
const ProxyDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => {
|
||||||
console.log(metrics)
|
console.log(metrics)
|
||||||
|
@ -25,7 +27,6 @@ const ProxyDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => {
|
||||||
]} />
|
]} />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Requests Per URLs" data={
|
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Requests Per URLs" data={
|
||||||
Object.keys(metrics).filter((key) => key.startsWith("cosmos.proxy.route.")).map((key) => metrics[key])
|
Object.keys(metrics).filter((key) => key.startsWith("cosmos.proxy.route.")).map((key) => metrics[key])
|
||||||
} />
|
} />
|
||||||
|
@ -37,7 +38,17 @@ const ProxyDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => {
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Reasons For Blocked Requests" data={
|
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={
|
||||||
|
<span>
|
||||||
|
Reasons For Blocked Requests <Tooltip title={<div>
|
||||||
|
<div><strong>bots</strong>: Bots</div>
|
||||||
|
<div><strong>geo</strong>: By Geolocation (blocked countries)</div>
|
||||||
|
<div><strong>referer</strong>: By Referer</div>
|
||||||
|
<div><strong>hostname</strong>: By Hostname (usually IP scanning threat)</div>
|
||||||
|
<div><strong>ip-whitelists</strong>: By IP Whitelists (Including restricted to Constellation)</div>
|
||||||
|
<div><strong>smart-shield</strong>: 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</div>
|
||||||
|
</div>}><InfoCircleOutlined /></Tooltip>
|
||||||
|
</span>} data={
|
||||||
Object.keys(metrics).filter((key) => key.startsWith("cosmos.proxy.blocked.")).map((key) => metrics[key])
|
Object.keys(metrics).filter((key) => key.startsWith("cosmos.proxy.blocked.")).map((key) => metrics[key])
|
||||||
} />
|
} />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -14,7 +14,7 @@ const ResourceDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => {
|
||||||
<PlotComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={'Resources'} data={[metrics["cosmos.system.cpu.0"], metrics["cosmos.system.ram"]]} />
|
<PlotComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={'Resources'} data={[metrics["cosmos.system.cpu.0"], metrics["cosmos.system.ram"]]} />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Containers - Resources" data={
|
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Containers - Average Resources" data={
|
||||||
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.docker.cpu") || key.startsWith("cosmos.system.docker.ram")).map((key) => metrics[key])
|
Object.keys(metrics).filter((key) => 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 }) => {
|
||||||
<PlotComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={'Network'} data={[metrics["cosmos.system.netTx"], metrics["cosmos.system.netRx"]]} />
|
<PlotComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={'Network'} data={[metrics["cosmos.system.netTx"], metrics["cosmos.system.netRx"]]} />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Containers - Network" data={
|
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Containers - Average Network" data={
|
||||||
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.docker.net")).map((key) => metrics[key])
|
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.docker.net")).map((key) => metrics[key])
|
||||||
} />
|
} />
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import IsLoggedIn from "../../isLoggedIn";
|
||||||
import { ServAppIcon } from "../../utils/servapp-icon";
|
import { ServAppIcon } from "../../utils/servapp-icon";
|
||||||
import Chart from 'react-apexcharts';
|
import Chart from 'react-apexcharts';
|
||||||
import { useClientInfos } from "../../utils/hooks";
|
import { useClientInfos } from "../../utils/hooks";
|
||||||
|
import { FormaterForMetric, formatDate } from "../dashboard/components/utils";
|
||||||
|
|
||||||
|
|
||||||
export const HomeBackground = () => {
|
export const HomeBackground = () => {
|
||||||
|
@ -90,6 +91,7 @@ const HomePage = () => {
|
||||||
const isMd = useMediaQuery(theme.breakpoints.up('md'));
|
const isMd = useMediaQuery(theme.breakpoints.up('md'));
|
||||||
const {role} = useClientInfos();
|
const {role} = useClientInfos();
|
||||||
const isAdmin = role === "2";
|
const isAdmin = role === "2";
|
||||||
|
const [metrics, setMetrics] = useState(null);
|
||||||
|
|
||||||
const blockStyle = {
|
const blockStyle = {
|
||||||
margin: 0,
|
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 = () => {
|
const refreshConfig = () => {
|
||||||
if(isAdmin) {
|
if(isAdmin) {
|
||||||
API.docker.list().then((res) => {
|
API.docker.list().then((res) => {
|
||||||
|
@ -185,11 +224,11 @@ const HomePage = () => {
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
formatter: function (val) {
|
formatter: function (val) {
|
||||||
return val + "%";
|
return val + "%"
|
||||||
},
|
},
|
||||||
color: "#111",
|
color: "#111",
|
||||||
offsetY: 9,
|
offsetY: 7,
|
||||||
fontSize: "24px",
|
fontSize: "18px",
|
||||||
show: true
|
show: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,6 +253,39 @@ const HomePage = () => {
|
||||||
labels: []
|
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 <Stack spacing={2} >
|
return <Stack spacing={2} >
|
||||||
<IsLoggedIn />
|
<IsLoggedIn />
|
||||||
|
@ -276,8 +348,9 @@ const HomePage = () => {
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Grid2 container spacing={2} style={{ zIndex: 2 }}>
|
<Grid2 container spacing={2} style={{ zIndex: 2 }}>
|
||||||
{/* {(!isMd || !coStatus || !coStatus.resources || !coStatus.resources.cpu ) && (<>
|
{coStatus && !coStatus.MonitoringDisabled && (<>
|
||||||
<Grid2 item xs={12} sm={6} md={4} lg={3} xl={3} xxl={3} key={'000'}>
|
{isMd && !metrics && (<>
|
||||||
|
<Grid2 item xs={12} sm={6} md={6} lg={3} xl={3} xxl={3} key={'000'}>
|
||||||
<Box className='app' style={{ padding: '20px', height: "107px", borderRadius: 5, ...appColor }}>
|
<Box className='app' style={{ padding: '20px', height: "107px", borderRadius: 5, ...appColor }}>
|
||||||
<Stack style={{flexGrow: 1}} spacing={0}>
|
<Stack style={{flexGrow: 1}} spacing={0}>
|
||||||
<div style={{fontSize: '18px', fontWeight: "bold"}}>CPU</div>
|
<div style={{fontSize: '18px', fontWeight: "bold"}}>CPU</div>
|
||||||
|
@ -286,25 +359,57 @@ const HomePage = () => {
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<Grid2 item xs={12} sm={6} md={4} lg={3} xl={3} xxl={3} key={'001'}>
|
<Grid2 item xs={12} sm={6} md={6} lg={3} xl={3} xxl={3} key={'001'}>
|
||||||
<Box className='app' style={{ padding: '20px', height: "107px", borderRadius: 5, ...appColor }}>
|
<Box className='app' style={{ padding: '20px', height: "107px", borderRadius: 5, ...appColor }}>
|
||||||
<Stack style={{flexGrow: 1}} spacing={0}>
|
<Stack style={{flexGrow: 1}} spacing={0}>
|
||||||
<div style={{fontSize: '18px', fontWeight: "bold"}}>RAM</div>
|
<div style={{fontSize: '18px', fontWeight: "bold"}}>RAM</div>
|
||||||
<div>Available: --GB</div>
|
<div>Available: -- GB</div>
|
||||||
<div>Used: --GB</div>
|
<div>Used: -- GB</div>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 item xs={12} sm={6} md={6} lg={3} xl={3} xxl={3} key={'001'}>
|
||||||
|
<Box className='app' style={{height: '106px',borderRadius: 5, ...appColor }}>
|
||||||
|
<Stack direction="row" justifyContent={'center'} alignItems={'center'} style={{ height: "100%" }}>
|
||||||
|
<div style={{width: '110px', height: '97px'}}>
|
||||||
|
<div style={bigNb}>
|
||||||
|
-
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Stack style={{flexGrow: 1}} spacing={0}>
|
||||||
|
<div style={{fontSize: '18px', fontWeight: "bold"}}>Network</div>
|
||||||
|
<div>Tx: -</div>
|
||||||
|
<div>Rx: -</div>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 item xs={12} sm={6} md={6} lg={3} xl={3} xxl={3} key={'001'}>
|
||||||
|
<Box className='app' style={{height: '106px',borderRadius: 5, ...appColor }}>
|
||||||
|
<Stack direction="row" justifyContent={'center'} alignItems={'center'} style={{ height: "100%" }}>
|
||||||
|
<div style={{width: '110px', height: '97px'}}>
|
||||||
|
<div style={bigNb}>
|
||||||
|
-
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Stack style={{flexGrow: 1}} spacing={0}>
|
||||||
|
<div style={{fontSize: '18px', fontWeight: "bold"}}>Requests</div>
|
||||||
|
<div>Success: -</div>
|
||||||
|
<div>Error: -</div>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</>)}
|
</>)}
|
||||||
|
|
||||||
{isMd && coStatus && coStatus.resources.cpu && (<>
|
{isMd && metrics && (<>
|
||||||
<Grid2 item xs={12} sm={6} md={4} lg={3} xl={3} xxl={3} key={'000'}>
|
<Grid2 item xs={12} sm={6} md={6} lg={3} xl={3} xxl={3} key={'000'}>
|
||||||
<Box className='app' style={{height: '106px', borderRadius: 5, ...appColor }}>
|
<Box className='app' style={{height: '106px', borderRadius: 5, ...appColor }}>
|
||||||
<Stack direction="row" justifyContent={'center'} alignItems={'center'} style={{ height: "100%" }}>
|
<Stack direction="row" justifyContent={'center'} alignItems={'center'} style={{ height: "100%" }}>
|
||||||
<div style={{width: '110px', height: '97px'}}>
|
<div style={{width: '110px', height: '97px'}}>
|
||||||
<Chart
|
<Chart
|
||||||
options={optionsRadial}
|
options={optionsRadial}
|
||||||
series={[parseInt(coStatus.resources.cpu)]}
|
series={[latestCPU]}
|
||||||
type="radialBar"
|
type="radialBar"
|
||||||
height={120}
|
height={120}
|
||||||
width={120}
|
width={120}
|
||||||
|
@ -318,15 +423,16 @@ const HomePage = () => {
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<Grid2 item xs={12} sm={6} md={4} lg={3} xl={3} xxl={3} key={'001'}>
|
<Grid2 item xs={12} sm={6} md={6} lg={3} xl={3} xxl={3} key={'001'}>
|
||||||
<Box className='app' style={{height: '106px',borderRadius: 5, ...appColor }}>
|
<Box className='app' style={{height: '106px',borderRadius: 5, ...appColor }}>
|
||||||
<Stack direction="row" justifyContent={'center'} alignItems={'center'} style={{ height: "100%" }}>
|
<Stack direction="row" justifyContent={'center'} alignItems={'center'} style={{ height: "100%" }}>
|
||||||
<div style={{width: '110px', height: '97px'}}>
|
<div style={{width: '110px', height: '97px'}}>
|
||||||
<Chart
|
<Chart
|
||||||
options={optionsRadial}
|
options={optionsRadial}
|
||||||
series={[parseInt(
|
// series={[parseInt(
|
||||||
coStatus.resources.ram / (coStatus.resources.ram + coStatus.resources.ramFree) * 100
|
// coStatus.resources.ram / (coStatus.resources.ram + coStatus.resources.ramFree) * 100
|
||||||
)]}
|
// )]}
|
||||||
|
series={[parseInt(latestRAMRaw / maxRAMRaw * 100)]}
|
||||||
type="radialBar"
|
type="radialBar"
|
||||||
height={120}
|
height={120}
|
||||||
width={120}
|
width={120}
|
||||||
|
@ -334,14 +440,47 @@ const HomePage = () => {
|
||||||
</div>
|
</div>
|
||||||
<Stack style={{flexGrow: 1}} spacing={0}>
|
<Stack style={{flexGrow: 1}} spacing={0}>
|
||||||
<div style={{fontSize: '18px', fontWeight: "bold"}}>RAM</div>
|
<div style={{fontSize: '18px', fontWeight: "bold"}}>RAM</div>
|
||||||
<div>Available: {Math.ceil((coStatus.resources.ram + coStatus.resources.ramFree) / 1024 / 1024 / 1024)}GB</div>
|
<div>Available: {maxRAM}</div>
|
||||||
<div>Used: {Math.ceil(coStatus.resources.ram / 1024 / 1024 / 1024)}GB</div>
|
<div>Used: {latestRAM}</div>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 item xs={12} sm={6} md={6} lg={3} xl={3} xxl={3} key={'001'}>
|
||||||
|
<Box className='app' style={{height: '106px',borderRadius: 5, ...appColor }}>
|
||||||
|
<Stack direction="row" justifyContent={'center'} alignItems={'center'} style={{ height: "100%" }}>
|
||||||
|
<div style={{width: '110px', height: '97px'}}>
|
||||||
|
<div style={bigNb}>
|
||||||
|
{latestNetworkSum}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Stack style={{flexGrow: 1}} spacing={0}>
|
||||||
|
<div style={{fontSize: '18px', fontWeight: "bold"}}>Network</div>
|
||||||
|
<div>Tx: {latestNetworkTx}</div>
|
||||||
|
<div>Rx: {latestNetworkRx}</div>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 item xs={12} sm={6} md={6} lg={3} xl={3} xxl={3} key={'001'}>
|
||||||
|
<Box className='app' style={{height: '106px',borderRadius: 5, ...appColor }}>
|
||||||
|
<Stack direction="row" justifyContent={'center'} alignItems={'center'} style={{ height: "100%" }}>
|
||||||
|
<div style={{width: '110px', height: '97px'}}>
|
||||||
|
<div style={bigNb}>
|
||||||
|
{latestRequestSum}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Stack style={{flexGrow: 1}} spacing={0}>
|
||||||
|
<div style={{fontSize: '18px', fontWeight: "bold"}}>Requests</div>
|
||||||
|
<div>Success: {latestRequests}</div>
|
||||||
|
<div>Error: {latestRequestsError}</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
|
|
||||||
</>)} */}
|
</>)}
|
||||||
|
</>)}
|
||||||
|
|
||||||
{config && servApps && routes.map((route) => {
|
{config && servApps && routes.map((route) => {
|
||||||
let skip = route.Mode == "REDIRECT";
|
let skip = route.Mode == "REDIRECT";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cosmos-server",
|
"name": "cosmos-server",
|
||||||
"version": "0.12.0-unstable32",
|
"version": "0.12.0-unstable33",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "test-server.js",
|
"main": "test-server.js",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|
|
@ -275,7 +275,8 @@ func InitServer() *mux.Router {
|
||||||
utils.Log("Initialising HTTP(S) Router and all routes")
|
utils.Log("Initialising HTTP(S) Router and all routes")
|
||||||
|
|
||||||
router := mux.NewRouter().StrictSlash(true)
|
router := mux.NewRouter().StrictSlash(true)
|
||||||
router.HandleFunc("/logo", SendLogo)
|
|
||||||
|
router.Use(utils.BlockBannedIPs)
|
||||||
|
|
||||||
router.Use(middleware.Logger)
|
router.Use(middleware.Logger)
|
||||||
|
|
||||||
|
@ -283,6 +284,8 @@ func InitServer() *mux.Router {
|
||||||
router.Use(utils.BlockByCountryMiddleware(config.BlockedCountries, config.CountryBlacklistIsWhitelist))
|
router.Use(utils.BlockByCountryMiddleware(config.BlockedCountries, config.CountryBlacklistIsWhitelist))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
router.HandleFunc("/logo", SendLogo)
|
||||||
|
|
||||||
srapi := router.PathPrefix("/cosmos").Subrouter()
|
srapi := router.PathPrefix("/cosmos").Subrouter()
|
||||||
|
|
||||||
srapi.HandleFunc("/api/dns", GetDNSRoute)
|
srapi.HandleFunc("/api/dns", GetDNSRoute)
|
||||||
|
|
|
@ -87,6 +87,14 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt
|
||||||
|
|
||||||
destination = utils.Restrictions(route.RestrictToConstellation, route.WhitelistInboundIPs)(destination)
|
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)
|
destination = SmartShieldMiddleware(route.Name, route)(destination)
|
||||||
|
|
||||||
originCORS := route.CORSOrigin
|
originCORS := route.CORSOrigin
|
||||||
|
@ -133,14 +141,6 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt
|
||||||
destination = utils.BandwithLimiterMiddleware(route.MaxBandwith)(destination)
|
destination = utils.BandwithLimiterMiddleware(route.MaxBandwith)(destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
if route.BlockCommonBots {
|
|
||||||
destination = BotDetectionMiddleware(destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
if route.BlockAPIAbuse {
|
|
||||||
destination = utils.BlockPostWithoutReferer(destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !route.DisableHeaderHardening {
|
if !route.DisableHeaderHardening {
|
||||||
destination = utils.SetSecurityHeaders(destination)
|
destination = utils.SetSecurityHeaders(destination)
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,6 +165,7 @@ func (shield *smartShieldState) isAllowedToReqest(shieldID string, policy utils.
|
||||||
banType: PERM,
|
banType: PERM,
|
||||||
time: time.Now(),
|
time: time.Now(),
|
||||||
})
|
})
|
||||||
|
|
||||||
utils.Warn("User " + ClientID + " has been banned permanently: "+ fmt.Sprintf("%+v", userConsumed))
|
utils.Warn("User " + ClientID + " has been banned permanently: "+ fmt.Sprintf("%+v", userConsumed))
|
||||||
return false
|
return false
|
||||||
} else if nbStrikes >= 3 {
|
} 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) {
|
if !isPrivileged(r, policy) && !shield.isAllowedToReqest(shieldID, policy, userConsumed) {
|
||||||
lastBan := shield.GetLastBan(policy, userConsumed)
|
lastBan := shield.GetLastBan(policy, userConsumed)
|
||||||
go metrics.PushShieldMetrics("smart-shield")
|
go metrics.PushShieldMetrics("smart-shield")
|
||||||
|
utils.IncrementIPAbuseCounter(clientID)
|
||||||
utils.Log("SmartShield: User is blocked due to abuse: " + fmt.Sprintf("%+v", lastBan))
|
utils.Log("SmartShield: User is blocked due to abuse: " + fmt.Sprintf("%+v", lastBan))
|
||||||
http.Error(w, "Too many requests", http.StatusTooManyRequests)
|
http.Error(w, "Too many requests", http.StatusTooManyRequests)
|
||||||
return
|
return
|
||||||
|
|
|
@ -63,6 +63,7 @@ func StatusRoute(w http.ResponseWriter, req *http.Request) {
|
||||||
"CPU": runtime.GOARCH,
|
"CPU": runtime.GOARCH,
|
||||||
"AVX": cpu.X86.HasAVX,
|
"AVX": cpu.X86.HasAVX,
|
||||||
"LetsEncryptErrors": utils.LetsEncryptErrors,
|
"LetsEncryptErrors": utils.LetsEncryptErrors,
|
||||||
|
"MonitoringDisabled": utils.GetMainConfig().MonitoringDisabled,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/mxk/go-flowrate/flowrate"
|
"github.com/mxk/go-flowrate/flowrate"
|
||||||
"github.com/oschwald/geoip2-golang"
|
"github.com/oschwald/geoip2-golang"
|
||||||
|
@ -16,6 +18,66 @@ import (
|
||||||
|
|
||||||
var PushShieldMetrics func(string)
|
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 {
|
func MiddlewareTimeout(timeout time.Duration) func(next http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -151,6 +213,7 @@ func BlockByCountryMiddleware(blockedCountries []string, CountryBlacklistIsWhite
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
PushShieldMetrics("geo")
|
PushShieldMetrics("geo")
|
||||||
|
IncrementIPAbuseCounter(ip)
|
||||||
http.Error(w, "Access denied", http.StatusForbidden)
|
http.Error(w, "Access denied", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -183,6 +246,12 @@ func BlockPostWithoutReferer(next http.Handler) http.Handler {
|
||||||
PushShieldMetrics("referer")
|
PushShieldMetrics("referer")
|
||||||
Error("Blocked POST request without Referer header", nil)
|
Error("Blocked POST request without Referer header", nil)
|
||||||
http.Error(w, "Bad Request: Invalid request.", http.StatusBadRequest)
|
http.Error(w, "Bad Request: Invalid request.", http.StatusBadRequest)
|
||||||
|
|
||||||
|
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
if ip != "" {
|
||||||
|
IncrementIPAbuseCounter(ip)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
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)
|
Error("Invalid Hostname " + r.Host + " for request. Expecting one of " + fmt.Sprintf("%v", hostnames), nil)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
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)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,12 +387,14 @@ func Restrictions(RestrictToConstellation bool, WhitelistInboundIPs []string) fu
|
||||||
if(!isInConstellation) {
|
if(!isInConstellation) {
|
||||||
if(!isUsingWhiteList) {
|
if(!isUsingWhiteList) {
|
||||||
PushShieldMetrics("ip-whitelists")
|
PushShieldMetrics("ip-whitelists")
|
||||||
|
IncrementIPAbuseCounter(ip)
|
||||||
Error("Request from " + ip + " is blocked because of restrictions", nil)
|
Error("Request from " + ip + " is blocked because of restrictions", nil)
|
||||||
Debug("Blocked by RestrictToConstellation isInConstellation isUsingWhiteList")
|
Debug("Blocked by RestrictToConstellation isInConstellation isUsingWhiteList")
|
||||||
http.Error(w, "Access denied", http.StatusForbidden)
|
http.Error(w, "Access denied", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
} else if (!isInWhitelist) {
|
} else if (!isInWhitelist) {
|
||||||
PushShieldMetrics("ip-whitelists")
|
PushShieldMetrics("ip-whitelists")
|
||||||
|
IncrementIPAbuseCounter(ip)
|
||||||
Error("Request from " + ip + " is blocked because of restrictions", nil)
|
Error("Request from " + ip + " is blocked because of restrictions", nil)
|
||||||
Debug("Blocked by RestrictToConstellation isInConstellation isInWhitelist")
|
Debug("Blocked by RestrictToConstellation isInConstellation isInWhitelist")
|
||||||
http.Error(w, "Access denied", http.StatusForbidden)
|
http.Error(w, "Access denied", http.StatusForbidden)
|
||||||
|
@ -326,6 +403,7 @@ func Restrictions(RestrictToConstellation bool, WhitelistInboundIPs []string) fu
|
||||||
}
|
}
|
||||||
} else if(isUsingWhiteList && !isInWhitelist) {
|
} else if(isUsingWhiteList && !isInWhitelist) {
|
||||||
PushShieldMetrics("ip-whitelists")
|
PushShieldMetrics("ip-whitelists")
|
||||||
|
IncrementIPAbuseCounter(ip)
|
||||||
Error("Request from " + ip + " is blocked because of restrictions", nil)
|
Error("Request from " + ip + " is blocked because of restrictions", nil)
|
||||||
Debug("Blocked by RestrictToConstellation isInConstellation isUsingWhiteList isInWhitelist")
|
Debug("Blocked by RestrictToConstellation isInConstellation isUsingWhiteList isInWhitelist")
|
||||||
http.Error(w, "Access denied", http.StatusForbidden)
|
http.Error(w, "Access denied", http.StatusForbidden)
|
||||||
|
|
Loading…
Reference in a new issue