diff --git a/.env.example b/.env.example index e8f63beb..84743f40 100644 --- a/.env.example +++ b/.env.example @@ -6,7 +6,7 @@ NODE_ENV=development # EdgeOps Console static embed (npm run build:console → dev/console/build) EDGEOPS_CONSOLE_PATH=dev/console/build # must be absolute path -EDGEOPS_CONSOLE_VERSION=v1.0.9 +EDGEOPS_CONSOLE_VERSION=v1.0.10 # EDGEOPS_CONSOLE_REPO=https://github.com/Datasance/edgeops-console # EDGEOPS_CONSOLE_FLAVOR=datasance diff --git a/.github/actions/set-build-env/action.yml b/.github/actions/set-build-env/action.yml index 9bb807a8..4b3d064a 100644 --- a/.github/actions/set-build-env/action.yml +++ b/.github/actions/set-build-env/action.yml @@ -8,7 +8,7 @@ runs: shell: bash run: | VERSION="${{ env.EDGEOPS_CONSOLE_VERSION }}" - if [ -z "$VERSION" ]; then VERSION="1.0.9"; fi + if [ -z "$VERSION" ]; then VERSION="1.0.10"; fi echo "EDGEOPS_CONSOLE_VERSION=$VERSION" >> "${GITHUB_ENV}" REPO="${{ env.EDGEOPS_CONSOLE_REPO }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 480ec2ed..2bf25882 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,30 @@ # Changelog +## [v3.8.1] - 2026-07-11 + +Patch release: EdgeOps Console refresh, console caching, RBAC and upload hardening, cluster-controller list filtering, and application-template deploy cleanup. + +### Added + +- **`GET /api/v3/cluster/controllers`** — optional query param **`includeInactive=true`** returns historical inactive replica rows; default list excludes inactive controllers. +- **`scripts/check-dockerfile-digests.sh`** — skopeo-based check that digest-pinned Dockerfile base images match current registry manifest lists (multi-arch aware). + +### Changed + +- Embedded **EdgeOps Console** default version **v1.0.9** → **v1.0.10** (Dockerfile, Makefile, CI build env, `.env.example`, `build-console-dev.js`). +- **`multer`** upgraded to **2.2.0**; multipart uploads capped at **1** file, **10** fields, **`fieldNestingDepth: 0`**. +- Dockerfile base image digest pins refreshed for **`node:24-bookworm`** and **`ubi9/nodejs-24-minimal`**. +- EdgeOps Console static serving — **`Cache-Control`** policy: **`no-cache`** for `index.html` and SPA fallbacks, **`no-store`** for `controller-config.js`, **`immutable`** long cache for hashed **`assets/`**, **24h** cache for **`branding/`**. + +### Fixed + +- **RBAC authorization pool pressure** — fresh RBAC cache hits serve cached allow/deny decisions without opening a DB transaction; full auth path runs only on cache miss or stale version. +- **Multipart upload DoS** — multer limits mitigate unbounded field nesting / file count abuse on file-upload routes. +- **Application template deploy** — template populate excludes **`created_at`** / **`updated_at`** so deploy-from-template does not leak template metadata into the generated application payload. + +--- + ## [v3.8.0] - 2026-07-03 Controller v3.8 is a **greenfield** release aligned with **Edgelet**. There is **no upgrade path** from v3.7: use a fresh database and redeploy Controller + Edgelet together. diff --git a/Dockerfile b/Dockerfile index d1b62197..9ecff7cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,10 @@ # ioFog overrides: EDGEOPS_CONSOLE_REPO=https://github.com/eclipse-iofog/edgeops-console # EDGEOPS_CONSOLE_FLAVOR=iofog # node:24-bookworm — pin manifest list digest for reproducible multi-arch builds -FROM node:24-bookworm@sha256:fdddfb3e688158251943d52eba361de991548f6814007acba4917ae6b512d6be AS console-builder +FROM node:24-bookworm@sha256:392e1e23f34da768d8d1f4e502b64f200d3be3465934d4b7930f57d7e2fc1989 AS console-builder ARG EDGEOPS_CONSOLE_REPO=https://github.com/Datasance/edgeops-console -ARG EDGEOPS_CONSOLE_VERSION=v1.0.9 +ARG EDGEOPS_CONSOLE_VERSION=v1.0.10 ARG EDGEOPS_CONSOLE_FLAVOR=datasance RUN apt-get update \ @@ -28,7 +28,7 @@ RUN test -f build/index.html \ && cp -a build /tmp/console/build -FROM node:24-bookworm@sha256:fdddfb3e688158251943d52eba361de991548f6814007acba4917ae6b512d6be AS builder +FROM node:24-bookworm@sha256:392e1e23f34da768d8d1f4e502b64f200d3be3465934d4b7930f57d7e2fc1989 AS builder ARG PKG_VERSION @@ -48,9 +48,9 @@ RUN npm pack # ubi9/nodejs-24-minimal:latest — pin manifest list digest for reproducible multi-arch builds -FROM registry.access.redhat.com/ubi9/nodejs-24-minimal@sha256:5f1ac8eab93c93eb2227f4ee7822668b312ee292d122dddd580bee8f17359c2f +FROM registry.access.redhat.com/ubi9/nodejs-24-minimal@sha256:c71bc3282e204a2b09143d2621236ebc83ef149af67ba2604ba43ad3ec21a5fb -ARG EDGEOPS_CONSOLE_VERSION=v1.0.9 +ARG EDGEOPS_CONSOLE_VERSION=v1.0.10 ARG IMAGE_REGISTRY ARG OCI_SOURCE_REPO ARG CONTROLLER_DISTRIBUTION=iofog diff --git a/Makefile b/Makefile index 025aaf29..033a0aef 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Local Docker build — mirrors CI/release build-args (see .github/actions/set-build-env). -# Override any variable: make build FLAVOR=iofog EDGEOPS_CONSOLE_VERSION=v1.0.9 +# Override any variable: make build FLAVOR=iofog EDGEOPS_CONSOLE_VERSION=v1.0.10 FLAVOR ?= datasance IMAGE_NAME ?= controller @@ -25,7 +25,7 @@ else $(error FLAVOR must be "datasance" or "iofog", got "$(FLAVOR)") endif -EDGEOPS_CONSOLE_VERSION ?= v1.0.9 +EDGEOPS_CONSOLE_VERSION ?= v1.0.10 IMAGE_REF = $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(DOCKER_TAG) diff --git a/package-lock.json b/package-lock.json index cfc74bf9..be026065 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "controller", - "version": "3.8.0", + "version": "3.8.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "controller", - "version": "3.8.0", + "version": "3.8.1", "hasInstallScript": true, "license": "EPL-2.0", "dependencies": { diff --git a/package.json b/package.json index 7515cb11..584788bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "controller", - "version": "3.8.0", + "version": "3.8.1", "description": "A cloud-native operations controller for managing edge computing workloads, edgelet nodes, and deployments across Eclipse IoFog and Datasance PoT.", "main": "./src/main.js", "author": "Eclipse ioFog Project", diff --git a/scripts/build-console-dev.js b/scripts/build-console-dev.js index 5367f7cd..5ec7abf3 100644 --- a/scripts/build-console-dev.js +++ b/scripts/build-console-dev.js @@ -9,7 +9,7 @@ const CONSOLE_DIR = path.join(DEV_DIR, 'console') const BUILD_OUT = path.join(CONSOLE_DIR, 'build') const REPO = process.env.EDGEOPS_CONSOLE_REPO || 'https://github.com/Datasance/edgeops-console' -const VERSION = process.env.EDGEOPS_CONSOLE_VERSION || 'v1.0.9' +const VERSION = process.env.EDGEOPS_CONSOLE_VERSION || 'v1.0.10' const FLAVOR = process.env.EDGEOPS_CONSOLE_FLAVOR || 'datasance' function normalizeTag (version) { diff --git a/src/server.js b/src/server.js index 074ca73a..faacde39 100755 --- a/src/server.js +++ b/src/server.js @@ -278,11 +278,33 @@ initialize().then(() => { const hasFileBasedTLS = tlsMaterial.enabled && !tlsMaterial.isBase64 const hasBase64TLS = tlsMaterial.enabled && tlsMaterial.isBase64 - consoleApp.use(express.static(consolePath, { index: 'index.html' })) + function setConsoleStaticCacheHeaders (res, filePath) { + if (filePath.endsWith('index.html')) { + res.setHeader('Cache-Control', 'no-cache') + return + } + if (filePath.endsWith('controller-config.js')) { + res.setHeader('Cache-Control', 'no-store') + return + } + if (filePath.includes(`${path.sep}assets${path.sep}`)) { + res.setHeader('Cache-Control', 'public, max-age=31536000, immutable') + return + } + if (filePath.includes(`${path.sep}branding${path.sep}`)) { + res.setHeader('Cache-Control', 'public, max-age=86400') + } + } + + consoleApp.use(express.static(consolePath, { + index: 'index.html', + setHeaders: setConsoleStaticCacheHeaders + })) consoleApp.get('*', (req, res, next) => { if (path.extname(req.path)) { return next() } + res.setHeader('Cache-Control', 'no-cache') res.sendFile(path.join(consolePath, 'index.html'), (error) => { if (error) { next(error) diff --git a/src/services/application-template-service.js b/src/services/application-template-service.js index 7353ca39..7b1e14b8 100644 --- a/src/services/application-template-service.js +++ b/src/services/application-template-service.js @@ -194,7 +194,8 @@ const getApplicationTemplateEndPoint = async function (name, isCLI, transaction) const getApplicationDataFromTemplate = async function (deploymentData, isCLI, transaction) { await Validator.validate(deploymentData, Validator.schemas.applicationTemplateDeploy) - const applicationTemplateDBObject = await ApplicationTemplateManager.findOnePopulated({ name: deploymentData.name }, transaction) + const attributes = { exclude: ['created_at', 'updated_at'] } + const applicationTemplateDBObject = await ApplicationTemplateManager.findOnePopulated({ name: deploymentData.name }, attributes, transaction) if (!applicationTemplateDBObject) { throw new Errors.NotFoundError(ErrorMessages.INVALID_APPLICATION_TEMPLATE_NAME, deploymentData.name) }