Rotatable secrets (#9982)

* Rotatable secrets

* Set new api encryption key var

* Lint

* Use fallback keys instead of array

* Point api encryption key to dedicated value

* Add API_ENCRYPTION_KEY to cli

* Lint + add api encryption key to env files
This commit is contained in:
Rory Powell 2023-03-13 15:02:59 +00:00 committed by GitHub
parent e74f80742e
commit e116941750
25 changed files with 132 additions and 84 deletions

View File

@ -62,16 +62,31 @@ spec:
{{ end }} {{ end }}
- name: ENABLE_ANALYTICS - name: ENABLE_ANALYTICS
value: {{ .Values.globals.enableAnalytics | quote }} value: {{ .Values.globals.enableAnalytics | quote }}
- name: API_ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: budibase-budibase {{ template "budibase.fullname" . }}
key: apiEncryptionKey
- name: INTERNAL_API_KEY - name: INTERNAL_API_KEY
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: {{ template "budibase.fullname" . }} name: {{ template "budibase.fullname" . }}
key: internalApiKey key: internalApiKey
- name: INTERNAL_API_KEY_FALLBACK
valueFrom:
secretKeyRef:
name: {{ template "budibase.fullname" . }}
key: internalApiKeyFallback
- name: JWT_SECRET - name: JWT_SECRET
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: {{ template "budibase.fullname" . }} name: {{ template "budibase.fullname" . }}
key: jwtSecret key: jwtSecret
- name: JWT_SECRET_FALLBACK
valueFrom:
secretKeyRef:
name: {{ template "budibase.fullname" . }}
key: jwtSecretFallback
{{ if .Values.services.objectStore.region }} {{ if .Values.services.objectStore.region }}
- name: AWS_REGION - name: AWS_REGION
value: {{ .Values.services.objectStore.region }} value: {{ .Values.services.objectStore.region }}

View File

@ -10,8 +10,14 @@ metadata:
heritage: "{{ .Release.Service }}" heritage: "{{ .Release.Service }}"
type: Opaque type: Opaque
data: data:
{{/* For new installations this can be any value. For existing installations this must match the first used jwtSecret */}}
apiEncryptionKey: {{ .Values.globals.apiEncryptionKey }}
internalApiKey: {{ template "budibase.defaultsecret" .Values.globals.internalApiKey }} internalApiKey: {{ template "budibase.defaultsecret" .Values.globals.internalApiKey }}
{{/* Fallback value auto generated */}}
internalApiKeyFallback: {{ .Values.globals.internalApiKeyFallback }}
jwtSecret: {{ template "budibase.defaultsecret" .Values.globals.jwtSecret }} jwtSecret: {{ template "budibase.defaultsecret" .Values.globals.jwtSecret }}
{{/* Falback value never auto generated */}}
jwtSecretFallback: {{ .Values.globals.jwtSecretFallback }}
objectStoreAccess: {{ template "budibase.defaultsecret" .Values.services.objectStore.accessKey }} objectStoreAccess: {{ template "budibase.defaultsecret" .Values.services.objectStore.accessKey }}
objectStoreSecret: {{ template "budibase.defaultsecret" .Values.services.objectStore.secretKey }} objectStoreSecret: {{ template "budibase.defaultsecret" .Values.services.objectStore.secretKey }}
{{- end -}} {{- end -}}

View File

@ -62,16 +62,31 @@ spec:
{{ else }} {{ else }}
value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }} value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }}
{{ end }} {{ end }}
- name: API_ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: budibase-budibase {{ template "budibase.fullname" . }}
key: apiEncryptionKey
- name: INTERNAL_API_KEY - name: INTERNAL_API_KEY
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: {{ template "budibase.fullname" . }} name: {{ template "budibase.fullname" . }}
key: internalApiKey key: internalApiKey
- name: INTERNAL_API_KEY_FALLBACK
valueFrom:
secretKeyRef:
name: {{ template "budibase.fullname" . }}
key: internalApiKeyFallback
- name: JWT_SECRET - name: JWT_SECRET
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: {{ template "budibase.fullname" . }} name: {{ template "budibase.fullname" . }}
key: jwtSecret key: jwtSecret
- name: JWT_SECRET_FALLBACK
valueFrom:
secretKeyRef:
name: {{ template "budibase.fullname" . }}
key: jwtSecretFallback
{{ if .Values.services.objectStore.region }} {{ if .Values.services.objectStore.region }}
- name: AWS_REGION - name: AWS_REGION
value: {{ .Values.services.objectStore.region }} value: {{ .Values.services.objectStore.region }}

View File

@ -96,9 +96,13 @@ globals:
createSecrets: true # creates an internal API key, JWT secrets and redis password for you createSecrets: true # creates an internal API key, JWT secrets and redis password for you
# if createSecrets is set to false, you can hard-code your secrets here # if createSecrets is set to false, you can hard-code your secrets here
apiEncryptionKey: ""
internalApiKey: "" internalApiKey: ""
jwtSecret: "" jwtSecret: ""
cdnUrl: "" cdnUrl: ""
# fallback values used during live rotation
internalApiKeyFallback: ""
jwtSecretFallback: ""
smtp: smtp:
enabled: false enabled: false

View File

@ -3,6 +3,7 @@ MAIN_PORT=10000
# This section contains all secrets pertaining to the system # This section contains all secrets pertaining to the system
# These should be updated # These should be updated
API_ENCRYPTION_KEY=testsecret
JWT_SECRET=testsecret JWT_SECRET=testsecret
MINIO_ACCESS_KEY=budibase MINIO_ACCESS_KEY=budibase
MINIO_SECRET_KEY=budibase MINIO_SECRET_KEY=budibase

View File

@ -17,6 +17,7 @@ services:
INTERNAL_API_KEY: ${INTERNAL_API_KEY} INTERNAL_API_KEY: ${INTERNAL_API_KEY}
BUDIBASE_ENVIRONMENT: ${BUDIBASE_ENVIRONMENT} BUDIBASE_ENVIRONMENT: ${BUDIBASE_ENVIRONMENT}
PORT: 4002 PORT: 4002
API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY}
JWT_SECRET: ${JWT_SECRET} JWT_SECRET: ${JWT_SECRET}
LOG_LEVEL: info LOG_LEVEL: info
SENTRY_DSN: https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131 SENTRY_DSN: https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131
@ -40,6 +41,7 @@ services:
SELF_HOSTED: 1 SELF_HOSTED: 1
PORT: 4003 PORT: 4003
CLUSTER_PORT: ${MAIN_PORT} CLUSTER_PORT: ${MAIN_PORT}
API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY}
JWT_SECRET: ${JWT_SECRET} JWT_SECRET: ${JWT_SECRET}
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY} MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}

View File

@ -3,6 +3,7 @@ MAIN_PORT=10000
# This section contains all secrets pertaining to the system # This section contains all secrets pertaining to the system
# These should be updated # These should be updated
API_ENCRYPTION_KEY=testsecret
JWT_SECRET=testsecret JWT_SECRET=testsecret
MINIO_ACCESS_KEY=budibase MINIO_ACCESS_KEY=budibase
MINIO_SECRET_KEY=budibase MINIO_SECRET_KEY=budibase

View File

@ -1,6 +1,5 @@
const _passport = require("koa-passport") const _passport = require("koa-passport")
const LocalStrategy = require("passport-local").Strategy const LocalStrategy = require("passport-local").Strategy
const JwtStrategy = require("passport-jwt").Strategy
import { getGlobalDB } from "../context" import { getGlobalDB } from "../context"
import { Cookie } from "../constants" import { Cookie } from "../constants"
import { getSessionsForUser, invalidateSessions } from "../security/sessions" import { getSessionsForUser, invalidateSessions } from "../security/sessions"
@ -8,7 +7,6 @@ import {
authenticated, authenticated,
csrf, csrf,
google, google,
jwt as jwtPassport,
local, local,
oidc, oidc,
tenancy, tenancy,
@ -21,14 +19,11 @@ import {
OIDCInnerConfig, OIDCInnerConfig,
PlatformLogoutOpts, PlatformLogoutOpts,
SSOProviderType, SSOProviderType,
User,
} from "@budibase/types" } from "@budibase/types"
import { logAlert } from "../logging"
import * as events from "../events" import * as events from "../events"
import * as configs from "../configs" import * as configs from "../configs"
import { clearCookie, getCookie } from "../utils" import { clearCookie, getCookie } from "../utils"
import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso" import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso"
import env from "../environment"
const refresh = require("passport-oauth2-refresh") const refresh = require("passport-oauth2-refresh")
export { export {
@ -51,25 +46,6 @@ export const jwt = require("jsonwebtoken")
// Strategies // Strategies
_passport.use(new LocalStrategy(local.options, local.authenticate)) _passport.use(new LocalStrategy(local.options, local.authenticate))
if (jwtPassport.options.secretOrKey) {
_passport.use(new JwtStrategy(jwtPassport.options, jwtPassport.authenticate))
} else if (!env.DISABLE_JWT_WARNING) {
logAlert("No JWT Secret supplied, cannot configure JWT strategy")
}
_passport.serializeUser((user: User, done: any) => done(null, user))
_passport.deserializeUser(async (user: User, done: any) => {
const db = getGlobalDB()
try {
const dbUser = await db.get(user._id)
return done(null, dbUser)
} catch (err) {
console.error(`User not found`, err)
return done(null, false, { message: "User not found" })
}
})
async function refreshOIDCAccessToken( async function refreshOIDCAccessToken(
chosenConfig: OIDCInnerConfig, chosenConfig: OIDCInnerConfig,

View File

@ -30,6 +30,12 @@ const DefaultBucketName = {
const selfHosted = !!parseInt(process.env.SELF_HOSTED || "") const selfHosted = !!parseInt(process.env.SELF_HOSTED || "")
function getAPIEncryptionKey() {
return process.env.API_ENCRYPTION_KEY
? process.env.API_ENCRYPTION_KEY
: process.env.JWT_SECRET // fallback to the JWT_SECRET used historically
}
const environment = { const environment = {
isTest, isTest,
isJest, isJest,
@ -39,7 +45,9 @@ const environment = {
}, },
JS_BCRYPT: process.env.JS_BCRYPT, JS_BCRYPT: process.env.JS_BCRYPT,
JWT_SECRET: process.env.JWT_SECRET, JWT_SECRET: process.env.JWT_SECRET,
JWT_SECRET_FALLBACK: process.env.JWT_SECRET_FALLBACK,
ENCRYPTION_KEY: process.env.ENCRYPTION_KEY, ENCRYPTION_KEY: process.env.ENCRYPTION_KEY,
API_ENCRYPTION_KEY: getAPIEncryptionKey(),
COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005", COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005",
COUCH_DB_USERNAME: process.env.COUCH_DB_USER, COUCH_DB_USERNAME: process.env.COUCH_DB_USER,
COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD,
@ -55,6 +63,7 @@ const environment = {
MINIO_URL: process.env.MINIO_URL, MINIO_URL: process.env.MINIO_URL,
MINIO_ENABLED: process.env.MINIO_ENABLED || 1, MINIO_ENABLED: process.env.MINIO_ENABLED || 1,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
INTERNAL_API_KEY_FALLBACK: process.env.INTERNAL_API_KEY_FALLBACK,
MULTI_TENANCY: process.env.MULTI_TENANCY, MULTI_TENANCY: process.env.MULTI_TENANCY,
ACCOUNT_PORTAL_URL: ACCOUNT_PORTAL_URL:
process.env.ACCOUNT_PORTAL_URL || "https://account.budibase.app", process.env.ACCOUNT_PORTAL_URL || "https://account.budibase.app",

View File

@ -1,5 +1,10 @@
import { Cookie, Header } from "../constants" import { Cookie, Header } from "../constants"
import { getCookie, clearCookie, openJwt } from "../utils" import {
getCookie,
clearCookie,
openJwt,
isValidInternalAPIKey,
} from "../utils"
import { getUser } from "../cache/user" import { getUser } from "../cache/user"
import { getSession, updateSessionTTL } from "../security/sessions" import { getSession, updateSessionTTL } from "../security/sessions"
import { buildMatcherRegex, matches } from "./matchers" import { buildMatcherRegex, matches } from "./matchers"
@ -35,7 +40,9 @@ function finalise(ctx: any, opts: FinaliseOpts = {}) {
} }
async function checkApiKey(apiKey: string, populateUser?: Function) { async function checkApiKey(apiKey: string, populateUser?: Function) {
if (apiKey === env.INTERNAL_API_KEY) { // check both the primary and the fallback internal api keys
// this allows for rotation
if (isValidInternalAPIKey(apiKey)) {
return { valid: true } return { valid: true }
} }
const decrypted = decrypt(apiKey) const decrypted = decrypt(apiKey)

View File

@ -1,4 +1,3 @@
export * as jwt from "./passport/jwt"
export * as local from "./passport/local" export * as local from "./passport/local"
export * as google from "./passport/sso/google" export * as google from "./passport/sso/google"
export * as oidc from "./passport/sso/oidc" export * as oidc from "./passport/sso/oidc"

View File

@ -1,13 +1,21 @@
import env from "../environment"
import { Header } from "../constants" import { Header } from "../constants"
import { BBContext } from "@budibase/types" import { BBContext } from "@budibase/types"
import { isValidInternalAPIKey } from "../utils"
/** /**
* API Key only endpoint. * API Key only endpoint.
*/ */
export default async (ctx: BBContext, next: any) => { export default async (ctx: BBContext, next: any) => {
const apiKey = ctx.request.headers[Header.API_KEY] const apiKey = ctx.request.headers[Header.API_KEY]
if (apiKey !== env.INTERNAL_API_KEY) { if (!apiKey) {
ctx.throw(403, "Unauthorized")
}
if (Array.isArray(apiKey)) {
ctx.throw(403, "Unauthorized")
}
if (!isValidInternalAPIKey(apiKey)) {
ctx.throw(403, "Unauthorized") ctx.throw(403, "Unauthorized")
} }

View File

@ -1,19 +0,0 @@
import { Cookie } from "../../constants"
import env from "../../environment"
import { authError } from "./utils"
import { BBContext } from "@budibase/types"
export const options = {
secretOrKey: env.JWT_SECRET,
jwtFromRequest: function (ctx: BBContext) {
return ctx.cookies.get(Cookie.Auth)
},
}
export async function authenticate(jwt: Function, done: Function) {
try {
return done(null, jwt)
} catch (err) {
return authError(done, "JWT invalid", err)
}
}

View File

@ -8,7 +8,7 @@ const RANDOM_BYTES = 16
const STRETCH_LENGTH = 32 const STRETCH_LENGTH = 32
export enum SecretOption { export enum SecretOption {
JWT = "jwt", API = "api",
ENCRYPTION = "encryption", ENCRYPTION = "encryption",
} }
@ -19,10 +19,10 @@ function getSecret(secretOption: SecretOption): string {
secret = env.ENCRYPTION_KEY secret = env.ENCRYPTION_KEY
secretName = "ENCRYPTION_KEY" secretName = "ENCRYPTION_KEY"
break break
case SecretOption.JWT: case SecretOption.API:
default: default:
secret = env.JWT_SECRET secret = env.API_ENCRYPTION_KEY
secretName = "JWT_SECRET" secretName = "API_ENCRYPTION_KEY"
break break
} }
if (!secret) { if (!secret) {
@ -37,7 +37,7 @@ function stretchString(string: string, salt: Buffer) {
export function encrypt( export function encrypt(
input: string, input: string,
secretOption: SecretOption = SecretOption.JWT secretOption: SecretOption = SecretOption.API
) { ) {
const salt = crypto.randomBytes(RANDOM_BYTES) const salt = crypto.randomBytes(RANDOM_BYTES)
const stretched = stretchString(getSecret(secretOption), salt) const stretched = stretchString(getSecret(secretOption), salt)
@ -50,7 +50,7 @@ export function encrypt(
export function decrypt( export function decrypt(
input: string, input: string,
secretOption: SecretOption = SecretOption.JWT secretOption: SecretOption = SecretOption.API
) { ) {
const [salt, encrypted] = input.split(SEPARATOR) const [salt, encrypted] = input.split(SEPARATOR)
const saltBuffer = Buffer.from(salt, "hex") const saltBuffer = Buffer.from(salt, "hex")

View File

@ -1,5 +1,4 @@
import { getAllApps, queryGlobalView } from "../db" import { getAllApps, queryGlobalView } from "../db"
import { options } from "../middleware/passport/jwt"
import { import {
Header, Header,
MAX_VALID_DATE, MAX_VALID_DATE,
@ -133,7 +132,30 @@ export function openJwt(token: string) {
if (!token) { if (!token) {
return token return token
} }
return jwt.verify(token, options.secretOrKey) try {
return jwt.verify(token, env.JWT_SECRET)
} catch (e) {
if (env.JWT_SECRET_FALLBACK) {
// fallback to enable rotation
return jwt.verify(token, env.JWT_SECRET_FALLBACK)
} else {
throw e
}
}
}
export function isValidInternalAPIKey(apiKey: string) {
if (env.INTERNAL_API_KEY && env.INTERNAL_API_KEY === apiKey) {
return true
}
// fallback to enable rotation
if (
env.INTERNAL_API_KEY_FALLBACK &&
env.INTERNAL_API_KEY_FALLBACK === apiKey
) {
return true
}
return false
} }
/** /**
@ -165,7 +187,7 @@ export function setCookie(
opts = { sign: true } opts = { sign: true }
) { ) {
if (value && opts && opts.sign) { if (value && opts && opts.sign) {
value = jwt.sign(value, options.secretOrKey) value = jwt.sign(value, env.JWT_SECRET)
} }
const config: SetOption = { const config: SetOption = {

View File

@ -13,6 +13,7 @@ export const ENV_PATH = path.resolve("./.env")
function getSecrets(opts = { single: false }) { function getSecrets(opts = { single: false }) {
const secrets = [ const secrets = [
"API_ENCRYPTION_KEY",
"JWT_SECRET", "JWT_SECRET",
"MINIO_ACCESS_KEY", "MINIO_ACCESS_KEY",
"MINIO_SECRET_KEY", "MINIO_SECRET_KEY",

View File

@ -29,13 +29,6 @@ router
br: false, br: false,
}) })
) )
.use(async (ctx, next) => {
ctx.config = {
jwtSecret: env.JWT_SECRET,
useAppRootPath: true,
}
await next()
})
// re-direct before any middlewares occur // re-direct before any middlewares occur
.redirect("/", "/builder") .redirect("/", "/builder")
.use( .use(

View File

@ -39,7 +39,6 @@ let inThread = false
const environment = { const environment = {
// important - prefer app port to generic port // important - prefer app port to generic port
PORT: process.env.APP_PORT || process.env.PORT, PORT: process.env.APP_PORT || process.env.PORT,
JWT_SECRET: process.env.JWT_SECRET,
COUCH_DB_URL: process.env.COUCH_DB_URL, COUCH_DB_URL: process.env.COUCH_DB_URL,
MINIO_URL: process.env.MINIO_URL, MINIO_URL: process.env.MINIO_URL,
WORKER_URL: process.env.WORKER_URL, WORKER_URL: process.env.WORKER_URL,
@ -48,7 +47,6 @@ const environment = {
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
REDIS_URL: process.env.REDIS_URL, REDIS_URL: process.env.REDIS_URL,
REDIS_PASSWORD: process.env.REDIS_PASSWORD, REDIS_PASSWORD: process.env.REDIS_PASSWORD,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
HTTP_MIGRATIONS: process.env.HTTP_MIGRATIONS, HTTP_MIGRATIONS: process.env.HTTP_MIGRATIONS,
API_REQ_LIMIT_PER_SEC: process.env.API_REQ_LIMIT_PER_SEC, API_REQ_LIMIT_PER_SEC: process.env.API_REQ_LIMIT_PER_SEC,
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,

View File

@ -205,7 +205,6 @@ class TestConfiguration {
request.appId = appId request.appId = appId
// fake cookies, we don't need them // fake cookies, we don't need them
request.cookies = { set: () => {}, get: () => {} } request.cookies = { set: () => {}, get: () => {} }
request.config = { jwtSecret: env.JWT_SECRET }
request.user = { appId, tenantId: this.getTenantId() } request.user = { appId, tenantId: this.getTenantId() }
request.query = {} request.query = {}
request.request = { request.request = {
@ -332,8 +331,8 @@ class TestConfiguration {
roleId: roleId, roleId: roleId,
appId, appId,
} }
const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET)
const appToken = auth.jwt.sign(app, env.JWT_SECRET) const appToken = auth.jwt.sign(app, coreEnv.JWT_SECRET)
// returning necessary request headers // returning necessary request headers
await cache.user.invalidateUser(userId) await cache.user.invalidateUser(userId)
@ -361,8 +360,8 @@ class TestConfiguration {
roleId: roles.BUILTIN_ROLE_IDS.ADMIN, roleId: roles.BUILTIN_ROLE_IDS.ADMIN,
appId: this.appId, appId: this.appId,
} }
const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET)
const appToken = auth.jwt.sign(app, env.JWT_SECRET) const appToken = auth.jwt.sign(app, coreEnv.JWT_SECRET)
const headers: any = { const headers: any = {
Accept: "application/json", Accept: "application/json",
Cookie: [ Cookie: [

View File

@ -6,6 +6,7 @@ import {
constants, constants,
tenancy, tenancy,
logging, logging,
env as coreEnv,
} from "@budibase/backend-core" } from "@budibase/backend-core"
import { updateAppRole } from "./global" import { updateAppRole } from "./global"
import { BBContext, User } from "@budibase/types" import { BBContext, User } from "@budibase/types"
@ -15,7 +16,7 @@ export function request(ctx?: BBContext, request?: any) {
request.headers = {} request.headers = {}
} }
if (!ctx) { if (!ctx) {
request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY request.headers[constants.Header.API_KEY] = coreEnv.INTERNAL_API_KEY
if (tenancy.isTenantIdSet()) { if (tenancy.isTenantIdSet()) {
request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId() request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId()
} }

View File

@ -30,10 +30,8 @@ const environment = {
// auth // auth
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
JWT_SECRET: process.env.JWT_SECRET,
SALT_ROUNDS: process.env.SALT_ROUNDS, SALT_ROUNDS: process.env.SALT_ROUNDS,
REDIS_PASSWORD: process.env.REDIS_PASSWORD, REDIS_PASSWORD: process.env.REDIS_PASSWORD,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN, COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
// urls // urls
MINIO_URL: process.env.MINIO_URL, MINIO_URL: process.env.MINIO_URL,

View File

@ -1,5 +1,5 @@
import env from "../environment" import env from "../environment"
import { constants } from "@budibase/backend-core" import { constants, utils } from "@budibase/backend-core"
import { BBContext } from "@budibase/types" import { BBContext } from "@budibase/types"
/** /**
@ -9,7 +9,15 @@ import { BBContext } from "@budibase/types"
export default async (ctx: BBContext, next: any) => { export default async (ctx: BBContext, next: any) => {
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
const apiKey = ctx.request.headers[constants.Header.API_KEY] const apiKey = ctx.request.headers[constants.Header.API_KEY]
if (apiKey !== env.INTERNAL_API_KEY) { if (!apiKey) {
ctx.throw(403, "Unauthorized")
}
if (Array.isArray(apiKey)) {
ctx.throw(403, "Unauthorized")
}
if (!utils.isValidInternalAPIKey(apiKey)) {
ctx.throw(403, "Unauthorized") ctx.throw(403, "Unauthorized")
} }
} }

View File

@ -5,10 +5,10 @@ import {
sessions, sessions,
events, events,
HTTPError, HTTPError,
env as coreEnv,
} from "@budibase/backend-core" } from "@budibase/backend-core"
import { PlatformLogoutOpts, User } from "@budibase/types" import { PlatformLogoutOpts, User } from "@budibase/types"
import jwt from "jsonwebtoken" import jwt from "jsonwebtoken"
import env from "../../environment"
import * as userSdk from "../users" import * as userSdk from "../users"
import * as emails from "../../utilities/email" import * as emails from "../../utilities/email"
import * as redis from "../../utilities/redis" import * as redis from "../../utilities/redis"
@ -26,7 +26,7 @@ export async function loginUser(user: User) {
sessionId, sessionId,
tenantId, tenantId,
}, },
env.JWT_SECRET! coreEnv.JWT_SECRET!
) )
return token return token
} }

View File

@ -74,7 +74,6 @@ class TestConfiguration {
const request: any = {} const request: any = {}
// fake cookies, we don't need them // fake cookies, we don't need them
request.cookies = { set: () => {}, get: () => {} } request.cookies = { set: () => {}, get: () => {} }
request.config = { jwtSecret: env.JWT_SECRET }
request.user = { tenantId: this.getTenantId() } request.user = { tenantId: this.getTenantId() }
request.query = {} request.query = {}
request.request = { request.request = {
@ -180,7 +179,7 @@ class TestConfiguration {
sessionId: "sessionid", sessionId: "sessionid",
tenantId: user.tenantId, tenantId: user.tenantId,
} }
const authCookie = auth.jwt.sign(authToken, env.JWT_SECRET) const authCookie = auth.jwt.sign(authToken, coreEnv.JWT_SECRET)
return { return {
Accept: "application/json", Accept: "application/json",
...this.cookieHeader([`${constants.Cookie.Auth}=${authCookie}`]), ...this.cookieHeader([`${constants.Cookie.Auth}=${authCookie}`]),
@ -197,7 +196,7 @@ class TestConfiguration {
} }
internalAPIHeaders() { internalAPIHeaders() {
return { [constants.Header.API_KEY]: env.INTERNAL_API_KEY } return { [constants.Header.API_KEY]: coreEnv.INTERNAL_API_KEY }
} }
adminOnlyResponse = () => { adminOnlyResponse = () => {
@ -277,7 +276,7 @@ class TestConfiguration {
// CONFIGS - OIDC // CONFIGS - OIDC
getOIDConfigCookie(configId: string) { getOIDConfigCookie(configId: string) {
const token = auth.jwt.sign(configId, env.JWT_SECRET) const token = auth.jwt.sign(configId, coreEnv.JWT_SECRET)
return this.cookieHeader([[`${constants.Cookie.OIDC_CONFIG}=${token}`]]) return this.cookieHeader([[`${constants.Cookie.OIDC_CONFIG}=${token}`]])
} }

View File

@ -1,5 +1,10 @@
import fetch from "node-fetch" import fetch from "node-fetch"
import { constants, tenancy, logging } from "@budibase/backend-core" import {
constants,
tenancy,
logging,
env as coreEnv,
} from "@budibase/backend-core"
import { checkSlashesInUrl } from "../utilities" import { checkSlashesInUrl } from "../utilities"
import env from "../environment" import env from "../environment"
import { SyncUserRequest, User } from "@budibase/types" import { SyncUserRequest, User } from "@budibase/types"
@ -9,7 +14,7 @@ async function makeAppRequest(url: string, method: string, body: any) {
return return
} }
const request: any = { headers: {} } const request: any = { headers: {} }
request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY request.headers[constants.Header.API_KEY] = coreEnv.INTERNAL_API_KEY
if (tenancy.isTenantIdSet()) { if (tenancy.isTenantIdSet()) {
request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId() request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId()
} }