Add ASN-based resource rule matching

- Add MaxMind ASN database integration
- Implement ASN lookup and matching in resource rule verification
- Add curated list of 100+ major ASNs (cloud, ISP, CDN, mobile carriers)
- Add ASN dropdown selector in resource rules UI with search functionality
- Support custom ASN input for unlisted ASNs
- Add 'ALL ASNs' special case handling (AS0)
- Cache ASN lookups with 5-minute TTL for performance
- Update validation schemas to support ASN match type

This allows administrators to create resource access rules based on
Autonomous System Numbers, similar to existing country-based rules.
Useful for restricting access by ISP, cloud provider, or mobile carrier.
This commit is contained in:
Thomas Wilde
2025-12-16 11:18:54 -07:00
committed by Owen
parent 981d777a65
commit 4f154d212e
11 changed files with 678 additions and 9 deletions

29
server/lib/asn.ts Normal file
View File

@@ -0,0 +1,29 @@
import logger from "@server/logger";
import { maxmindAsnLookup } from "@server/db/maxmindAsn";
export async function getAsnForIp(ip: string): Promise<number | undefined> {
try {
if (!maxmindAsnLookup) {
logger.debug(
"MaxMind ASN DB path not configured, cannot perform ASN lookup"
);
return;
}
const result = maxmindAsnLookup.get(ip);
if (!result || !result.autonomous_system_number) {
return;
}
logger.debug(
`ASN lookup successful for IP ${ip}: AS${result.autonomous_system_number}`
);
return result.autonomous_system_number;
} catch (error) {
logger.error("Error performing ASN lookup:", error);
}
return;
}

View File

@@ -99,6 +99,10 @@ export class Config {
process.env.MAXMIND_DB_PATH = parsedConfig.server.maxmind_db_path;
}
if (parsedConfig.server.maxmind_asn_path) {
process.env.MAXMIND_ASN_PATH = parsedConfig.server.maxmind_asn_path;
}
this.rawConfig = parsedConfig;
}

View File

@@ -133,7 +133,8 @@ export const configSchema = z
.optional(),
trust_proxy: z.int().gte(0).optional().default(1),
secret: z.string().pipe(z.string().min(8)).optional(),
maxmind_db_path: z.string().optional()
maxmind_db_path: z.string().optional(),
maxmind_asn_path: z.string().optional()
})
.optional()
.default({