aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorkamtschatka <simon.schatka@gmx.at>2024-10-19 22:24:26 +0200
committerGitHub <noreply@github.com>2024-10-19 21:24:26 +0100
commit0debc6b415baa466245901fb52c009d09ef3ba15 (patch)
tree8590ad3849dd2652dd567308f9cc9ace125c691d /packages
parente55362ec57f2a20ed096f971e769269b6f8211c8 (diff)
downloadkarakeep-0debc6b415baa466245901fb52c009d09ef3ba15.tar.zst
feature: Log authentication failures to support fail2ban. Fixes #477 (#569)
* How do I set the variable "user" or "system" for AI inference #262 changed from system to user * [Feature Request] Log failed login attempts for fail2ban implementation #477 added logging of failed logins * [Feature Request] Log failed login attempts for fail2ban implementation #477 added more logging for extension related logins * Propagte IP to trpc --------- Co-authored-by: Your Name <you@example.com>
Diffstat (limited to 'packages')
-rw-r--r--packages/shared/logger.ts19
-rw-r--r--packages/trpc/auth.ts11
-rw-r--r--packages/trpc/index.ts6
-rw-r--r--packages/trpc/routers/apiKeys.ts27
-rw-r--r--packages/trpc/testUtils.ts3
5 files changed, 59 insertions, 7 deletions
diff --git a/packages/shared/logger.ts b/packages/shared/logger.ts
index f406b447..f3aa3cb9 100644
--- a/packages/shared/logger.ts
+++ b/packages/shared/logger.ts
@@ -15,3 +15,22 @@ const logger = winston.createLogger({
});
export default logger;
+
+export const authFailureLogger = winston.createLogger({
+ level: "debug",
+ format: winston.format.combine(
+ winston.format.timestamp(),
+ winston.format.printf(
+ (info) => `${info.timestamp} ${info.level}: ${info.message}`,
+ ),
+ ),
+ transports: [
+ new winston.transports.Console(),
+ new winston.transports.File({
+ filename: "auth_failures.log",
+ dirname: serverConfig.dataDir,
+ maxFiles: 2,
+ maxsize: 1024 * 1024,
+ }),
+ ],
+});
diff --git a/packages/trpc/auth.ts b/packages/trpc/auth.ts
index 39aebd3b..1efbdde6 100644
--- a/packages/trpc/auth.ts
+++ b/packages/trpc/auth.ts
@@ -4,6 +4,7 @@ import * as bcrypt from "bcryptjs";
import { db } from "@hoarder/db";
import { apiKeys } from "@hoarder/db/schema";
import serverConfig from "@hoarder/shared/config";
+import { authFailureLogger } from "@hoarder/shared/logger";
// API Keys
@@ -102,3 +103,13 @@ export async function validatePassword(email: string, password: string) {
return user;
}
+
+export function logAuthenticationError(
+ user: string,
+ message: string,
+ ip: string | null,
+): void {
+ authFailureLogger.error(
+ `Authentication error. User: "${user}", Message: "${message}", IP-Address: "${ip}"`,
+ );
+}
diff --git a/packages/trpc/index.ts b/packages/trpc/index.ts
index 5f351a8e..26d8ea96 100644
--- a/packages/trpc/index.ts
+++ b/packages/trpc/index.ts
@@ -15,11 +15,17 @@ interface User {
export interface Context {
user: User | null;
db: typeof db;
+ req: {
+ ip: string | null;
+ };
}
export interface AuthedContext {
user: User;
db: typeof db;
+ req: {
+ ip: string | null;
+ };
}
// Avoid exporting the entire t-object
diff --git a/packages/trpc/routers/apiKeys.ts b/packages/trpc/routers/apiKeys.ts
index b7468dd2..c55dc095 100644
--- a/packages/trpc/routers/apiKeys.ts
+++ b/packages/trpc/routers/apiKeys.ts
@@ -5,7 +5,12 @@ import { z } from "zod";
import { apiKeys } from "@hoarder/db/schema";
import serverConfig from "@hoarder/shared/config";
-import { authenticateApiKey, generateApiKey, validatePassword } from "../auth";
+import {
+ authenticateApiKey,
+ generateApiKey,
+ logAuthenticationError,
+ validatePassword,
+} from "../auth";
import { authedProcedure, publicProcedure, router } from "../index";
const zApiKeySchema = z.object({
@@ -73,7 +78,7 @@ export const apiKeysAppRouter = router({
}),
)
.output(zApiKeySchema)
- .mutation(async ({ input }) => {
+ .mutation(async ({ input, ctx }) => {
let user;
// Special handling as otherwise the extension would show "username or password is wrong"
if (serverConfig.auth.disablePasswordAuth) {
@@ -85,6 +90,8 @@ export const apiKeysAppRouter = router({
try {
user = await validatePassword(input.email, input.password);
} catch (e) {
+ const error = e as Error;
+ logAuthenticationError(input.email, error.message, ctx.req.ip);
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return await generateApiKey(input.keyName, user.id);
@@ -92,10 +99,16 @@ export const apiKeysAppRouter = router({
validate: publicProcedure
.input(z.object({ apiKey: z.string() }))
.output(z.object({ success: z.boolean() }))
- .mutation(async ({ input }) => {
- await authenticateApiKey(input.apiKey); // Throws if the key is invalid
- return {
- success: true,
- };
+ .mutation(async ({ input, ctx }) => {
+ try {
+ await authenticateApiKey(input.apiKey); // Throws if the key is invalid
+ return {
+ success: true,
+ };
+ } catch (e) {
+ const error = e as Error;
+ logAuthenticationError("<unknown>", error.message, ctx.req.ip);
+ throw e;
+ }
}),
});
diff --git a/packages/trpc/testUtils.ts b/packages/trpc/testUtils.ts
index 04e6b0a3..23dcdb33 100644
--- a/packages/trpc/testUtils.ts
+++ b/packages/trpc/testUtils.ts
@@ -37,6 +37,9 @@ export function getApiCaller(db: TestDB, userId?: string, email?: string) {
}
: null,
db,
+ req: {
+ ip: null,
+ },
});
}