# GravityLAN — Docker Container Hardening Guide
This document outlines the security posture, vulnerability minimization steps, and architectural trade-offs applied to the production GravityLAN Docker container.
---
## 1. Vulnerability & Risk Assessment
A full security scan of standard Python-based Docker containers often reveals multiple OS-level vulnerabilities (CVEs) residing in pre-baked libraries of the base image (e.g., `Debian Bookworm` or `Ubuntu`).
### Core CVE Drivers in Homelab/Network Tools
1. **OS System Libraries**: Vulnerabilities inside core dependencies like `glibc` (GNU C Library), `openssl`, `libssl`, `systemd`, `libxml2`, and `gnutls`.
2. **Network Clients & Utilities**: Libraries like `curl` and `libcurl` are historically high-frequency targets for remote and local security advisories due to complex parsing and protocol handling.
3. **Compilers & Build Artifacts**: The presence of development headers, compilers (like `gcc`), and temporary build-stage artifacts inside the runtime environment unnecessarily increases the attack surface.
---
## 2. Hardened Architecture Implemented (v0.2.5)
GravityLAN implements a secure, **multi-stage, capabilities-aware build** designed to minimize the final container's attack surface while preserving maximum network discovery performance.
```mermaid
graph TD
A[Stage 1: Frontend Build
node:20-alpine] -->|Static HTML/JS| C(Stage 3: Runtime Environment)
B[Stage 2: Python Build
python:3.12-slim] -->|Isolate /opt/venv| C
C -->|Minimal System Tools| D[Low-privilege Runtime Layer]
D -->|Non-root user: gravitylan| E[Secure Container Execution]
```
### Hardening Interventions & Mitigations
#### A. Eliminating Curl & Transitioning to Native Python Healthcheck
* **Problem**: Installing the `curl` package for the container `HEALTHCHECK` introduces `curl` and its heavy dependencies (like `libcurl`, `gnutls`), which frequently suffer from network-level vulnerabilities.
* **Solution**: We completely removed `curl` from the runtime dependencies list. The container healthcheck now runs natively in Python using the built-in, dependency-free `urllib.request` module:
```dockerfile
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD python3 -c "import urllib.request, os, sys; port = os.environ.get('GRAVITYLAN_PORT', '8000'); urllib.request.urlopen(f'http://127.0.0.1:{port}/api/health', timeout=5)"
```
This eliminates the need for an external HTTP client, decreasing the image attack surface while providing a more robust, environment-variable-aware health check.
#### B. Absolute Minimal Runtime Dependencies
The final stage installs only essential binaries and system libraries specifically required for network mapping and name resolution, fully isolated from development compilers:
* **`nmap`**: The core asynchronous network scanning engine.
* **`libcap2-bin`**: Used to apply granular Linux capabilities specifically to the `nmap` executable.
* **`iputils-ping`**: Backing utility for ping-based connectivity tests.
* **`avahi-utils`**: Required for `avahi-resolve` to perform local mDNS (multicast DNS) hostname discovery fallback.
* **`iproute2`**: For interface and gateway mapping.
* **`dnsutils`**: Backing utility for `dig` to run reverse DNS fallback resolutions (PTR queries).
---
## 3. The Build Reproducibility vs. Upstream Patching Trade-Off
A key security decision in Docker image design is how to handle OS package patching:
### Rebuilding with Upstream Updates (Preferred)
Rather than executing generic, non-deterministic `apt-get upgrade -y` inside the `Dockerfile`—which introduces dynamic, unpinned dependency drifts and breaks build caching—GravityLAN prioritizes **Build Reproducibility**.
To ensure that the container remains highly deterministic and predictable across environments, package versions are governed by the base `python:3.12-slim` image tag.
To pull down the newest base OS-level security patches (such as updated `glibc` or `systemd` packages) in a standard, predictable manner, the image should be rebuilt periodically using the `--pull` and `--no-cache` flags:
```bash
docker build --pull --no-cache -t sleeperxr/gravitylan:latest .
```
This forces Docker to pull down the latest officially patched Python base image from the Docker Registry, keeping container packages secure without polluting the Dockerfile with non-deterministic upgrade side-effects.
---
## 4. Runtime Security & Privilege Isolation
To prevent container breakouts or host-level compromise, the following boundary controls are enforced:
1. **Non-Root Execution**:
The container does **not** execute as `root`. A dedicated user, `gravitylan`, is created:
```dockerfile
RUN useradd -m -s /bin/bash gravitylan
USER gravitylan
```
2. **Granular Capabilities (`setcap`)**:
Standard Docker containers run with limited privileges, preventing `nmap` from issuing raw socket commands unless executed as `root`. Rather than running the entire FastAPI application as root, we use `setcap` to grant only the absolute minimum required privileges directly to the `nmap` binary:
```dockerfile
RUN setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip /usr/bin/nmap
```
This allows the FastAPI backend to run as the non-root `gravitylan` user, but successfully spawns low-privileged `nmap` sub-processes that can perform high-speed ARP, ICMP, and TCP scans.
3. **Docker Compose Capabilities Assignment**:
To ensure the container can leverage these capabilities, you must include them in your `docker-compose.yml`:
```yaml
services:
gravitylan-server:
# ...
cap_add:
- NET_RAW
- NET_ADMIN
```
---
## 5. Remaining Risks & Trade-Offs
While the container is highly optimized, certain architectural trade-offs must be acknowledged:
| Package | Purpose | Risk | Alternative / Mitigation |
| :--- | :--- | :--- | :--- |
| **`nmap`** | Raw network scanning | Executable with `cap_net_raw` and `cap_net_admin` capabilities. | Privileges are strictly isolated using `setcap`. FastAPI input validation sanitizes all IP and subnet parameters to prevent command injection. |
| **`avahi-utils` / `dnsutils`** | Hostname resolution fallbacks (`avahi-resolve`, `dig`) | Injects external system utility dependencies into the runtime. | Required to support homelab dynamic networks where standard reverse-DNS might fail. Standardized library implementations (like `dnspython`) are prioritized first, with shell commands acting only as fallback. |
| **Debian Base Slim** | Standard Python runtime | Larger footprint than Alpine or Distroless. | Essential for compiled raw extensions (wheels) and Python-based native database and network libraries. Regularly rebuilt against active python tags to integrate upstream CVE fixes. |