Commit a05c64e5 authored by nanahira's avatar nanahira

everything before ipset

parent 10885155
......@@ -1155,6 +1155,14 @@
"@types/node": "*"
}
},
"@types/ip": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.0.tgz",
"integrity": "sha512-dwNe8gOoF70VdL6WJBwVHtQmAX4RMd62M+mAB9HQFjG1/qiCLM/meRy95Pd14FYBbEDwCq7jgJs89cHpLBu4HQ==",
"requires": {
"@types/node": "*"
}
},
"@types/istanbul-lib-coverage": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
......@@ -1210,8 +1218,7 @@
"@types/node": {
"version": "14.14.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz",
"integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==",
"dev": true
"integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ=="
},
"@types/normalize-package-data": {
"version": "2.4.0",
......@@ -1307,6 +1314,11 @@
}
}
},
"@types/underscore": {
"version": "1.10.24",
"resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.24.tgz",
"integrity": "sha512-T3NQD8hXNW2sRsSbLNjF/aBo18MyJlbw0lSpQHB/eZZtScPdexN4HSa8cByYwTw9Wy7KuOFr81mlDQcQQaZ79w=="
},
"@types/webpack": {
"version": "4.41.25",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.25.tgz",
......@@ -4043,6 +4055,11 @@
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
},
"ip-regex": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
......
import { Controller, Get } from '@nestjs/common';
import { Body, Controller, Get, Ip, Post } from '@nestjs/common';
import { AppService } from './app.service';
import { InjectConnection, InjectRepository } from '@nestjs/typeorm';
@Controller()
@Controller('api')
export class AppController {
constructor(private readonly appService: AppService) {}
@Get('gateways')
getConfig(): string {
return this.appService.getGateways();
@Get('data')
async getData(@Ip() ip: string) {
return await this.appService.getClientData(ip);
}
@Post('select')
async postData(
@Ip() ip: string,
@Body('destination') destination: string,
@Body('remoteGatewayId') remoteGatewayId: string,
@Body('localGatewayId') localGatewayId: string,
) {
return await this.appService.postData(
ip,
destination,
parseInt(remoteGatewayId),
parseInt(localGatewayId),
);
}
}
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ConfigModule } from '@nestjs/config';
import config from './config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/User';
import { AppLogger } from './logger.service';
@Module({
imports: [
......@@ -13,14 +14,14 @@ import { User } from './entities/User';
isGlobal: true,
}),
TypeOrmModule.forRoot({
name: 'railgun',
type: 'postgres',
entities: [User],
synchronize: true,
...config().db
name: 'railgun',
type: 'postgres',
entities: [User],
synchronize: true,
...config().db,
}),
],
controllers: [AppController],
providers: [AppService],
providers: [AppService, AppLogger],
})
export class AppModule {}
import { Injectable } from '@nestjs/common';
import { Injectable, LoggerService } from '@nestjs/common';
import { InjectConnection } from '@nestjs/typeorm';
import { Connection } from 'typeorm';
import { ConfigService } from '@nestjs/config';
import {
Router,
Gateway,
GatewayGroup,
RailgunRule,
GatewayOption,
} from './config';
import * as IP from 'ip';
import * as _ from 'underscore';
import { AppLogger } from './logger.service';
import { User } from './entities/User';
const validDestinations = ['chnroute', 'all'];
export interface RouteDisplayData {
id: number;
name: string;
description: string;
type: 'gateway' | 'group';
isLocal: boolean;
}
@Injectable()
export class AppService {
gateways: Gateway[];
routers: Router[];
gatewayGroups: GatewayGroup[];
railgunRules: RailgunRule[];
constructor(
@InjectConnection('railgun')
private db: Connection,
private configService: ConfigService,
) {}
getGateways(): string {
return this.configService.get('gateways');
private log: AppLogger,
) {
this.log.setContext('Railgun');
this.gateways = this.configService.get('gateways');
this.routers = this.configService.get('routers');
this.gatewayGroups = this.configService.get('gatewayGroups');
this.railgunRules = this.configService.get('railgunRules');
}
private subnetsContain(subnets: string[], ip) {
return subnets.some((subnet) => IP.cidrSubnet(subnet).contains(ip));
}
private isGatewayAllowed(ip: string, gateway: Gateway) {
if (!gateway.hidden) {
// 允许自己的网关的所有IP
const router = gateway.routerInfo;
if (router && this.subnetsContain(router.subnets, ip)) {
return true;
}
}
// 在 railgun enterprise 表中有的IP
return this.railgunRules.some((rule) => {
return (
_.contains(rule.gateways, `${gateway.router}/${gateway.isp}`) &&
this.subnetsContain(rule.allowedIPs, ip)
);
});
}
private isGatewayGroupAllowed(ip: string, gatewayGroup: GatewayGroup) {
// 在 railgun enterprise 表中有的IP
return this.railgunRules.some((rule) => {
return (
_.contains(rule.groups, gatewayGroup.name) &&
this.subnetsContain(rule.allowedIPs, ip)
);
});
}
private sortAddress(rawAddress: string) {
if (IP.isV4Format(rawAddress)) {
return rawAddress;
}
return rawAddress.slice(7);
}
private getAllSuitableGateways(ip: string) {
return this.gateways.filter((gateway) =>
this.isGatewayAllowed(ip, gateway),
);
}
private getGatewayDisplayData(
gateway: Gateway,
ip: string,
): RouteDisplayData {
return {
id: gateway.id,
name: `${gateway.router}/${gateway.isp}`,
description: gateway.description,
type: 'gateway',
isLocal: this.subnetsContain(gateway.routerInfo.subnets, ip),
};
}
private getGroupDisplayData(group: GatewayGroup): RouteDisplayData {
return {
id: group.id,
name: group.name,
description: group.description,
type: 'group',
isLocal: false,
};
}
private async findOrCreateUser(ip: string) {
const repo = this.db.getRepository(User);
let user = await repo.findOne({
where: { ip },
});
if (user) {
return user;
}
user = new User();
user.ip = ip;
return await repo.save(user);
}
async getClientData(rawAddress: string) {
const ip = this.sortAddress(rawAddress);
const displayGateways = this.gateways
.filter((gateway) => this.isGatewayAllowed(ip, gateway))
.map((gateway) => this.getGatewayDisplayData(gateway, ip));
const displayGroups = this.gatewayGroups
.filter((group) => this.isGatewayGroupAllowed(ip, group))
.map((group) => this.getGroupDisplayData(group));
const user = await this.findOrCreateUser(ip);
return {
gateways: displayGateways.concat(displayGroups),
user,
};
}
private checkId(id: number, ip: string) {
if (!id) {
return true;
}
if (id > 20000) {
const gatewayGroup = this.gatewayGroups.find((g) => g.id === id);
return gatewayGroup && this.isGatewayGroupAllowed(ip, gatewayGroup);
} else {
const gateway = this.gateways.find((gw) => gw.id === id);
return this.isGatewayAllowed(ip, gateway);
}
}
async postData(
rawAddress: string,
destination: string,
remoteGatewayId: number,
localGatewayId: number,
) {
const ip = this.sortAddress(rawAddress);
const user = await this.findOrCreateUser(ip);
if (destination && !_.contains(validDestinations, destination)) {
return {
success: false,
statusCode: 400,
message: 'invalid destination',
};
}
if (
!this.checkId(remoteGatewayId, ip) ||
!this.checkId(localGatewayId, ip)
) {
return { success: false, statusCode: 404, message: 'invalid gateway' };
}
user.destination = destination || null;
user.remoteGatewayId = remoteGatewayId || null;
user.localGatewayId = localGatewayId || null;
await this.db.getRepository(User).save(user);
return { success: true, statusCode: 200, message: 'success' };
}
}
......@@ -4,6 +4,7 @@ import * as parse from 'csv-parse/lib/sync';
export interface Router {
id: number;
name: string;
address: string;
subnets: string[];
lanInterfaces: string[];
......@@ -24,11 +25,15 @@ export interface Router {
frpsPort: number;
}
export interface Gateway {
export interface GatewayOption {
id: number;
description: string;
}
export interface Gateway extends GatewayOption {
router: string;
routerInfo: Router;
isp: string;
description: string;
hidden: number;
address: string;
ipv4: string;
......@@ -47,8 +52,7 @@ export interface RailgunRule {
gateways: string[];
}
export interface GatewayGroup {
id: number;
export interface GatewayGroup extends GatewayOption {
name: string;
locationPrefix: string[];
includeRouters: string[];
......@@ -87,6 +91,17 @@ function loadSheet(sheetName: string, splitColumns: string[]) {
}
export default function config() {
const routers = loadSheet('nextgen2', [
'subnets',
'lanInterfaces',
'splitInterfaces',
]) as Router[];
const gateways = loadSheet('gateways2', []) as Gateway[];
for (const gateway of gateways) {
gateway.routerInfo = routers.find(
(router) => router.name === gateway.router,
);
}
return {
db: {
host: process.env.DB_HOST,
......@@ -95,12 +110,8 @@ export default function config() {
password: process.env.DB_PASS,
database: process.env.DB_NAME,
},
routers: loadSheet('nextgen2', [
'subnets',
'lanInterfaces',
'splitInterfaces',
]) as Router[],
gateways: loadSheet('gateways2', []) as Gateway[],
routers,
gateways,
gatewayGroups: loadSheet('gateway groups', [
'locationPrefix',
'includeRouters',
......
import { Column, Entity, Index, PrimaryColumn } from 'typeorm';
import { Column, Entity, PrimaryColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryColumn({ type: 'varchar', length: 16 })
ip: string;
@Column('varchar', { length: 16 })
rule: string;
@Column('varchar', { length: 16, nullable: true })
destination: string;
@Column('smallint', { unsigned: true })
@Column('smallint', { unsigned: true, nullable: true })
remoteGatewayId: number;
@Column('smallint', { unsigned: true })
@Column('smallint', { unsigned: true, nullable: true })
localGatewayId: number;
}
import { Injectable, Scope, Logger } from '@nestjs/common';
@Injectable()
export class AppLogger extends Logger {}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment