cvmts: Add staff audit logging support

Basically what it says on the tin.

More staff operations should probably be audited, but for now this provides a good starting point.
This commit is contained in:
modeco80 2024-09-21 21:14:27 -04:00
parent 072fd06918
commit 41ee71f053
3 changed files with 73 additions and 2 deletions

5
.gitignore vendored
View file

@ -14,4 +14,7 @@ cvm-rs/target
cvm-rs/index.node
# geolite shit
**/geoip/
**/geoip/
# staff audit log
audit.log

62
cvmts/src/AuditLog.ts Normal file
View file

@ -0,0 +1,62 @@
import pino from 'pino';
import { Rank, User } from './User.js';
// Staff audit log.
// TODO:
// - Hook this up to a db or something instead of misusing pino
export class AuditLog {
private auditLogger = pino({
name: 'AuditLog',
transport: {
target: 'pino/file',
options: {
destination: './audit.log'
}
}
});
private static StaffHonorFromRank(user: User, uppercase: boolean) {
switch (user.rank) {
case Rank.Moderator:
if (uppercase) return 'Moderator';
else return 'moderator';
case Rank.Admin:
if (uppercase) return 'Administrator';
else return 'administrator';
default:
throw new Error("input user is not staff.. how'd you even get here?");
}
}
onReset(callingUser: User) {
this.auditLogger.info({ staffUsername: callingUser.username }, `${AuditLog.StaffHonorFromRank(callingUser, true)} reset the virtual machine.`);
}
onReboot(callingUser: User) {
this.auditLogger.info({ staffUsername: callingUser.username }, `${AuditLog.StaffHonorFromRank(callingUser, true)} rebooted the virtual machine.`);
}
onMute(callingUser: User, target: User, perm: boolean) {
this.auditLogger.info({ staffUsername: callingUser.username, targetUsername: target.username, perm: perm }, `${AuditLog.StaffHonorFromRank(callingUser, true)} muted user.`);
}
onUnmute(callingUser: User, target: User) {
this.auditLogger.info({ staffUsername: callingUser.username, targetUsername: target.username }, `${AuditLog.StaffHonorFromRank(callingUser, true)} unmuted user.`);
}
onKick(callingUser: User, target: User) {
this.auditLogger.info({ staffUsername: callingUser.username, targetUsername: target.username }, `${AuditLog.StaffHonorFromRank(callingUser, true)} kicked user.`);
}
onBan(callingUser: User, target: User) {
this.auditLogger.info({ staffUsername: callingUser.username, targetUsername: target.username }, `${AuditLog.StaffHonorFromRank(callingUser, true)} banned user.`);
}
onMonitorCommand(callingUser: User, command: string) {
this.auditLogger.info({ staffUsername: callingUser.username, commandLine: command }, `${AuditLog.StaffHonorFromRank(callingUser, true)} executed monitor command.`);
}
}
export let TheAuditLog = new AuditLog();

View file

@ -20,6 +20,7 @@ import { CollabVMProtocolMessage, CollabVMProtocolMessageType } from '@cvmts/col
import { Size, Rect } from './Utilities.js';
import pino from 'pino';
import { BanManager } from './BanManager.js';
import { TheAuditLog } from './AuditLog.js';
// Instead of strange hacks we can just use nodejs provided
// import.meta properties, which have existed since LTS if not before
@ -526,18 +527,21 @@ export default class CollabVMServer {
// QEMU Monitor
if (client.rank !== Rank.Admin) return;
if (msgArr.length !== 4 || msgArr[2] !== this.Config.collabvm.node) return;
TheAuditLog.onMonitorCommand(client, msgArr[3]);
let output = await this.VM.MonitorCommand(msgArr[3]);
client.sendMsg(cvm.guacEncode('admin', '2', String(output)));
break;
case '8':
// Restore
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.restore)) return;
TheAuditLog.onReset(client);
this.VM.Reset();
break;
case '10':
// Reboot
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.reboot)) return;
if (msgArr.length !== 3 || msgArr[2] !== this.Config.collabvm.node) return;
TheAuditLog.onReboot(client);
await this.VM.Reboot();
break;
case '12':
@ -545,7 +549,7 @@ export default class CollabVMServer {
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.ban)) return;
var user = this.clients.find((c) => c.username === msgArr[2]);
if (!user) return;
this.logger.info(`Banning ${user.username!} (${user.IP.address}) by request of ${client.username!}`);
TheAuditLog.onBan(client, user);
user.ban(this.banmgr);
case '13':
// Force Vote
@ -578,6 +582,7 @@ export default class CollabVMServer {
default:
return;
}
//TheAdminLogger.onMute(client, user, permamute);
user.mute(permamute);
break;
case '15':
@ -585,6 +590,7 @@ export default class CollabVMServer {
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.kick)) return;
var user = this.clients.find((c) => c.username === msgArr[2]);
if (!user) return;
TheAuditLog.onKick(client, user);
user.kick();
break;
case '16':