Commit a05c64e5 authored by nanahira's avatar nanahira

everything before ipset

parent 10885155
...@@ -1155,6 +1155,14 @@ ...@@ -1155,6 +1155,14 @@
"@types/node": "*" "@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": { "@types/istanbul-lib-coverage": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
...@@ -1210,8 +1218,7 @@ ...@@ -1210,8 +1218,7 @@
"@types/node": { "@types/node": {
"version": "14.14.13", "version": "14.14.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz",
"integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==", "integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ=="
"dev": true
}, },
"@types/normalize-package-data": { "@types/normalize-package-data": {
"version": "2.4.0", "version": "2.4.0",
...@@ -1307,6 +1314,11 @@ ...@@ -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": { "@types/webpack": {
"version": "4.41.25", "version": "4.41.25",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.25.tgz", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.25.tgz",
...@@ -4043,6 +4055,11 @@ ...@@ -4043,6 +4055,11 @@
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true "dev": true
}, },
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
},
"ip-regex": { "ip-regex": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", "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 { AppService } from './app.service';
import { InjectConnection, InjectRepository } from '@nestjs/typeorm';
@Controller() @Controller('api')
export class AppController { export class AppController {
constructor(private readonly appService: AppService) {} constructor(private readonly appService: AppService) {}
@Get('gateways') @Get('data')
getConfig(): string { async getData(@Ip() ip: string) {
return this.appService.getGateways(); 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 { Module } from '@nestjs/common';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { AppService } from './app.service'; import { AppService } from './app.service';
import { ConfigModule, ConfigService } from '@nestjs/config'; import { ConfigModule } from '@nestjs/config';
import config from './config'; import config from './config';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/User'; import { User } from './entities/User';
import { AppLogger } from './logger.service';
@Module({ @Module({
imports: [ imports: [
...@@ -17,10 +18,10 @@ import { User } from './entities/User'; ...@@ -17,10 +18,10 @@ import { User } from './entities/User';
type: 'postgres', type: 'postgres',
entities: [User], entities: [User],
synchronize: true, synchronize: true,
...config().db ...config().db,
}), }),
], ],
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService, AppLogger],
}) })
export class AppModule {} export class AppModule {}
import { Injectable } from '@nestjs/common'; import { Injectable, LoggerService } from '@nestjs/common';
import { InjectConnection } from '@nestjs/typeorm'; import { InjectConnection } from '@nestjs/typeorm';
import { Connection } from 'typeorm'; import { Connection } from 'typeorm';
import { ConfigService } from '@nestjs/config'; 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() @Injectable()
export class AppService { export class AppService {
gateways: Gateway[];
routers: Router[];
gatewayGroups: GatewayGroup[];
railgunRules: RailgunRule[];
constructor( constructor(
@InjectConnection('railgun') @InjectConnection('railgun')
private db: Connection, private db: Connection,
private configService: ConfigService, private configService: ConfigService,
) {} private log: AppLogger,
getGateways(): string { ) {
return this.configService.get('gateways'); 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'; ...@@ -4,6 +4,7 @@ import * as parse from 'csv-parse/lib/sync';
export interface Router { export interface Router {
id: number; id: number;
name: string;
address: string; address: string;
subnets: string[]; subnets: string[];
lanInterfaces: string[]; lanInterfaces: string[];
...@@ -24,11 +25,15 @@ export interface Router { ...@@ -24,11 +25,15 @@ export interface Router {
frpsPort: number; frpsPort: number;
} }
export interface Gateway { export interface GatewayOption {
id: number; id: number;
description: string;
}
export interface Gateway extends GatewayOption {
router: string; router: string;
routerInfo: Router;
isp: string; isp: string;
description: string;
hidden: number; hidden: number;
address: string; address: string;
ipv4: string; ipv4: string;
...@@ -47,8 +52,7 @@ export interface RailgunRule { ...@@ -47,8 +52,7 @@ export interface RailgunRule {
gateways: string[]; gateways: string[];
} }
export interface GatewayGroup { export interface GatewayGroup extends GatewayOption {
id: number;
name: string; name: string;
locationPrefix: string[]; locationPrefix: string[];
includeRouters: string[]; includeRouters: string[];
...@@ -87,6 +91,17 @@ function loadSheet(sheetName: string, splitColumns: string[]) { ...@@ -87,6 +91,17 @@ function loadSheet(sheetName: string, splitColumns: string[]) {
} }
export default function config() { 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 { return {
db: { db: {
host: process.env.DB_HOST, host: process.env.DB_HOST,
...@@ -95,12 +110,8 @@ export default function config() { ...@@ -95,12 +110,8 @@ export default function config() {
password: process.env.DB_PASS, password: process.env.DB_PASS,
database: process.env.DB_NAME, database: process.env.DB_NAME,
}, },
routers: loadSheet('nextgen2', [ routers,
'subnets', gateways,
'lanInterfaces',
'splitInterfaces',
]) as Router[],
gateways: loadSheet('gateways2', []) as Gateway[],
gatewayGroups: loadSheet('gateway groups', [ gatewayGroups: loadSheet('gateway groups', [
'locationPrefix', 'locationPrefix',
'includeRouters', 'includeRouters',
......
import { Column, Entity, Index, PrimaryColumn } from 'typeorm'; import { Column, Entity, PrimaryColumn } from 'typeorm';
@Entity() @Entity()
export class User { export class User {
@PrimaryColumn({ type: 'varchar', length: 16 }) @PrimaryColumn({ type: 'varchar', length: 16 })
ip: string; ip: string;
@Column('varchar', { length: 16 }) @Column('varchar', { length: 16, nullable: true })
rule: string; destination: string;
@Column('smallint', { unsigned: true }) @Column('smallint', { unsigned: true, nullable: true })
remoteGatewayId: number; remoteGatewayId: number;
@Column('smallint', { unsigned: true }) @Column('smallint', { unsigned: true, nullable: true })
localGatewayId: number; 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