cc and ccn geo blocking

dev
Thomas Lynch 10 months ago
parent c41a3ecf58
commit abe7fc904b
  1. 2
      .env.example
  2. 215
      components/MenuLinks.js
  3. 34
      controllers/maps.js
  4. 86
      pages/map/[name].js
  5. 2
      router.js
  6. 12
      util.js

@ -10,6 +10,8 @@ SERVER_PREFIX="websrv"
HOSTS_MAP_NAME="hosts"
BLOCKED_IP_MAP_NAME="blockedip"
BLOCKED_ASN_MAP_NAME="blockedasn"
BLOCKED_CC_MAP_NAME="blockedcc"
BLOCKED_CN_MAP_NAME="blockedcn"
DDOS_MAP_NAME="ddos"
DDOS_CONFIG_MAP_NAME="ddos_config"
BACKENDS_MAP_NAME="backends"

@ -20,107 +20,126 @@ export default withRouter(function MenuLinks({ router }) {
switch(true) {
case router.pathname.startsWith('/kb'):
mainLinks = (<>
<li className='nav-item'>
<Link href='/kb' className={path === '/kb' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-layers pe-none me-2' width='16' height='16' />
Index
</Link>
</li>
<li className='nav-item'>
<Link href='/kb/firewall' className={path === '/kb/firewall' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-bricks pe-none me-2' width='16' height='16' />
Firewall
</Link>
</li>
<li className='nav-item'>
<Link href='/kb/https' className={path === '/kb/https' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-file-earmark-lock pe-none me-2' width='16' height='16' />
HTTPS & CSRs
</Link>
</li>
<ul className='nav nav-pills flex-column mb-auto'>
<li className='nav-item'>
<Link href='/kb' className={path === '/kb' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-layers pe-none me-2' width='16' height='16' />
Index
</Link>
</li>
<li className='nav-item'>
<Link href='/kb/firewall' className={path === '/kb/firewall' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-bricks pe-none me-2' width='16' height='16' />
Firewall
</Link>
</li>
<li className='nav-item'>
<Link href='/kb/https' className={path === '/kb/https' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-file-earmark-lock pe-none me-2' width='16' height='16' />
HTTPS & CSRs
</Link>
</li>
</ul>
</>);
bottomLinks = null;
break;
default:
mainLinks = (<>
<li className='nav-item'>
<Link href='/account' className={path === '/account' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-person-square pe-none me-2' width='16' height='16' />
Account
</Link>
</li>
<li className='nav-item'>
<Link href='/domains' className={path === '/domains' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-layers pe-none me-2' width='16' height='16' />
Domains
</Link>
</li>
<li className='nav-item'>
<Link href='/map/hosts' className={path === '/map/hosts' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-hdd-network pe-none me-2' width='16' height='16' />
Backends
</Link>
</li>
<li className='nav-item'>
<Link href='/certs' className={path === '/certs' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-file-earmark-lock pe-none me-2' width='16' height='16' />
HTTPS Certificates
</Link>
</li>
<li className='nav-item'>
<Link href='/csr' className={path === '/csr' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-building-lock pe-none me-2' width='16' height='16' />
Origin CSR
</Link>
</li>
<li className='nav-item'>
<Link href='/map/ddos_config' className={path === '/map/ddos_config' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-sliders2 pe-none me-2' width='16' height='16' />
Protection Settings
</Link>
</li>
<li className='nav-item'>
<Link href='/map/ddos' className={path === '/map/ddos' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-shield-check pe-none me-2' width='16' height='16' />
Protection Rules
</Link>
</li>
<li className='nav-item'>
<Link href='/map/rewrite' className={path === '/map/rewrite' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-pencil pe-none me-2' width='16' height='16' />
Rewrites
</Link>
</li>
<li className='nav-item'>
<Link href='/map/redirect' className={path === '/map/redirect' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-signpost pe-none me-2' width='16' height='16' />
Redirects
</Link>
</li>
<li className='nav-item'>
<Link href='/map/whitelist' className={path === '/map/whitelist' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-person-check pe-none me-2' width='16' height='16' />
IP Whitelist
</Link>
</li>
<li className='nav-item'>
<Link href='/map/blockedip' className={path === '/map/blockedip' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-person-slash pe-none me-2' width='16' height='16' />
IP Blacklist
</Link>
</li>
<li className='nav-item'>
<Link href='/map/blockedasn' className={path === '/map/blockedasn' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-building-slash pe-none me-2' width='16' height='16' />
ASN Blacklist
</Link>
</li>
<li className='nav-item'>
<Link href='/map/maintenance' className={path === '/map/maintenance' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-info-square pe-none me-2' width='16' height='16' />
Maintenance Mode
</Link>
</li>
<ul className='nav nav-pills flex-column mb-auto'>
<li className='nav-item'>
<Link href='/account' className={path === '/account' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-person-square pe-none me-2' width='16' height='16' />
Account
</Link>
</li>
<li className='nav-item'>
<Link href='/domains' className={path === '/domains' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-layers pe-none me-2' width='16' height='16' />
Domains
</Link>
</li>
<li className='nav-item'>
<Link href='/map/hosts' className={path === '/map/hosts' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-hdd-network pe-none me-2' width='16' height='16' />
Backends
</Link>
</li>
<li className='nav-item'>
<Link href='/certs' className={path === '/certs' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-file-earmark-lock pe-none me-2' width='16' height='16' />
HTTPS Certificates
</Link>
</li>
<li className='nav-item'>
<Link href='/csr' className={path === '/csr' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-building-lock pe-none me-2' width='16' height='16' />
Origin CSR
</Link>
</li>
<li className='nav-item'>
<Link href='/map/ddos_config' className={path === '/map/ddos_config' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-sliders2 pe-none me-2' width='16' height='16' />
Protection Settings
</Link>
</li>
<li className='nav-item'>
<Link href='/map/ddos' className={path === '/map/ddos' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-shield-check pe-none me-2' width='16' height='16' />
Protection Rules
</Link>
</li>
<li className='nav-item'>
<Link href='/map/rewrite' className={path === '/map/rewrite' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-pencil pe-none me-2' width='16' height='16' />
Rewrites
</Link>
</li>
<li className='nav-item'>
<Link href='/map/redirect' className={path === '/map/redirect' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-signpost pe-none me-2' width='16' height='16' />
Redirects
</Link>
</li>
<li className='nav-item'>
<Link href='/map/maintenance' className={path === '/map/maintenance' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-info-square pe-none me-2' width='16' height='16' />
Maintenance Mode
</Link>
</li>
</ul>
<hr />
<ul className='nav nav-pills flex-column'>
<li className='nav-item'>
<Link href='/map/whitelist' className={path === '/map/whitelist' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-person-check pe-none me-2' width='16' height='16' />
IP Whitelist
</Link>
</li>
<li className='nav-item'>
<Link href='/map/blockedip' className={path === '/map/blockedip' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-person-slash pe-none me-2' width='16' height='16' />
IP Blacklist
</Link>
</li>
<li className='nav-item'>
<Link href='/map/blockedasn' className={path === '/map/blockedasn' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-building-slash pe-none me-2' width='16' height='16' />
ASN Blacklist
</Link>
</li>
<li className='nav-item'>
<Link href='/map/blockedcc' className={path === '/map/blockedcc' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-globe2 pe-none me-2' width='16' height='16' />
Country Blacklist
</Link>
</li>
<li className='nav-item'>
<Link href='/map/blockedcn' className={path === '/map/blockedcn' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'>
<i className='bi-globe2 pe-none me-2' width='16' height='16' />
Continent Blacklist
</Link>
</li>
</ul>
</>);
bottomLinks = (<>
<hr />
@ -159,9 +178,7 @@ export default withRouter(function MenuLinks({ router }) {
<span className='mx-2 fs-4 text-decoration-none'>BasedFlare</span>
</Link>
<hr />
<ul className='nav nav-pills flex-column mb-auto'>
{mainLinks}
</ul>
{mainLinks}
{bottomLinks}
</>);

@ -1,7 +1,17 @@
import { extractMap, dynamicResponse } from '../util.js';
import { createCIDR, parse } from 'ip6addr';
import url from 'url';
import countries from 'i18n-iso-countries';
const countryMap = countries.getAlpha2Codes();
const continentMap = {
'NA': 'North America',
'SA': 'South America',
'EU': 'Europe',
'AS': 'Asia',
'OC': 'Oceania',
'AF': 'Africa',
'AN': 'Antarctica',
};
/**
* GET /maps/:name
* Show map filtering to users domains
@ -54,6 +64,8 @@ export async function mapData(req, res, next) {
break;
case process.env.BLOCKED_IP_MAP_NAME:
case process.env.BLOCKED_ASN_MAP_NAME:
case process.env.BLOCKED_CC_MAP_NAME:
case process.env.BLOCKED_CN_MAP_NAME:
case process.env.WHITELIST_MAP_NAME:
map = map
.filter(a => {
@ -100,6 +112,8 @@ export async function deleteMapForm(req, res, next) {
if (req.params.name === process.env.BLOCKED_IP_MAP_NAME
|| req.params.name === process.env.BLOCKED_ASN_MAP_NAME
|| req.params.name === process.env.BLOCKED_CC_MAP_NAME
|| req.params.name === process.env.BLOCKED_CN_MAP_NAME
|| req.params.name === process.env.WHITELIST_MAP_NAME) {
let value;
const existingEntry = await res.locals
@ -233,6 +247,22 @@ export async function patchMapForm(req, res, next) {
//req.body.key is a number
}
//validate key is country code
if (req.params.name === process.env.BLOCKED_CC_MAP_NAME) {
if (!countryMap[req.body.key]) {
return dynamicResponse(req, res, 403, { error: 'Invalid country code' });
}
//req.body.key is a cc
}
//validate key is country code
if (req.params.name === process.env.BLOCKED_CN_MAP_NAME) {
if (!continentMap[req.body.key]) {
return dynamicResponse(req, res, 403, { error: 'Invalid continent code' });
}
//req.body.key is a cn
}
//validate value is url (roughly)
if (req.params.name === process.env.REWRITE_MAP_NAME
|| req.params.name === process.env.REDIRECT_MAP_NAME) {
@ -294,6 +324,8 @@ export async function patchMapForm(req, res, next) {
break;
case process.env.BLOCKED_IP_MAP_NAME:
case process.env.BLOCKED_ASN_MAP_NAME:
case process.env.BLOCKED_CC_MAP_NAME:
case process.env.BLOCKED_CN_MAP_NAME:
case process.env.WHITELIST_MAP_NAME: {
const existingEntry = await res.locals
.dataPlaneRetry('getRuntimeMapEntry', {

@ -1,12 +1,29 @@
import { useRouter } from 'next/router';
import React, { useState, useEffect } from 'react';
import Head from 'next/head';
import Select from 'react-select';
import MapRow from '../../components/MapRow.js';
import BackButton from '../../components/BackButton.js';
import ErrorAlert from '../../components/ErrorAlert.js';
import SearchFilter from '../../components/SearchFilter.js';
import * as API from '../../api.js';
import countries from 'i18n-iso-countries';
import enCountries from 'i18n-iso-countries/langs/en.json';
countries.registerLocale(enCountries);
const continentMap = {
'NA': 'North America',
'SA': 'South America',
'EU': 'Europe',
'AS': 'Asia',
'OC': 'Oceania',
'AF': 'Africa',
'AN': 'Antarctica',
};
const countryOptions = Object.entries(countries.getNames('en'))
.map(e => ({ value: e[0], label: `${e[1]} (${e[0]})` }));
const MapPage = (props) => {
const router = useRouter();
@ -253,6 +270,73 @@ const MapPage = (props) => {
</>
);
break;
case 'blockedcc':
mapInfoHelper = <div className='alert alert-info' role='info'>
Blocked countries based on geoip data. Uses <a target='_blank' rel='noreferrer' href='https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes'>ISO 3166-1 alpha-2 country codes</a>.
</div>;
formElements = (
<>
<td>
<button className='btn btn-sm btn-success' type='submit'>
<i className='bi-plus-lg pe-none' width='16' height='16' />
</button>
</td>
<td>
<Select
theme={(theme) => ({
...theme,
borderRadius: 5,
})}
required
closeMenuOnSelect={true}
options={countryOptions}
// value={(rec.geov||[]).map(x => ({ value: x, label: `${countries.getName(x, 'en')} (${x})` }))}
getOptionLabel={x => `${countries.getName(x.value, 'en')} (${x.value})`}
classNamePrefix='select'
name='key'
className='basic-multi-select'
/>
</td>
</>
);
break;
case 'blockedcn':
mapInfoHelper = <div className='alert alert-info' role='info'>
Block continents based on geoip data.
</div>;
formElements = (
<>
<td>
<button className='btn btn-sm btn-success' type='submit'>
<i className='bi-plus-lg pe-none' width='16' height='16' />
</button>
</td>
<td>
<Select
theme={(theme) => ({
...theme,
borderRadius: 5,
})}
required
closeMenuOnSelect={true}
options={[
{ value: 'NA', label: 'North America' },
{ value: 'SA', label: 'South America' },
{ value: 'EU', label: 'Europe' },
{ value: 'AS', label: 'Asia' },
{ value: 'OC', label: 'Oceania' },
{ value: 'AF', label: 'Africa' },
{ value: 'AN', label: 'Antarctica' },
]}
getOptionLabel={x => `${continentMap[x.value]} (${x.value})`}
classNamePrefix='select'
name='key'
className='basic-multi-select'
/>
</td>
</>
);
break;
case 'redirect':
mapInfoHelper = <div className='alert alert-info' role='info'>
Redirects redirect all requests from a domain to another domain or a domain+path e.g. &quot;www.example.com&quot; -&gt; &quot;example.com&quot; or &quot;example.com/something&quot;.
@ -314,7 +398,7 @@ const MapPage = (props) => {
<SearchFilter filter={filter} setFilter={setFilter} />
{/* Map table */}
<div className='table-responsive w-100 round-shadow'>
<div className='w-100 round-shadow'>
<form onSubmit={addToMap} className='d-flex' action={`/forms/map/${mapInfo.name}/add`} method='post'>
<table className='table text-nowrap mb-0'>
<tbody>

@ -242,6 +242,8 @@ export default function router(server, app) {
const mapNames = [
process.env.BLOCKED_IP_MAP_NAME,
process.env.BLOCKED_ASN_MAP_NAME,
process.env.BLOCKED_CC_MAP_NAME,
process.env.BLOCKED_CN_MAP_NAME,
process.env.MAINTENANCE_MAP_NAME,
process.env.WHITELIST_MAP_NAME,
process.env.REDIRECT_MAP_NAME,

@ -42,6 +42,18 @@ const fMap = {
columnNames: ['AS Number', ''],
},
[process.env.BLOCKED_CC_MAP_NAME]: {
fname: 'Country Blacklist',
description: 'Countries that are outright blocked',
columnNames: ['Country Code', ''],
},
[process.env.BLOCKED_CN_MAP_NAME]: {
fname: 'Continent Blacklist',
description: 'Continents that are outright blocked',
columnNames: ['Continent Code', ''],
},
[process.env.WHITELIST_MAP_NAME]: {
fname: 'IP Whitelist',
description: 'IPs/subnets that bypass protection rules',

Loading…
Cancel
Save