mirror of
https://github.com/geoffsee/open-gsio.git
synced 2025-09-08 22:56:46 +00:00
init
This commit is contained in:
1
workers/session-proxy/block-list-ipv4.txt
Normal file
1
workers/session-proxy/block-list-ipv4.txt
Normal file
@@ -0,0 +1 @@
|
||||
See .github/update-vpn-blocklist.yaml
|
66
workers/session-proxy/index.ts
Normal file
66
workers/session-proxy/index.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import IPParser from "./ip-parser";
|
||||
import blockList from "./block-list-ipv4.txt";
|
||||
|
||||
interface Env {
|
||||
KV_STORAGE: KVNamespace;
|
||||
WORKER_SITE: Fetcher;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
try {
|
||||
const ip = getIp(request);
|
||||
if (ip !== "::1") {
|
||||
return await runVpnBlocker(request, env);
|
||||
} else {
|
||||
return forwardRequest(request, env);
|
||||
}
|
||||
} catch (e) {
|
||||
throw "Server Error";
|
||||
}
|
||||
},
|
||||
} satisfies ExportedHandler<Env>;
|
||||
|
||||
function forwardRequest(request, env) {
|
||||
// Forward the request to the origin
|
||||
return env.WORKER_SITE.fetch(request);
|
||||
}
|
||||
|
||||
const getIp = (request) => {
|
||||
try {
|
||||
try {
|
||||
const ipv4 = IPParser.parseIP(request.headers.get("CF-Connecting-IP"));
|
||||
return ipv4;
|
||||
} catch (e) {
|
||||
const v6ToV4 = IPParser.parseIP(request.headers.get("Cf-Pseudo-IPv4"));
|
||||
return v6ToV4;
|
||||
}
|
||||
} catch (e) {
|
||||
const fallback = request.headers.get("CF-Connecting-IP") as string;
|
||||
if (!fallback) {
|
||||
throw "Missing CF-Connecting-IP header";
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
};
|
||||
|
||||
function runVpnBlocker(request, env) {
|
||||
const reqIp = getIp(request).join(".");
|
||||
|
||||
if (!reqIp) {
|
||||
return new Response("Missing IP address", { status: 400 });
|
||||
}
|
||||
|
||||
const blockListContent: string = blockList as unknown as string;
|
||||
|
||||
const blocked = blockListContent
|
||||
.split("\n")
|
||||
.some((cidr: string) => IPParser.isInRange(reqIp, cidr));
|
||||
|
||||
if (blocked) {
|
||||
return new Response("Access Denied.\nReason: VPN Detected!\nCode: 403", {
|
||||
status: 403,
|
||||
});
|
||||
}
|
||||
return forwardRequest(request, env);
|
||||
}
|
64
workers/session-proxy/ip-parser.ts
Normal file
64
workers/session-proxy/ip-parser.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
export default class IPParser {
|
||||
/**
|
||||
* Parse and validate an IP address (IPv4 only for simplicity).
|
||||
* @param {string} ip - The IP address to parse.
|
||||
* @returns {number[]} - Parsed IP address as an array of numbers.
|
||||
* @throws {Error} - If the IP is invalid.
|
||||
*/
|
||||
static parseIP(ip) {
|
||||
const octets = ip?.split(".");
|
||||
if (octets?.length !== 4) {
|
||||
throw new Error("Invalid IP address format");
|
||||
}
|
||||
return octets.map((octet) => {
|
||||
const num = parseInt(octet, 10);
|
||||
if (isNaN(num) || num < 0 || num > 255) {
|
||||
throw new Error("Invalid IP address octet");
|
||||
}
|
||||
return num;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an IP address to a 32-bit integer for comparison.
|
||||
* @param {number[]} ipArray - IP address as an array of numbers.
|
||||
* @returns {number} - 32-bit integer representation of the IP.
|
||||
* @private
|
||||
*/
|
||||
static #ipToInt(ipArray) {
|
||||
return (
|
||||
((ipArray[0] << 24) |
|
||||
(ipArray[1] << 16) |
|
||||
(ipArray[2] << 8) |
|
||||
ipArray[3]) >>>
|
||||
0
|
||||
); // Ensure unsigned 32-bit
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an IP is within a given CIDR range.
|
||||
* @param {string} ip - The IP address to check.
|
||||
* @param {string} cidr - CIDR range (e.g., "192.168.0.0/24").
|
||||
* @returns {boolean} - True if the IP is within the range, otherwise false.
|
||||
* @throws {Error} - If either the IP or CIDR format is invalid.
|
||||
*/
|
||||
static isInRange(ip, cidr) {
|
||||
if (!cidr || typeof cidr !== "string" || cidr.length < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [range, prefixLength] = cidr.split("/");
|
||||
if (!prefixLength) {
|
||||
throw new Error("Invalid CIDR format");
|
||||
}
|
||||
|
||||
const ipArray = IPParser.parseIP(ip);
|
||||
const rangeArray = IPParser.parseIP(range);
|
||||
|
||||
const ipInt = IPParser.#ipToInt(ipArray);
|
||||
const rangeInt = IPParser.#ipToInt(rangeArray);
|
||||
|
||||
const mask = ~(2 ** (32 - parseInt(prefixLength, 10)) - 1) >>> 0;
|
||||
return (rangeInt & mask) === (ipInt & mask);
|
||||
}
|
||||
}
|
34
workers/session-proxy/wrangler-session-proxy.toml
Normal file
34
workers/session-proxy/wrangler-session-proxy.toml
Normal file
@@ -0,0 +1,34 @@
|
||||
name = "session-proxy-geoff-seemueller-io"
|
||||
main = "./index.ts"
|
||||
compatibility_date = "2024-12-20"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
workers_dev = false
|
||||
preview_urls = false
|
||||
dev.port = 3001
|
||||
|
||||
|
||||
[env.local]
|
||||
routes = [{ pattern = "dev.geoff.seemueller.io", custom_domain = true }]
|
||||
services = [
|
||||
{ binding = "WORKER_SITE", service = "geoff-seemueller-io" }
|
||||
]
|
||||
|
||||
[env.dev]
|
||||
#routes = [{ pattern = "dev.geoff.seemueller.io", custom_domain = true }]
|
||||
services = [
|
||||
{ binding = "WORKER_SITE", service = "geoff-seemueller-io-dev" }
|
||||
]
|
||||
|
||||
# Staging configuration
|
||||
[env.staging]
|
||||
#routes = [{ pattern = "geoff-staging.seemueller.io", custom_domain = true }]
|
||||
services = [
|
||||
{ binding = "WORKER_SITE", service = "geoff-seemueller-io-staging" }
|
||||
]
|
||||
|
||||
# Production configuration
|
||||
[env.production]
|
||||
routes = [{ pattern = "geoff.seemueller.io", custom_domain = true }]
|
||||
services = [
|
||||
{ binding = "WORKER_SITE", service = "geoff-seemueller-io-production" }
|
||||
]
|
Reference in New Issue
Block a user