Major improvements: multi-stage Docker, security hardening, host metrics, tests

- Multi-stage Docker build: Dockerfile compiles frontend (Node.js) and
  backend (Gradle) inside Docker; no local Java/Node.js required
- Security hardening: replaced full host root mount with specific
  directory mounts (/proc, /sys, /etc, /var, /home, /opt, /tmp, /srv, /usr)
- Host metrics consistency: LinuxMemoryService and CpuDetailsService now
  read from /host/proc instead of container OSHI
- Concurrency: replaced synchronized with AtomicReference in 3 services
- Global error handling: added @RestControllerAdvice for JSON error responses
- Unit tests: added 32 tests for /proc parsers, CPU usage, process uptime
- Docker security: non-root user, read-only FS, no-new-privileges, healthcheck
- Debug logging: added log.debug() in silent catch blocks
- Documentation: updated README, INSTALLATION, DEVELOPMENT, CONFIGURATION,
  ARCHITECTURE, CHANGELOG

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-10-28 23:18:01 +01:00
co-authored by Junie
parent 539175654c
commit 597d8f380a
23 changed files with 1122 additions and 137 deletions
+15 -1
View File
@@ -35,7 +35,21 @@ Controllers expose system information to the frontend, while service classes col
## System information
OSHI is used to obtain operating-system and hardware information. The data available depends on the runtime environment and permissions.
The application collects system metrics through two complementary approaches:
1. **Direct `/proc` parsing** — Services read host files directly from mounted paths (`/host/proc`, `/host/sys`, `/host/etc`) to obtain accurate host metrics rather than container metrics.
2. **OSHI fallback** — If mounted files are unavailable, OSHI provides a fallback source of system information.
### Host filesystem access
To minimize the attack surface, the container mounts specific host directories rather than the entire root filesystem:
- `/proc` → `/host/proc` — CPU, memory, processes, network, disk mounts
- `/sys` → `/host/sys` — Network interface details
- `/etc/hostname` and `/etc/os-release` → `/host/etc/` — Host identification
- Selected mount points (`/var`, `/home`, `/opt`, etc.) → `/host/` — Disk space information
See `docs/CONFIGURATION.md` for the complete list of volume mounts and instructions for adding custom mount points.
## Packaging
+36
View File
@@ -23,6 +23,42 @@ Review the actual configuration file before changing application properties. Thi
System metrics available to the application depend on the operating system, container runtime, mounted filesystems, and granted permissions.
### Volume mounts
The application mounts specific host directories rather than the entire root filesystem, to minimize the attack surface:
| Host path | Container path | Purpose |
|---|---|---|
| `/proc` | `/host/proc` | CPU, memory, processes, network, disk mounts |
| `/sys` | `/host/sys` | Network interface details |
| `/etc/hostname` | `/host/etc/hostname` | Host name |
| `/etc/os-release` | `/host/etc/os-release` | OS information |
| `/var` | `/host/var` | Disk space for `/var` mounts |
| `/home` | `/host/home` | Disk space for `/home` mounts |
| `/opt` | `/host/opt` | Disk space for `/opt` mounts |
| `/tmp` | `/host/tmp` | Disk space for `/tmp` mounts |
| `/srv` | `/host/srv` | Disk space for `/srv` mounts |
| `/usr` | `/host/usr` | Disk space for `/usr` mounts |
If your system has additional mount points (e.g., `/data`, `/mnt`), add them to the `volumes` section in `docker-compose.yml`:
```yaml
volumes:
- /data:/host/data:ro
```
### Security hardening
The container runs with these security settings:
- `read_only: true` — container filesystem is read-only
- `no-new-privileges:true` — prevents privilege escalation
- `tmpfs` on `/tmp` — temporary storage in memory
- Non-root user (`appuser`) — application does not run as root
- `pid: host` — required to see host processes
### Docker Desktop limitation
A container does not automatically have complete visibility into its host. Docker Desktop on Windows runs Linux containers in a virtualized Linux environment, so reported metrics may describe that environment rather than Windows itself.
Do not expose the monitoring interface to untrusted networks without appropriate access controls and network security.
+47 -1
View File
@@ -8,6 +8,8 @@
## Build and test
### Option 1: Gradle build (requires Java 21 and Node.js)
From the repository root:
```bash
@@ -17,6 +19,21 @@ cd backend
This invokes the configured Gradle build, including frontend build tasks.
### Option 2: Docker multi-stage build (requires only Docker)
Build the entire application inside Docker — no local Java or Node.js required:
```bash
docker build -t htop-web .
```
This uses a multi-stage Dockerfile with three stages:
1. **Frontend** — builds Angular with Node.js 22 Alpine
2. **Builder** — compiles the backend with Gradle and Java 21 JDK
3. **Runtime** — packages only the JAR and Java 21 JRE (~200 MB final image)
The Docker build is fully reproducible and does not depend on tools installed on the host machine.
## Frontend development
Frontend source code is located in `frontend/`.
@@ -38,13 +55,42 @@ If the project does not define a `start` script, inspect `frontend/package.json`
## Run the container
From the repository root, after a successful build:
### With Docker Compose (recommended)
From the repository root:
```bash
docker compose up -d --build
docker compose logs -f htop-web
```
The `--build` flag triggers the multi-stage Docker build automatically.
### With Docker only
If you built the image manually:
```bash
docker run -d \
--name htop-web \
--pid host \
--read-only \
--security-opt no-new-privileges:true \
--tmpfs /tmp:noexec,nosuid,size=64m \
-v /proc:/host/proc:ro \
-v /sys:/host/sys:ro \
-v /etc/hostname:/host/etc/hostname:ro \
-v /etc/os-release:/host/etc/os-release:ro \
-v /var:/host/var:ro \
-v /home:/host/home:ro \
-v /opt:/host/opt:ro \
-v /tmp:/host/tmp:ro \
-v /srv:/host/srv:ro \
-v /usr:/host/usr:ro \
-p 8080:8080 \
htop-web
```
## Contribution guidelines
- Keep changes focused and reviewable.
+43 -16
View File
@@ -4,11 +4,35 @@
- Docker Engine
- Docker Compose
- Java 21 and Node.js/npm if building the application locally
## Build the application
No local Java or Node.js installation is required — the Dockerfile uses multi-stage build to compile everything inside Docker.
From the repository root:
## Build and start with Docker Compose
From the repository root, run a single command:
```bash
docker compose up -d --build
```
The `--build` flag triggers the multi-stage Docker build that compiles both the Angular frontend and the Spring Boot backend inside Docker. No local JDK or Node.js is needed.
The default Compose configuration maps host port `8088` to container port `8080`.
Open http://localhost:8088 in your browser.
## Alternative: Build the image manually
If you prefer to build the Docker image separately:
```bash
docker build -t htop-web .
docker compose up -d
```
## Alternative: Local Gradle build (requires Java 21 and Node.js)
If you need to build locally (for example, to run tests):
```bash
cd backend
@@ -18,16 +42,6 @@ cd ..
The Gradle build also builds the Angular frontend and packages its production assets into the Spring Boot application.
## Start with Docker Compose
```bash
docker compose up -d --build
```
The default Compose configuration maps host port `8088` to container port `8080`.
Open http://localhost:8088 in your browser.
## Check status and logs
```bash
@@ -43,16 +57,29 @@ docker compose down
## Simple Scenario of using
```bash
```yaml
services:
htop-web:
image: gitea.jambotron.com/liosha84/htop-web:latest
container_name: htop-web
restart: unless-stopped
pid: host
read_only: true
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:noexec,nosuid,size=64m
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/host/root:ro
- /etc/hostname:/host/etc/hostname:ro
- /etc/os-release:/host/etc/os-release:ro
- /var:/host/var:ro
- /home:/host/home:ro
- /opt:/host/opt:ro
- /tmp:/host/tmp:ro
- /srv:/host/srv:ro
- /usr:/host/usr:ro
labels:
caddy: htop-web.jambotron.com
caddy.reverse_proxy: "{{upstreams 8080}}"
@@ -64,6 +91,6 @@ networks:
external: true
```
```bash
```bash
docker compose up -d
```