From 93b6cd7e90b3ba3d353f9bbfa0022e0ea2f28419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agn=C3=A8s=20Toulet?= <35176601+AgnesToulet@users.noreply.github.com> Date: Fri, 14 Apr 2023 11:07:49 +0200 Subject: [PATCH 1/2] Security: can set array of auth tokens --- scripts/clean_target.sh | 18 ++++++++++++++++++ src/app.ts | 3 ++- src/config.ts | 11 ++++++++++- src/plugin/v2/grpc_plugin.ts | 16 +++++++--------- src/service/middlewares.ts | 6 ++---- 5 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 scripts/clean_target.sh diff --git a/scripts/clean_target.sh b/scripts/clean_target.sh new file mode 100644 index 00000000..01cc241b --- /dev/null +++ b/scripts/clean_target.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +ARCH="${1:-}" +OUT="${2:-}" + +if [ -z "$ARCH" ]; then + echo "ARCH (arg 1) has to be set" + exit 1 +fi + +PLUGIN_NAME=plugin-${ARCH} + +if [ ! -z "$OUT" ]; then + PLUGIN_NAME=${OUT} +fi + +rm -rf .dist/${PLUGIN_NAME} +rm -f ./artifacts/${PLUGIN_NAME}.zip \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 16ec3788..beefff8c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -109,7 +109,8 @@ function populateServiceConfigFromEnv(config: ServiceConfig, env: NodeJS.Process } if (env['AUTH_TOKEN']) { - config.service.security.authToken = env['AUTH_TOKEN']; + const authToken = env['AUTH_TOKEN'] as string; + config.service.security.authToken = authToken.includes(' ') ? authToken.split(' ') : authToken; } if (env['LOG_LEVEL']) { diff --git a/src/config.ts b/src/config.ts index 50b26320..e352064b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -56,7 +56,7 @@ export interface LoggingConfig { } export interface SecurityConfig { - authToken: string; + authToken: string | string[]; } export interface ServiceConfig { @@ -148,3 +148,12 @@ export const readJSONFileSync = (filePath: string): any => { const rawdata = fs.readFileSync(filePath, 'utf8'); return JSON.parse(rawdata); }; + +export const isAuthTokenValid = (config: SecurityConfig, reqAuthToken: string): boolean => { + let configToken = config.authToken || ['']; + if (typeof configToken == "string") { + configToken = [configToken] + } + + return reqAuthToken !== "" && configToken.includes(reqAuthToken) +} diff --git a/src/plugin/v2/grpc_plugin.ts b/src/plugin/v2/grpc_plugin.ts index 80223849..f972731e 100644 --- a/src/plugin/v2/grpc_plugin.ts +++ b/src/plugin/v2/grpc_plugin.ts @@ -3,7 +3,7 @@ import * as protoLoader from '@grpc/proto-loader'; import * as promClient from 'prom-client'; import { GrpcPlugin } from '../../node-plugin'; import { Logger } from '../../logger'; -import { PluginConfig, SecurityConfig } from '../../config'; +import { PluginConfig, SecurityConfig, isAuthTokenValid } from '../../config'; import { createBrowser, Browser } from '../../browser'; import { HTTPHeaders, ImageRenderOptions, RenderOptions } from '../../types'; import { @@ -93,7 +93,7 @@ export class RenderGRPCPluginV2 implements GrpcPlugin { class PluginGRPCServer { private browserVersion: string | undefined; - constructor(private browser: Browser, private log: Logger, private sanitizer: Sanitizer, private securityCfg: SecurityConfig) {} + constructor(private browser: Browser, private log: Logger, private sanitizer: Sanitizer, private securityCfg: SecurityConfig) { } async start(browserVersion?: string) { this.browserVersion = browserVersion; @@ -108,8 +108,7 @@ class PluginGRPCServer { return callback({ code: Status.INVALID_ARGUMENT, details: 'Request cannot be null' }); } - const configToken = this.securityCfg.authToken || ''; - if (!req.authToken || req.authToken !== configToken) { + if (!isAuthTokenValid(this.securityCfg, req.authToken)) { return callback({ code: Status.UNAUTHENTICATED, details: 'Unauthorized request' }); } @@ -158,8 +157,7 @@ class PluginGRPCServer { return callback({ code: Status.INVALID_ARGUMENT, details: 'Request cannot be null' }); } - const configToken = this.securityCfg.authToken || ''; - if (!req.authToken || req.authToken !== configToken) { + if (!isAuthTokenValid(this.securityCfg, req.authToken)) { return callback({ code: Status.UNAUTHENTICATED, details: 'Unauthorized request' }); } @@ -217,8 +215,7 @@ class PluginGRPCServer { async sanitize(call: grpc.ServerUnaryCall, callback: grpc.sendUnaryData) { const grpcReq = call.request; - const configToken = this.securityCfg.authToken || ''; - if (!grpcReq.authToken || grpcReq.authToken !== configToken) { + if (!isAuthTokenValid(this.securityCfg, grpcReq.authToken)) { return callback({ code: Status.UNAUTHENTICATED, details: 'Unauthorized request' }); } @@ -324,7 +321,8 @@ const populateConfigFromEnv = (config: PluginConfig) => { } if (env['GF_PLUGIN_AUTH_TOKEN']) { - config.plugin.security.authToken = env['GF_PLUGIN_AUTH_TOKEN']; + const authToken = env['GF_PLUGIN_AUTH_TOKEN'] as string; + config.plugin.security.authToken = authToken.includes(' ') ? authToken.split(' ') : authToken; } }; diff --git a/src/service/middlewares.ts b/src/service/middlewares.ts index ace22a33..563919d6 100644 --- a/src/service/middlewares.ts +++ b/src/service/middlewares.ts @@ -1,7 +1,7 @@ import express = require('express'); import * as boom from '@hapi/boom'; import { ImageRenderOptions } from '../types'; -import { SecurityConfig } from '../config'; +import { SecurityConfig, isAuthTokenValid } from '../config'; export const asyncMiddleware = (fn) => (req, res, next) => { Promise.resolve(fn(req, res, next)).catch((err) => { @@ -28,10 +28,8 @@ export const trustedUrlMiddleware = ( export const authTokenMiddleware = (config: SecurityConfig) => { return (req: express.Request, res: express.Response, next: express.NextFunction) => { - const cfgToken = config.authToken || ''; const headerToken = req.header('X-Auth-Token'); - - if (headerToken === undefined || headerToken !== cfgToken) { + if (headerToken === undefined || !isAuthTokenValid(config, headerToken)) { return next(boom.unauthorized('Unauthorized request')); } From 5c6752fa236969627d6ddc26db4fef55fe220789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agn=C3=A8s=20Toulet?= <35176601+AgnesToulet@users.noreply.github.com> Date: Mon, 17 Apr 2023 09:46:49 +0200 Subject: [PATCH 2/2] Update src/config.ts Co-authored-by: Selene --- src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index e352064b..8c26b7a6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -151,7 +151,7 @@ export const readJSONFileSync = (filePath: string): any => { export const isAuthTokenValid = (config: SecurityConfig, reqAuthToken: string): boolean => { let configToken = config.authToken || ['']; - if (typeof configToken == "string") { + if (typeof configToken === "string") { configToken = [configToken] }