Skip to content
This repository has been archived by the owner on Jun 27, 2024. It is now read-only.

Commit

Permalink
feat(ext): firewall extension (#184)
Browse files Browse the repository at this point in the history
* feat: add firewall extention

closes #152

* test: add tetsing for firewall extention

* refactor: removed customLists, use customRanges instead

chore: `deno task check`

* test: removed debug option and added proxy to cloudflare in firewall tests

* feat: check for updates on request

* chore: removed unused file

fix: double checking

---------

Co-authored-by: Samuel Kopp <62482066+boywithkeyboard@users.noreply.github.com>
  • Loading branch information
ivy and boywithkeyboard authored Aug 21, 2023
1 parent 1226a29 commit 97354d0
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 0 deletions.
53 changes: 53 additions & 0 deletions ext/firewall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { createExtension } from '../mod.ts'

let VPN_LIST: string[] = []
let DATACENTER_LIST: string[] = []
let LAST_UPDATE = 0

type FirewallOptions = {
blockVPN?: boolean
blockDatacenter?: boolean
customRanges?: string[]
}

export const firewall = createExtension<FirewallOptions>({
async onRequest({ _: opts, app }) {
await checkUpdate()
if (
opts?.blockVPN && VPN_LIST.find((range) => isIpInRange(app.ip, range))
) {
return new Response(null, { status: 403 })
}
if (
opts?.blockDatacenter &&
DATACENTER_LIST.find((range) => isIpInRange(app.ip, range))
) return new Response(null, { status: 403 })
if (
opts?.customRanges &&
opts.customRanges.find((range) => isIpInRange(app.ip, range))
) return new Response(null, { status: 403 })
},
})

async function checkUpdate() {
if (Date.now() - LAST_UPDATE < 9e5 /* 15 minutes */) return
VPN_LIST = (await (await fetch(
'https://raw.githubusercontent.com/X4BNet/lists_vpn/main/output/vpn/ipv4.txt',
)).text()).split('\n')
DATACENTER_LIST = (await (await fetch(
'https://raw.githubusercontent.com/X4BNet/lists_vpn/main/output/datacenter/ipv4.txt',
)).text()).split('\n')
LAST_UPDATE = Date.now()
}

function isIpInRange(ip: string, range: string) {
const [rangeIp, rangeMask] = range.split('/')
const rangeStart = ipToNumber(rangeIp) >>> 0
const rangeEnd = rangeStart + ((1 << (32 - parseInt(rangeMask))) - 1)
const numIp = ipToNumber(ip)
return numIp >= rangeStart && numIp <= rangeEnd
}

function ipToNumber(ip: string) {
return ip.split('.').reduce((acc, val) => (acc << 8) | parseInt(val), 0) >>> 0
}
61 changes: 61 additions & 0 deletions test/ext/firewall.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { assertEquals } from '../deps.ts'
import cheetah from '../../mod.ts'
import { firewall } from '../../ext/firewall.ts'

Deno.test('ext/firewall', async (t) => {
await t.step('vpn', async () => {
const app = new cheetah({ proxy: 'cloudflare' })

app.use(firewall({
blockVPN: true,
}))

const res = await app.fetch(
new Request('http://localhost', {
headers: {
'cf-connecting-ip': '2.56.16.0',
},
}),
)

assertEquals(res.status, 403)
})
})

Deno.test('datacenters', async () => {
const app = new cheetah({ proxy: 'cloudflare' })

app.use(firewall({
blockDatacenter: true,
}))

const res = await app.fetch(
new Request('http://localhost', {
headers: {
'cf-connecting-ip': '1.12.32.0',
},
}),
)

assertEquals(res.status, 403)
})

Deno.test('customRanges', async () => {
const app = new cheetah({ proxy: 'cloudflare' })

app.use(firewall({
customRanges: [
'1.2.3.4/32',
],
}))

const res = await app.fetch(
new Request('http://localhost', {
headers: {
'cf-connecting-ip': '1.2.3.4',
},
}),
)

assertEquals(res.status, 403)
})

0 comments on commit 97354d0

Please sign in to comment.