From f98a180dfb19b1ba3a89aa122d89dad43b6c503c Mon Sep 17 00:00:00 2001 From: Simon Merschjohann Date: Fri, 26 Apr 2024 19:39:52 +0000 Subject: [PATCH] support Authorization token protection in bw serve --- apps/cli/src/commands/serve.command.ts | 39 ++++++++++++++++++++++++++ apps/cli/src/program.ts | 4 +++ 2 files changed, 43 insertions(+) diff --git a/apps/cli/src/commands/serve.command.ts b/apps/cli/src/commands/serve.command.ts index 7a11dc4b4a..35a6e0fee9 100644 --- a/apps/cli/src/commands/serve.command.ts +++ b/apps/cli/src/commands/serve.command.ts @@ -32,6 +32,7 @@ import { GetCommand } from "./get.command"; import { ListCommand } from "./list.command"; import { RestoreCommand } from "./restore.command"; import { StatusCommand } from "./status.command"; +import { readFileSync, existsSync } from "fs"; export class ServeCommand { private listCommand: ListCommand; @@ -183,6 +184,29 @@ export class ServeCommand { process.env.BW_SERVE = "true"; process.env.BW_NOINTERACTION = "true"; + const authTokenEnabled = !options.disableAuth; + + var useToken = null; + if(options.authToken != null && options.authToken.length > 0) { + useToken = options.authToken; + } + if(options.authTokenFile != null && options.authTokenFile.length > 0) { + if(!existsSync(options.authTokenFile)) { + this.main.logService.error("Auth token file does not exist."); + return; + } + useToken = readFileSync(options.authTokenFile, 'utf8').trim(); + } + if(options.authTokenEnv != null && options.authTokenEnv.length > 0) { + useToken = process.env[options.authTokenEnv]; + } + + const authToken = useToken || process.env.BW_SERVE_AUTH_TOKEN; + if(authTokenEnabled && (authToken == null || authToken.length == 0)) { + this.main.logService.error("No auth token provided. Please deactivate auth explicitly or provide an auth token using --auth-token, --auth-token-file, --auth-token-env or BW_SERVE_AUTH_TOKEN environment variable."); + return; + } + server .use(async (ctx, next) => { if (protectOrigin && ctx.headers.origin != undefined) { @@ -198,6 +222,21 @@ export class ServeCommand { } await next(); }) + .use(async (ctx, next) => { + if(!authTokenEnabled) { + await next(); + return; + } + + if (ctx.request.headers.authorization == null || ctx.request.headers.authorization.indexOf("Bearer " + authToken) != 0) { + ctx.status = 403; + this.main.logService.warning( + `Blocking request from as token is invalid or missing.`, + ); + return; + } + await next(); + }) .use(koaBodyParser()) .use(koaJson({ pretty: false, param: "pretty" })); diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index 5d26b0850e..684185f7e3 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -479,6 +479,10 @@ export class Program { .description("Start a RESTful API webserver.") .option("--hostname ", "The hostname to bind your API webserver to.") .option("--port ", "The port to run your API webserver on.") + .option("--auth-token-env ", "The environment variable to use for the auth token. Defaults to BW_SERVE_AUTH_TOKEN if not set.") + .option("--auth-token-file ", "The file to use for the auth token.") + .option("--auth-token ", "The auth token to use for authentication.") + .option("--disable-auth", "If set, disables authentication.") .option( "--disable-origin-protection", "If set, allows requests with origin header. Warning, this option exists for backwards compatibility reasons and exposes your environment to known CSRF attacks.",