From abe7fc904bf33c4160686bb64b21a99bc20ffa39 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sun, 15 Oct 2023 18:03:10 +1100 Subject: [PATCH] cc and ccn geo blocking --- .env.example | 2 + components/MenuLinks.js | 215 ++++++++++++++++++++++------------------ controllers/maps.js | 34 ++++++- pages/map/[name].js | 86 +++++++++++++++- router.js | 2 + util.js | 12 +++ 6 files changed, 250 insertions(+), 101 deletions(-) diff --git a/.env.example b/.env.example index c30d6b3..00bcd8d 100644 --- a/.env.example +++ b/.env.example @@ -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" diff --git a/components/MenuLinks.js b/components/MenuLinks.js index 75ab3fc..654e433 100644 --- a/components/MenuLinks.js +++ b/components/MenuLinks.js @@ -20,107 +20,126 @@ export default withRouter(function MenuLinks({ router }) { switch(true) { case router.pathname.startsWith('/kb'): mainLinks = (<> -
  • - - - Index - -
  • -
  • - - - Firewall - -
  • -
  • - - - HTTPS & CSRs - -
  • +
      +
    • + + + Index + +
    • +
    • + + + Firewall + +
    • +
    • + + + HTTPS & CSRs + +
    • +
    ); bottomLinks = null; break; default: mainLinks = (<> -
  • - - - Account - -
  • -
  • - - - Domains - -
  • -
  • - - - Backends - -
  • -
  • - - - HTTPS Certificates - -
  • -
  • - - - Origin CSR - -
  • -
  • - - - Protection Settings - -
  • -
  • - - - Protection Rules - -
  • -
  • - - - Rewrites - -
  • -
  • - - - Redirects - -
  • -
  • - - - IP Whitelist - -
  • -
  • - - - IP Blacklist - -
  • -
  • - - - ASN Blacklist - -
  • -
  • - - - Maintenance Mode - -
  • +
      +
    • + + + Account + +
    • +
    • + + + Domains + +
    • +
    • + + + Backends + +
    • +
    • + + + HTTPS Certificates + +
    • +
    • + + + Origin CSR + +
    • +
    • + + + Protection Settings + +
    • +
    • + + + Protection Rules + +
    • +
    • + + + Rewrites + +
    • +
    • + + + Redirects + +
    • +
    • + + + Maintenance Mode + +
    • +
    +
    +
      +
    • + + + IP Whitelist + +
    • +
    • + + + IP Blacklist + +
    • +
    • + + + ASN Blacklist + +
    • +
    • + + + Country Blacklist + +
    • +
    • + + + Continent Blacklist + +
    • +
    ); bottomLinks = (<>
    @@ -159,9 +178,7 @@ export default withRouter(function MenuLinks({ router }) { BasedFlare
    -
      - {mainLinks} -
    + {mainLinks} {bottomLinks} ); diff --git a/controllers/maps.js b/controllers/maps.js index b76117e..352b991 100644 --- a/controllers/maps.js +++ b/controllers/maps.js @@ -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', { diff --git a/pages/map/[name].js b/pages/map/[name].js index 1ed0d54..ac867ab 100644 --- a/pages/map/[name].js +++ b/pages/map/[name].js @@ -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 =
    + Blocked countries based on geoip data. Uses ISO 3166-1 alpha-2 country codes. +
    ; + formElements = ( + <> + + + + + ({ + ...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' + /> + + + ); + break; case 'redirect': mapInfoHelper =
    Redirects redirect all requests from a domain to another domain or a domain+path e.g. "www.example.com" -> "example.com" or "example.com/something". @@ -314,7 +398,7 @@ const MapPage = (props) => { {/* Map table */} -
    +
    diff --git a/router.js b/router.js index 55af197..2793992 100644 --- a/router.js +++ b/router.js @@ -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, diff --git a/util.js b/util.js index 2f38efa..98bb6a6 100644 --- a/util.js +++ b/util.js @@ -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',