diff --git a/Studio/README.md b/Studio/README.md index 4eb40cf..34cc054 100644 --- a/Studio/README.md +++ b/Studio/README.md @@ -39,7 +39,10 @@ The guide is designed to be understandable even if you've never used Docker befo This repository contains files and instructions for running the [Hackolade Studio](https://hackolade.com) data modeling application, using the base image published on [Docker Hub](https://hub.docker.com/r/hackolade/studio): - [Dockerfile](Dockerfile): ready-to-use example of a full installation of Hackolade Studio, including the possibility to install selected target plugins -- [docker-compose.yml](docker-compose.yml): example of the recommended way to configure the launch of containers of the Hackolade CLI. +- [docker-compose.yml](docker-compose.yml): example for **custom-built** `hackolade/studio` images (traditional paths) +- [compose.yml](compose.yml): simple example for the pre-built **`hackolade/hck-cli`** image +- [compose.hardened.yml](compose.hardened.yml): hardened `hck-cli` example (read-only rootfs, `/data` + `/tmp` tmpfs) +- [k8s/](k8s/): Kubernetes Job examples with PVC at `/data` and memory emptyDir at `/tmp` - [securityPolicies.json](securityPolicies.json) - [optional] the list of required system call operations to be able to run Hackolade with Chrome sandboxing (disabled by default) inside a container ([more details](https://docs.docker.com/engine/security/seccomp/)) - batch files examples when running on Windows: - [docker-help.bat](docker-help.bat): verify the proper running of the CLI by displaying the CLI help in a container. Will work without a validated license key. @@ -80,12 +83,29 @@ The **hackolade** user pre-configured inside the image has the following UID/GID ### Required directories (inside containers) -Hackolade reads and writes data to the following folders inside containers: +#### Pre-built `hackolade/hck-cli` image (recommended) --**/home/hackolade/.config/Hackolade**: this folder (**appData**) is necessary for the proper operation of the application in containers and must be readable and writable by **hackolade** user. --**/home/hackolade/Documents/HackoladeLogs**: this folder is where Hackolade Studio writes its logging information, which is useful in case of any issue and must be readable and writable by **hackolade** user. -- **/home/hackolade/Documents/data**: we recommend this folder that will be used for generated artifacts within containers but you can define it anywhere inside container filesystem as long as you make it persistent. It may contain models, documentation, sources for reverse-engineering, artifacts out of forward-engineering, etc... Instead of a relative path to the location where the container is run, you may reference an absolute path to the location of these files. --[Optional]**/home/hackolade/.hackolade/options**: is where Hackolade reads user defined configurations like naming conventions, excel export options, custom properties, etc... +The image expects a **read-only root filesystem** with exactly two writable mounts: + +- **`/data`** (persistent volume): license/userData under `/data/app`, plus logs, models, output, settings, and options +- **`/tmp`** (tmpfs): sockets, caches, and scratch files + +See [`doc/getting-started-hck-cli.md`](./doc/getting-started-hck-cli.md) and [`compose.yml`](./compose.yml). Custom CAs use read-only PEM mounts and `NODE_EXTRA_CA_CERTS` / `SSL_CERT_FILE` — see [`doc/custom-certificates.md`](./doc/custom-certificates.md). + +For hardened deployments, see [`compose.hardened.yml`](./compose.hardened.yml) and [`k8s/`](./k8s/). + +**Breaking change:** do not mount `/home/hackolade/.config` for the pre-built image; that path is no longer used for license state. + +#### Custom-built `hackolade/studio` images + +Older custom builds may still use the historical layout: + +- `/home/hackolade/.config/Hackolade`: application data (**appData**) — must be readable and writable by the container user +- `/home/hackolade/Documents/HackoladeLogs`: logging information +- `/home/hackolade/Documents/data`: generated artifacts (models, documentation, RE/FE outputs, etc.) +- [Optional] `/home/hackolade/.hackolade/options`: user-defined configurations + +Prefer migrating custom images to the `/data` + `/tmp` model used by `hackolade/hck-cli`. You must create manually the folders you will bind mount prior to running hackolade studio containers because docker doesn't create them automatically anymore. diff --git a/Studio/doc/getting-started-hck-cli.md b/Studio/doc/getting-started-hck-cli.md index 4f08835..b6de9c7 100644 --- a/Studio/doc/getting-started-hck-cli.md +++ b/Studio/doc/getting-started-hck-cli.md @@ -95,50 +95,71 @@ The image uses `hck-cli` as its default entrypoint - a simple binary that execut ### Data Volume Structure -The image uses a simplified data structure with volumes mounted directly under `/data`: +The image is designed for a **read-only root filesystem**. Runtime writes go to exactly two places: +| Mount | Purpose | +| --- | --- | +| `/data` | Persistent volume: license/userData (`/data/app`), logs, models, output, settings, options | +| `/tmp` | tmpfs: sockets, caches, and scratch files (discarded when the container exits) | + +Layout under `/data`: + +- `/data/app` - Application data (license state, Electron userData). Lives on the `/data` volume via `XDG_CONFIG_HOME`. - `/data/models` - Your input model files - `/data/output` - Generated artifacts (documentation, schemas, etc.) - `/data/logs` - Application logs organized in `-command` folders (e.g., `2024-01-15-genDoc`) for per-command isolation and troubleshooting - `/data/options` - (Optional) User-defined configurations +- `/data/settings` - Optional settings -**⚠️ MANDATORY:** The application data folder (`/home/hackolade/.config`) **MUST** be mounted as a volume. This volume is absolutely required for licensing and configuration to work properly. Without this volume mounted, the CLI will not function correctly. +**⚠️ MANDATORY:** Mount a volume at `/data` **and** a writable `/tmp` (tmpfs recommended). Without `/data`, licensing and configuration will not persist. Without `/tmp`, Electron and scratch I/O will fail under `read_only: true`. -This structure reduces path length and simplifies volume management compared to the previous `/home/hackolade/Documents/...` structure. +**Breaking change:** Earlier releases required a separate volume at `/home/hackolade/.config`. That path is no longer written; migrate the named volume to `/data` (license state is under `/data/app`). -**Volume validation:** The CLI automatically validates that required volumes are properly mounted. If a required volume is missing, the CLI will display a warning message to help you identify and fix the issue before command execution fails. +**Volume validation:** The CLI automatically validates that required mounts are writable. If a required mount is missing, the CLI will display a warning (or fail in the official image) before command execution. -**Log isolation:** Logs are automatically organized per command in `/data/logs` using folders named `-command` (e.g., `2024-01-15-genDoc`, `2024-01-15-forweng`). This folder structure provides proper command isolation, making it easier to analyze logs for specific commands when troubleshooting issues. Each command execution creates its own log folder, allowing you to trace problems to specific operations by date and command type. +**Log isolation:** Logs are automatically organized per command in `/data/logs` using folders named `-command` (e.g., `2024-01-15-genDoc`, `2024-01-15-forweng`). This folder structure provides proper command isolation, making it easier to analyze logs for specific commands when troubleshooting issues. ## Quick start with Docker Compose -The easiest way to use this image is with Docker Compose. We provide a `compose.yml` file that handles all the configuration. +The easiest way to use this image is with Docker Compose. We provide two example files: -**Important:** The [`compose.yml`](../compose.yml) file in this repository is specifically designed for the **pre-built `hackolade/hck-cli` image** and is tied to this documentation. It uses simplified data paths (`/data/*`) and the `hck-cli` binary entrypoint, which differ from compose files used with custom-built images. +| File | Use when | +| --- | --- | +| [`compose.yml`](../compose.yml) | Getting started locally — minimal configuration, single `/data` volume | +| [`compose.hardened.yml`](../compose.hardened.yml) | Production or CI — read-only root filesystem, dropped capabilities, `/data` + `/tmp` tmpfs (matches Kubernetes Restricted) | -### Step 1: Set Up Your Compose File +**Important:** Both files are designed for the **pre-built `hackolade/hck-cli` image**. They use `/data/*` paths and the `hck-cli` binary entrypoint, which differ from compose files used with custom-built images. -We provide a ready-to-use `compose.yml` file specifically for the pre-built image. You can either: +### Step 1: Set Up Your Compose File -**Option A: Copy the provided compose file** (recommended) +**Option A: Copy the provided compose file** (recommended for first use) -Copy the [`compose.yml`](../compose.yml) file from this repository to your working directory. This compose file is specifically designed for the pre-built `hackolade/hck-cli` image and is documented in this guide: +Copy [`compose.yml`](../compose.yml) to your working directory: ```bash cp compose.yml /path/to/your/working/directory/ ``` -**Option B: Create your own compose file** +**Option B: Use the hardened compose file** (recommended for production / Kubernetes parity) + +Copy [`compose.hardened.yml`](../compose.hardened.yml) instead, or alongside `compose.yml`: + +```bash +cp compose.hardened.yml /path/to/your/working/directory/ +docker compose -f compose.hardened.yml run --rm hck-cli version +``` + +**Option C: Create your own compose file** -Create a `compose.yml` file in your working directory. See the [`compose.yml`](../compose.yml) file in this repository for a complete example. Make sure to follow the structure documented in this guide. +See [`compose.yml`](../compose.yml) for a simple example and [`compose.hardened.yml`](../compose.hardened.yml) for the restricted profile. -The compose file (designed for the pre-built image) includes: -- `hck-cli` service - Main service for running CLI commands -- `showComputerIdForOfflineValidation` service - Gets computer ID for offline license validation -- `validateKeyOnline` service - Validates license online using Docker secrets -- `validateKeyOffline` service - Validates license offline using Docker secrets -- Volume definitions for app data, logs, models, and output +The compose files include: +- `hck-cli` service — main service for running CLI commands +- `showComputerIdForOfflineValidation` — computer ID for offline license validation +- `validateKeyOnline` / `validateKeyOffline` — license validation via Docker secrets +- A single named volume at `/data` (license state, logs, models, output, settings) - Secret definitions for license key and license file +- (`compose.hardened.yml` only) read-only root filesystem, `cap_drop: ALL`, and `/tmp` tmpfs ### Step 2: Create Your Models Directory @@ -286,6 +307,27 @@ docker compose run --rm hck-cli forweng \ --outputtype jsonschema ``` +## Hardened Docker Compose (Kubernetes parity) + +For production clusters or CI pipelines that enforce the Kubernetes **Restricted** Pod Security Standard (or OpenShift **restricted-v2** SCC), use [`compose.hardened.yml`](../compose.hardened.yml). It adds: + +- `read_only: true` — read-only root filesystem +- `cap_drop: [ALL]` and `no-new-privileges` — no extra capabilities or privilege escalation +- `user: "1000:1001"` — non-root (OpenShift arbitrary UID variant included as `hck-cli-arbitrary-uid`) +- `/data` named volume — persistent state (same as `compose.yml`) +- `/tmp` tmpfs — scratch, sockets, and caches (discarded when the container exits) + +```bash +# Smoke test +docker compose -f compose.hardened.yml run --rm hck-cli version + +# OpenShift-style arbitrary UID +docker compose -f compose.hardened.yml run --rm hck-cli-arbitrary-uid version + +# Command that writes output (requires a model at ./models/smoke.hck.json) +docker compose -f compose.hardened.yml run --rm genDoc +``` + ## Using Docker CLI directly If you prefer using Docker CLI directly instead of Docker Compose, here's how: @@ -293,45 +335,52 @@ If you prefer using Docker CLI directly instead of Docker Compose, here's how: ### Basic Command Structure ```bash -docker run --rm \ - -v hackolade-studio-app-data:/home/hackolade/.config \ - -v hackolade-studio-logs:/data/logs \ +docker run --rm --read-only \ + --cap-drop=ALL --security-opt=no-new-privileges \ + --user 1000:1001 \ + -v hackolade-studio-data:/data \ -v ${PWD}/models:/data/models \ - -v hackolade-studio-output:/data/output \ + --tmpfs /tmp:rw,size=1g,mode=1777 \ hackolade/hck-cli:8.9.2 COMMAND [OPTIONS] ``` ### Create Required Volumes -First, create the named volumes: +First, create the named volume for persistent state: ```bash -docker volume create hackolade-studio-app-data -docker volume create hackolade-studio-logs -docker volume create hackolade-studio-output +docker volume create hackolade-studio-data ``` +`/tmp` should be a tmpfs (shown above), not a named volume. + ### Example Commands **Check version:** ```bash -docker run --rm \ - -v hackolade-studio-app-data:/home/hackolade/.config \ +docker run --rm --read-only \ + --user 1000:1001 \ + -v hackolade-studio-data:/data \ + --tmpfs /tmp:rw,size=1g,mode=1777 \ hackolade/hck-cli:8.9.2 version ``` **Get computer ID:** ```bash -docker run --rm hackolade/hck-cli:8.9.2 getComputerId +docker run --rm --read-only \ + --user 1000:1001 \ + -v hackolade-studio-data:/data \ + --tmpfs /tmp:rw,size=1g,mode=1777 \ + hackolade/hck-cli:8.9.2 getComputerId ``` **Generate documentation:** ```bash -docker run --rm \ - -v hackolade-studio-app-data:/home/hackolade/.config \ - -v hackolade-studio-logs:/data/logs \ +docker run --rm --read-only \ + --user 1000:1001 \ + -v hackolade-studio-data:/data \ -v ${PWD}/models:/data/models \ - -v hackolade-studio-output:/data/output \ + --tmpfs /tmp:rw,size=1g,mode=1777 \ hackolade/hck-cli:8.9.2 genDoc \ --format=HTML \ --model /data/models/model.json \ @@ -339,12 +388,12 @@ docker run --rm \ ``` In case of offline validation: ```bash -docker run --rm \ - -v hackolade-studio-app-data:/home/hackolade/.config \ - -v hackolade-studio-logs:/data/logs \ +docker run --rm --read-only \ + --user 1000:1001 \ + -v hackolade-studio-data:/data \ -v ${PWD}/models:/data/models \ - -v hackolade-studio-output:/data/output \ - -v ${PWD}/LicenseFile.xml:/LicenseFile.xml \ + -v ${PWD}/LicenseFile.xml:/data/LicenseFile.xml:ro \ + --tmpfs /tmp:rw,size=1g,mode=1777 \ hackolade/hck-cli:8.9.2 genDoc \ --format=HTML \ --model /data/models/model.json \ @@ -509,30 +558,28 @@ chown -R 1000:1001 ./models chown -R 1000:1001 ./output ``` -**Note:** The container runs as user `hackolade` with UID 1000 and GID 1001 (data-modelers group). +**Note:** The container runs as numeric user `1000:1001` by default (compatible with Kubernetes `runAsNonRoot`). OpenShift-style arbitrary UIDs in group 0 are also supported when `/data` is group-writable. -**Note:** Docker named volumes (like `hackolade-studio-app-data`) don't require permission changes on the host. +**Note:** Docker named volumes (like `hackolade-studio-data`) don't require permission changes on the host. Bind mounts for models should be owned by UID 1000 (or writable by group 0). ### Volume Not Found If Docker says a volume doesn't exist, create it: ```bash -docker volume create hackolade-studio-app-data -docker volume create hackolade-studio-logs -docker volume create hackolade-studio-output +docker volume create hackolade-studio-data ``` -Or let Docker Compose create them automatically on first run. +Or let Docker Compose create it automatically on first run. Ensure every run also mounts a writable `/tmp` (compose uses `tmpfs`). ### Volume Validation Warnings -The CLI automatically checks for required volumes and will warn you if they're not properly mounted. If you see warnings about missing volumes: +The CLI automatically checks for required writable mounts and will warn you if they're not properly mounted. If you see warnings about missing mounts: -1. **Check your compose.yml or docker run command** - Ensure all required volumes are defined: - - `hackolade-studio-app-data` → `/home/hackolade/.config` ⚠️ **MANDATORY** - Required for licensing and configuration - - `hackolade-studio-logs` → `/data/logs` (recommended for log isolation) - - `hackolade-studio-output` → `/data/output` (required for output operations) +1. **Check your compose.yml or docker run command** - Ensure: + - `hackolade-studio-data` → `/data` ⚠️ **MANDATORY** - license state, logs, output, settings + - tmpfs (or equivalent) → `/tmp` ⚠️ **MANDATORY** under `read_only: true` + - Optional bind: host `models` → `/data/models` 2. **Verify volumes exist:** ```bash @@ -544,9 +591,9 @@ The CLI automatically checks for required volumes and will warn you if they're n docker inspect | grep -A 10 Mounts ``` -4. **Review the warning message** - The CLI will indicate which specific volume is missing and what it's used for. +4. **Review the warning message** - The CLI will indicate which specific mount is missing and what it's used for. -**Important:** The `/home/hackolade/.config` volume is **MANDATORY** and must be mounted for the CLI to function. While the CLI will warn about missing volumes, operations will fail without the application data volume. For proper functionality and log isolation, mount all volumes as shown in the compose examples. +**Important:** A single `/data` volume plus `/tmp` tmpfs replaces the older multi-volume layout (`/home/hackolade/.config`, separate logs/output volumes). Migrate by mounting your persistent state at `/data`. ### License Validation Failed @@ -672,19 +719,84 @@ docker compose run --rm hck-cli COMMAND [OPTIONS] docker pull hackolade/hck-cli:8.9.2 # Create volumes -docker volume create hackolade-studio-app-data -docker volume create hackolade-studio-logs -docker volume create hackolade-studio-output +docker volume create hackolade-studio-data # Run command -docker run --rm \ - -v hackolade-studio-app-data:/home/hackolade/.config \ - -v hackolade-studio-logs:/data/logs \ +docker run --rm --read-only \ + --user 1000:1001 \ + -v hackolade-studio-data:/data \ -v ${PWD}/models:/data/models \ - -v hackolade-studio-output:/data/output \ + --tmpfs /tmp:rw,size=1g,mode=1777 \ hackolade/hck-cli:8.9.2 COMMAND ``` +## Kubernetes (restricted / read-only rootfs) + +Example manifests live in [`k8s/`](../k8s/). They mirror `compose.hardened.yml`: `readOnlyRootFilesystem`, non-root, dropped capabilities, a **PVC at `/data`**, and a **memory `emptyDir` at `/tmp`**. + +| Manifest | Purpose | +| --- | --- | +| [`k8s/hck-cli-job.yaml`](../k8s/hck-cli-job.yaml) | Smoke test (`version`) with PVC + `/tmp` emptyDir | +| [`k8s/hck-cli-job-openshift.yaml`](../k8s/hck-cli-job-openshift.yaml) | Same, for OpenShift `restricted-v2` (arbitrary UID, `fsGroup: 0`) | +| [`k8s/hck-cli-gendoc-job.yaml`](../k8s/hck-cli-gendoc-job.yaml) | Generate documentation from a model on the PVC | + +Apply the standard restricted example: + +```bash +kubectl apply -f k8s/hck-cli-job.yaml +kubectl logs job/hck-cli-version +``` + +Minimal Job excerpt (full file includes the PVC): + +```yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: hck-cli-version + labels: + pod-security.kubernetes.io/enforce: restricted +spec: + template: + metadata: + labels: + pod-security.kubernetes.io/enforce: restricted + spec: + restartPolicy: Never + automountServiceAccountToken: false + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1001 + fsGroup: 0 + seccompProfile: + type: RuntimeDefault + volumes: + - name: data + persistentVolumeClaim: + claimName: hck-cli-data + - name: tmp + emptyDir: + medium: Memory + sizeLimit: 1Gi + containers: + - name: hck-cli + image: hackolade/hck-cli:8.9.2 + args: ["version"] + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + volumeMounts: + - name: data + mountPath: /data + - name: tmp + mountPath: /tmp +``` + +See [`k8s/README.md`](../k8s/README.md) for OpenShift and genDoc variants. + ## Backward compatibility with other images For users migrating from the `hackolade/studio` image or custom-built images that use `startup.sh` as the entrypoint, this image maintains backward compatibility by including the `startup.sh` and `show-computer-id.sh` scripts. diff --git a/Studio/doc/license-validation.md b/Studio/doc/license-validation.md index 107845e..2699a9b 100644 --- a/Studio/doc/license-validation.md +++ b/Studio/doc/license-validation.md @@ -30,6 +30,8 @@ docker run --rm \ --identifier=YOUR-UUID-FROM-STEP-1 ``` +For the pre-built `hackolade/hck-cli` image, mount a single `/data` volume (license state lives under `/data/app`) and a `/tmp` tmpfs for hardened deployments. See [`getting-started-hck-cli.md`](./getting-started-hck-cli.md), [`compose.yml`](../compose.yml), and [`compose.hardened.yml`](../compose.hardened.yml). + Replace: - `YOUR-LICENSE-KEY` with your actual floating license key - `YOUR-UUID-FROM-STEP-1` with the UUID you copied from step 1 @@ -114,6 +116,8 @@ docker run --rm \ --file=/LicenseFile.xml ``` +With `hackolade/hck-cli`, prefer `-v hackolade-studio-data:/data` and mount the license file under `/data/...` (see [`getting-started-hck-cli.md`](./getting-started-hck-cli.md)). + Replace `YOUR-LICENSE-KEY` with your actual license key. **Important:** diff --git a/Studio/k8s/README.md b/Studio/k8s/README.md new file mode 100644 index 0000000..8797087 --- /dev/null +++ b/Studio/k8s/README.md @@ -0,0 +1,20 @@ +# Kubernetes examples for hackolade/hck-cli +# +# All manifests follow the Kubernetes Restricted Pod Security Standard and match +# compose.hardened.yml: read-only root filesystem, dropped capabilities, non-root +# user, persistent storage at /data, and a memory-backed emptyDir at /tmp. +# +# Files: +# hck-cli-job.yaml — smoke test (version) with PVC + /tmp emptyDir +# hck-cli-job-openshift.yaml — same, but for OpenShift restricted-v2 (arbitrary UID) +# hck-cli-gendoc-job.yaml — generate documentation from a model on the PVC +# +# Apply (pick the manifest that matches your platform): +# kubectl apply -f hck-cli-job.yaml +# kubectl apply -f hck-cli-job-openshift.yaml +# kubectl apply -f hck-cli-gendoc-job.yaml +# +# Prerequisites: +# - A validated license stored on the /data PVC (run validateKey once via a Job +# or copy license state from a Docker Compose volume). +# - For genDoc: place a model at /data/models/smoke.hck.json on the PVC. diff --git a/Studio/k8s/hck-cli-gendoc-job.yaml b/Studio/k8s/hck-cli-gendoc-job.yaml new file mode 100644 index 0000000..2c9430b --- /dev/null +++ b/Studio/k8s/hck-cli-gendoc-job.yaml @@ -0,0 +1,58 @@ +# Generate documentation under a hardened Job — same security posture as hck-cli-job.yaml. +# +# Place a model at /data/models/smoke.hck.json on the hck-cli-data PVC before running +# (kubectl cp, an init container, or a CI step that seeds the volume). +# +# Apply: +# kubectl apply -f hck-cli-gendoc-job.yaml +# kubectl logs job/hck-cli-gendoc +# +apiVersion: batch/v1 +kind: Job +metadata: + name: hck-cli-gendoc + labels: + app: hck-cli + pod-security.kubernetes.io/enforce: restricted +spec: + backoffLimit: 1 + template: + metadata: + labels: + app: hck-cli + pod-security.kubernetes.io/enforce: restricted + spec: + restartPolicy: Never + automountServiceAccountToken: false + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1001 + fsGroup: 0 + seccompProfile: + type: RuntimeDefault + volumes: + - name: data + persistentVolumeClaim: + claimName: hck-cli-data + - name: tmp + emptyDir: + medium: Memory + sizeLimit: 1Gi + containers: + - name: hck-cli + image: hackolade/hck-cli:8.9.2 + args: + - genDoc + - --model=/data/models/smoke.hck.json + - --doc=/data/output/smoke.pdf + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + volumeMounts: + - name: data + mountPath: /data + - name: tmp + mountPath: /tmp diff --git a/Studio/k8s/hck-cli-job-openshift.yaml b/Studio/k8s/hck-cli-job-openshift.yaml new file mode 100644 index 0000000..a78a5dd --- /dev/null +++ b/Studio/k8s/hck-cli-job-openshift.yaml @@ -0,0 +1,73 @@ +# OpenShift restricted-v2 variant: arbitrary UID in the root supplementary group. +# +# OpenShift assigns a random UID with no /etc/passwd entry. The image handles this +# via nss_wrapper when /data is group-writable (fsGroup: 0 matches image ownership). +# +# Bind explicitly to restricted-v2 when your cluster requires it: +# openshift.io/scc: restricted-v2 +# +# Apply: +# kubectl apply -f hck-cli-job-openshift.yaml +# kubectl logs job/hck-cli-version-openshift +# +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: hck-cli-data + labels: + app: hck-cli +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: hck-cli-version-openshift + labels: + app: hck-cli + annotations: + openshift.io/scc: restricted-v2 +spec: + backoffLimit: 1 + template: + metadata: + labels: + app: hck-cli + annotations: + openshift.io/scc: restricted-v2 + spec: + restartPolicy: Never + automountServiceAccountToken: false + securityContext: + runAsNonRoot: true + # runAsUser omitted — restricted-v2 assigns an arbitrary UID + runAsGroup: 0 + fsGroup: 0 + seccompProfile: + type: RuntimeDefault + volumes: + - name: data + persistentVolumeClaim: + claimName: hck-cli-data + - name: tmp + emptyDir: + medium: Memory + sizeLimit: 1Gi + containers: + - name: hck-cli + image: hackolade/hck-cli:8.9.2 + args: ["version"] + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + volumeMounts: + - name: data + mountPath: /data + - name: tmp + mountPath: /tmp diff --git a/Studio/k8s/hck-cli-job.yaml b/Studio/k8s/hck-cli-job.yaml new file mode 100644 index 0000000..b002b69 --- /dev/null +++ b/Studio/k8s/hck-cli-job.yaml @@ -0,0 +1,69 @@ +# Hardened Kubernetes Job for hackolade/hck-cli (Restricted Pod Security Standard). +# +# Writable mounts: +# /data — PersistentVolumeClaim (license state, logs, models, output) +# /tmp — emptyDir backed by memory (tmpfs equivalent, 1 GiB limit) +# +# Apply: +# kubectl apply -f hck-cli-job.yaml +# kubectl logs job/hck-cli-version +# +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: hck-cli-data + labels: + app: hck-cli +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: hck-cli-version + labels: + app: hck-cli + pod-security.kubernetes.io/enforce: restricted +spec: + backoffLimit: 1 + template: + metadata: + labels: + app: hck-cli + pod-security.kubernetes.io/enforce: restricted + spec: + restartPolicy: Never + automountServiceAccountToken: false + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1001 + fsGroup: 0 + seccompProfile: + type: RuntimeDefault + volumes: + - name: data + persistentVolumeClaim: + claimName: hck-cli-data + - name: tmp + emptyDir: + medium: Memory + sizeLimit: 1Gi + containers: + - name: hck-cli + image: hackolade/hck-cli:8.9.2 + args: ["version"] + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + volumeMounts: + - name: data + mountPath: /data + - name: tmp + mountPath: /tmp