Skip to content

Commit

Permalink
🐛 Update front and backend
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandreT-DevId committed Mar 22, 2024
1 parent bd923e1 commit 4c49d9d
Show file tree
Hide file tree
Showing 108 changed files with 43,425 additions and 710 deletions.
15 changes: 15 additions & 0 deletions Backend/src/_utils/decorators/protect.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { applyDecorators, UseGuards } from '@nestjs/common';
import { RoleEnum } from '../../user/_utils/enums/role.enum';
import { ApiBearerAuth, ApiForbiddenResponse } from '@nestjs/swagger';
import { GrpcAuthGuard } from '../../user/_utils/jwt/grpc-auth.guard';
import { Roles } from './roles.decorator';
import { RolesGuard } from '../../user/_utils/jwt/roles.guard';

export function Protect(...roles: RoleEnum[]) {
return applyDecorators(
ApiBearerAuth(),
Roles(...roles),
UseGuards(GrpcAuthGuard, RolesGuard),
ApiForbiddenResponse({ description: 'Unauthorized' }),
);
}
5 changes: 5 additions & 0 deletions Backend/src/_utils/decorators/roles.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SetMetadata } from '@nestjs/common';
import { RoleEnum } from '../../user/_utils/enums/role.enum';

export const ROLES_KEY = 'role';
export const Roles = (...roles: RoleEnum[]) => SetMetadata(ROLES_KEY, roles);
10 changes: 8 additions & 2 deletions Backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { EnvironmentVariables, validateEnv } from './_utils/config/config';
import { HelloworldModule } from './helloworld/helloworld.module';
import { LogsModule } from './logs/logs.module';
import { DashboardModule } from './dashboard/dashboard.module';
import { ContainersModule } from './containers/containers.module';
Expand All @@ -10,6 +9,10 @@ import { UserModule } from './user/user.module';
import { MongooseModule } from '@nestjs/mongoose';
import { GlobalModule } from './_utils/global.module';
import { MailsModule } from './mails/mails.module';
import { RulesModule } from './rules/rules.module';
import { MobileModule } from './mobile/mobile.module';
import { HistoryModule } from './history/history.module';
import { ReconfigureModule } from './reconfig/reconfig.module';

@Module({
imports: [
Expand All @@ -23,13 +26,16 @@ import { MailsModule } from './mails/mails.module';
}),
}),
ConfigModule.forRoot({ validate: validateEnv, isGlobal: true }),
HelloworldModule,
MailsModule,
LogsModule,
DashboardModule,
ContainersModule,
BlacklistModule,
UserModule,
RulesModule,
MobileModule,
HistoryModule,
ReconfigureModule,
],
})
export class AppModule {}
17 changes: 17 additions & 0 deletions Backend/src/blacklist/_utils/blacklist.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ package blacklist;

service Blacklist {
rpc PutBlackList (PutBlackListRequest) returns (PutBlackListReply) {}
rpc BlockCountry (BlockCountryRequest) returns (BlockCountryReply) {}
rpc UnblockCountry (BlockCountryRequest) returns (BlockCountryReply) {}
rpc GetBlackList (GetBlackListRequest) returns (stream GetBlackListReply) {}
rpc GetBlackListUnary (GetBlackListRequest) returns (GetBlackListReply) {}
rpc PutWhiteList (PutWhiteListRequest) returns (PutWhiteListReply) {}
rpc GetBlockCountry (GetBlockCountryRequest) returns (GetBlockCountryReply) {}
}

message PutBlackListRequest {
Expand All @@ -15,6 +19,13 @@ message PutBlackListRequest {
message PutBlackListReply {
}

message BlockCountryRequest {
string countryCode = 1;
}

message BlockCountryReply {
}

message GetBlackListRequest {}

message GetBlackListReply {
Expand All @@ -27,3 +38,9 @@ message PutWhiteListRequest {

message PutWhiteListReply {
}

message GetBlockCountryRequest {}

message GetBlockCountryReply {
repeated string countries = 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { IsString } from 'class-validator';

export class BlockCountryRequestDto {
@IsString()
countryCode: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class GetBlockCountryReply {
countries: string[];
}

23 changes: 23 additions & 0 deletions Backend/src/blacklist/blacklist.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import { ServerUnaryCall } from '@grpc/grpc-js';
import { GetIdsDto } from './_utils/dto/response/get-ids.dto';
import { ApiTags } from '@nestjs/swagger';
import { SetIpDto } from './_utils/dto/request/set-ip.dto';
import { BlockCountryRequestDto } from './_utils/dto/request/block-country-request.dto';
import { GetBlockCountryReply } from './_utils/dto/response/get-block-country-reply.dto';

@Controller('blacklist')
@ApiTags('Blacklist')
export class BlacklistController {
constructor(private readonly blacklistService: BlacklistService) {}

@GrpcMethod('Blacklist', 'GetBlackListUnary')
getBlackListUnary() {
return this.blacklistService.getBlackListUnary();
}

@GrpcMethod('Blacklist', 'GetBlackList')
getBlacklist$(_data: unknown, _metadata: unknown, call: ServerUnaryCall<unknown, GetIdsDto>) {
return this.blacklistService.getBlackList$(call);
Expand All @@ -21,6 +28,22 @@ export class BlacklistController {
return this.blacklistService.putBlackList(setIdDto);
}

@GrpcMethod('Blacklist', 'BlockCountry')
BlockCountry(blockCountryRequestDto: BlockCountryRequestDto) {
return this.blacklistService.blockCountry(blockCountryRequestDto.countryCode);
}

@GrpcMethod('Blacklist', 'UnblockCountry')
UnblockCountry(blockCountryRequestDto: BlockCountryRequestDto) {
return this.blacklistService.unblockCountry(blockCountryRequestDto.countryCode);
}

@GrpcMethod('Blacklist', 'GetBlockCountry')
getBlockCountry(_data: unknown, _metadata: unknown, call: ServerUnaryCall<unknown, GetBlockCountryReply>) {
return this.blacklistService.getBlockedCountries();
}


@GrpcMethod('Blacklist', 'PutWhiteList')
PutWhiteList(setIdDto: SetIpDto) {
return this.blacklistService.putWhiteList(setIdDto);
Expand Down
2 changes: 2 additions & 0 deletions Backend/src/blacklist/blacklist.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Module } from '@nestjs/common';
import { BlacklistService } from './blacklist.service';
import { BlacklistController } from './blacklist.controller';
import { HistoryModule } from 'src/history/history.module';

@Module({
controllers: [BlacklistController],
providers: [BlacklistService],
imports: [HistoryModule],
exports: [BlacklistService],
})
export class BlacklistModule {}
122 changes: 122 additions & 0 deletions Backend/src/blacklist/blacklist.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@ import { SetIpDto } from './_utils/dto/request/set-ip.dto';
import * as Docker from 'dockerode';
import { RpcException } from '@nestjs/microservices';
import { readFile, writeFile } from 'fs/promises';
import { HistoryRepository } from '../history/history.repository';
import { GetBlockCountryReply } from './_utils/dto/response/get-block-country-reply.dto';

@Injectable()
export class BlacklistService {

constructor(
private historyRepository: HistoryRepository,
// ... autres dépendances ...
) {}

private docker = new Docker();
private previousBlockedIps: string[] = [];

private async processFileChange(path: string, subject: Subject<GetIdsDto>) {
readFile(path, 'utf8')
Expand All @@ -27,12 +36,42 @@ export class BlacklistService {
.filter((ip) => ip !== null) as string[];

subject.next({ ips });

const newBlockedIps = ips.filter(ip => !this.previousBlockedIps.includes(ip));
newBlockedIps.forEach(async (ip) => {
await this.historyRepository.createHistoryEntry({
date: new Date(),
actionType: 'attack',
description: `Attaque détectée avec blocage de l'IP ${ip}`,
});
});
this.previousBlockedIps = ips;
})
.catch((err) => {
console.error(`Error reading block.conf: ${err}`);
throw new RpcException(`Error reading file: ${err}`);
});
}

async getBlackListUnary(): Promise<GetIdsDto> {
const filePath = '/app/honeypot/block.conf';
try {
const blockContent = await readFile(filePath, 'utf8');
const regex = /deny\s+((?:\d{1,3}\.){3}\d{1,3});/g;
const matches = blockContent.match(regex) || [];

const ips = matches.map((entry) => {
const ipRegex = /((?:\d{1,3}\.){3}\d{1,3})/;
const match = entry.match(ipRegex);
return match ? match[1] : null;
}).filter((ip) => ip !== null) as string[];

return { ips };
} catch (err) {
throw new RpcException(`Error reading block.conf: ${err}`);
}
}

getBlackList$(call: ServerUnaryCall<unknown, GetIdsDto>) {
const subject = new Subject<GetIdsDto>();

Expand Down Expand Up @@ -77,6 +116,89 @@ export class BlacklistService {
throw new RpcException(`FFailed to start execution iptables: ${err}`);
});
}

private async restartContainer(name: string): Promise<void> {
try {
const container = this.docker.getContainer(name);
await container.restart();
console.log('Fail2Ban container restarted successfully');
} catch (error) {
console.error('Error restarting Fail2Ban container:', error);
throw error;
}
}

async blockCountry(countryCode: string) {
if (!countryCode) throw new RpcException('countryCode is required');
const filePath = "/app/honeypot/geohostsdeny.conf";

try {
const blockConf = await readFile(filePath, 'utf8');

const lines = blockConf.split('\n');
const countryListIndex = lines.findIndex(line => line.trim().startsWith('country_list ='));
if (countryListIndex !== -1) {
const countryListParts = lines[countryListIndex].split('=');
const existingCountries = countryListParts[1].trim().split("|").filter(Boolean);
if (!existingCountries.includes(countryCode)) {
existingCountries.push(countryCode);
}
lines[countryListIndex] = `country_list = ${existingCountries.join("|")}`;
}

await writeFile(filePath, lines.join('\n'), 'utf8');
await this.restartContainer("suricata");

} catch (err) {
throw new RpcException(`Error while updating the file: ${err}`);
}
}

async unblockCountry(countryCode: string) {
if (!countryCode) throw new RpcException('countryCode is required');
const filePath = "/app/honeypot/geohostsdeny.conf";

try {
const blockConf = await readFile(filePath, 'utf8');

const lines = blockConf.split('\n');
const countryListIndex = lines.findIndex(line => line.trim().startsWith('country_list ='));
if (countryListIndex !== -1) {
const countryListParts = lines[countryListIndex].split('=');
let existingCountries = countryListParts[1].trim().split("|").filter(Boolean);
existingCountries = existingCountries.filter(c => c !== countryCode);
lines[countryListIndex] = `country_list = ${existingCountries.join("|")}`;
}

await writeFile(filePath, lines.join('\n'), 'utf8');
await this.restartContainer("suricata");

} catch (err) {
throw new RpcException(`Error while updating the file: ${err}`);
}
}

async getBlockedCountries(): Promise<GetBlockCountryReply> {
const filePath = "/app/honeypot/geohostsdeny.conf";

try {
const blockConf = await readFile(filePath, 'utf8');
const lines = blockConf.split('\n');
const countryListIndex = lines.findIndex(line => line.trim().startsWith('country_list ='));

if (countryListIndex !== -1) {
const countryListParts = lines[countryListIndex].split('=');
const existingCountries = countryListParts[1].trim().split("|").filter(Boolean);
return { countries: existingCountries }; // Retourne la liste des pays
}

// Si la liste des pays n'est pas trouvée, retourne un objet avec une liste vide
return { countries: [] };
} catch (err) {
console.error(`Error while reading the file: ${err}`);
throw new RpcException(`Error while reading the file: ${err}`);
}
}

async putWhiteList(setIdDto: SetIpDto) {
if (!setIdDto.ip) throw new RpcException('IP address is required');
Expand Down
1 change: 1 addition & 0 deletions Backend/src/containers/_utils/containers.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package containers;

service Containers {
rpc StreamContainers (ContainersRequest) returns (stream ContainersReply) {}
rpc GetContainers (ContainersRequest) returns (ContainersReply) {}
}

message ContainersRequest {}
Expand Down
5 changes: 5 additions & 0 deletions Backend/src/containers/containers.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import { GrpcMethod } from '@nestjs/microservices';
export class ContainersController {
constructor(private readonly containersService: ContainersService) {}

@GrpcMethod('Containers', 'GetContainers')
getContainers() {
return this.containersService.getContainers();
}

@GrpcMethod('Containers', 'StreamContainers')
streamContainers() {
return this.containersService.streamContainers();
Expand Down
31 changes: 31 additions & 0 deletions Backend/src/containers/containers.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@ import * as Docker from 'dockerode';
export class ContainersService {
private docker = new Docker();

private async getContainerData(): Promise<ContainersReplyDto> {
const network = await this.docker.getNetwork('honeypot_network').inspect();

const networkContainers = network.Containers;
const containerIds = Object.keys(networkContainers);

const containers = await this.docker.listContainers({ all: true });
const buildContainers = containers.filter((container) => container.Names[0].startsWith('/honeypot_'));

const containersData = buildContainers.map((container): Container => {
const networkContainerKey = containerIds.find((key) => key.startsWith(container.Id));
const networkContainer = networkContainerKey ? networkContainers[networkContainerKey] : undefined;

return {
ip: networkContainer ? networkContainer.IPv4Address.split('/')[0] : 'Not found',
name: container.Names[0].substring(1),
status: container.State,
};
});

return { containers: containersData };
}

public async getContainers(): Promise<ContainersReplyDto> {
try {
return await this.getContainerData();
} catch (error) {
throw new Error(`Error fetching container data: ${error}`);
}
}

private async handleContainerEvent(subject: Subject<ContainersReplyDto>) {
const network = await this.docker.getNetwork('honeypot_network').inspect();

Expand Down
4 changes: 2 additions & 2 deletions Backend/src/dashboard/dashboard.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export class DashboardService {
});

const containers$ = this.containersService.streamContainers().subscribe((containerReplyDto) => {
dashboard.containers = containerReplyDto.containers;
subject$.next(dashboard);
dashboard.containers = containerReplyDto.containers;
subject$.next(dashboard);
});

const blacklist$ = this.blacklistService.getBlackList$(call).subscribe((blacklistReplyDto) => {
Expand Down
Loading

0 comments on commit 4c49d9d

Please sign in to comment.