feat: Docker-Support (Compose + Dockerfiles)

- docker-compose.yml: db (timescaledb/pg17) + backend + frontend
- backend/Dockerfile: multi-stage (golang:1.24-alpine -> debian:bookworm-slim)
- frontend/Dockerfile: multi-stage (node:22-alpine -> nginx:alpine), Build-Args fuer VITE_*
- frontend/nginx.conf: SPA-Routing + Asset-Caching
- .env.example: BACKEND_HOST, DB_PASSWORD, JWT_SECRET, API_KEY
- backend/config/config.go: ENV-Var-Support vervollstaendigt (JWT_SECRET, API_KEY, DB_PORT, DB_USER, DB_NAME)
- backend/config.yaml.example: ENV-Var Kommentare ergaenzt
- README: Docker-Quickstart als empfohlener Einstieg (3 Befehle)
- .gitignore: .env zur ignore-Liste hinzugefuegt
This commit is contained in:
cynfo3000 2026-03-08 19:39:10 +01:00
parent 3916dc8bee
commit 9d5f060cbd
9 changed files with 230 additions and 5 deletions

18
.env.example Normal file
View File

@ -0,0 +1,18 @@
# docker-compose Konfiguration
# Kopiere diese Datei nach .env und trage deine Werte ein
# IP oder Hostname des Servers auf dem du docker compose ausfuehrst
# (wird vom Browser genutzt um das Backend zu erreichen)
BACKEND_HOST=192.168.1.100
BACKEND_PORT=8443
# Datenbank-Passwort (frei waehlbar)
DB_PASSWORD=sicheres_db_passwort
# JWT Secret — langer zufaelliger String (min. 32 Zeichen)
# Beispiel: openssl rand -hex 32
JWT_SECRET=ZUFAELLIGER_STRING_MIN_32_ZEICHEN
# API Key — frei waehlbar, wird fuer Agent und Frontend benoetigt
# Beispiel: openssl rand -hex 16
API_KEY=SELBST_GEWAEHLTER_API_KEY

4
.gitignore vendored
View File

@ -38,9 +38,11 @@ frontend/dist/
frontend/src/config.js frontend/src/config.js
!frontend/src/config.example.js !frontend/src/config.example.js
# .env hat echte Credentials — nie einchecken # .env Dateien haben echte Credentials — nie einchecken
frontend/.env frontend/.env
!frontend/.env.example !frontend/.env.example
.env
!.env.example
# ======================== # ========================
# OS / Editor # OS / Editor

View File

@ -158,6 +158,45 @@ Client ──► Backend:ProxyPort ──► WebSocket (Binary) ──► Agent
## Schnellstart ## Schnellstart
### Docker (empfohlen)
Der einfachste Weg. Voraussetzung: [Docker](https://docs.docker.com/get-docker/) + [Docker Compose](https://docs.docker.com/compose/install/) installiert.
```bash
# 1. Repository klonen
git clone https://git.example.com/rmm.git && cd rmm
# 2. Konfiguration anlegen
cp .env.example .env
```
`.env` anpassen:
```env
BACKEND_HOST=192.168.1.100 # IP/Hostname dieses Servers
DB_PASSWORD=sicheres_passwort
JWT_SECRET=$(openssl rand -hex 32)
API_KEY=$(openssl rand -hex 16)
```
```bash
# 3. Starten (baut alle Images automatisch)
docker compose up -d
# 4. Status prüfen
docker compose ps
docker compose logs backend
```
Fertig. Frontend unter `http://<BACKEND_HOST>`, Backend-API unter `https://<BACKEND_HOST>:8443`.
> **Hinweis:** Das Frontend-Image wird mit der `BACKEND_HOST`-URL gebaut.
> Nach einer IP-Änderung muss das Frontend neu gebaut werden: `docker compose build frontend && docker compose up -d frontend`
---
### Manuell (ohne Docker)
### 1. Voraussetzungen ### 1. Voraussetzungen
**Backend-Server** (Linux, Debian 12+ empfohlen): **Backend-Server** (Linux, Debian 12+ empfohlen):

31
backend/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM golang:1.24-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o rmm-backend .
# ---
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /build/rmm-backend .
COPY config.yaml.example config.yaml.example
RUN mkdir -p certs
EXPOSE 8443
# Umgebungsvariablen mit Standardwerten (koennen alle per ENV ueberschrieben werden)
ENV RMM_LISTEN_ADDR=":8443" \
RMM_DB_HOST="db" \
RMM_DB_PORT="5432" \
RMM_DB_USER="rmm" \
RMM_DB_NAME="rmm"
CMD ["./rmm-backend"]

View File

@ -1,8 +1,23 @@
# RMM Backend Konfiguration # RMM Backend Konfiguration
# Kopiere diese Datei nach config.yaml und passe die Werte an.
# Alle Werte koennen auch als Umgebungsvariablen gesetzt werden (Docker).
listen_addr: ":8443" listen_addr: ":8443"
tls_cert: "certs/server.crt" tls_cert: "certs/server.crt"
tls_key: "certs/server.key" tls_key: "certs/server.key"
db_path: "rmm.db"
api_keys: # Datenbank
- "YOUR_API_KEY" database:
host: "127.0.0.1" # ENV: RMM_DB_HOST
port: 5432 # ENV: RMM_DB_PORT
user: "rmm" # ENV: RMM_DB_USER
password: "" # ENV: RMM_DB_PASSWORD
dbname: "rmm" # ENV: RMM_DB_NAME
sslmode: "disable"
# Sicherheit — zufaellige Strings waehlen, nie die Beispielwerte verwenden!
jwt_secret: "ZUFAELLIGER_STRING_MIN_32_ZEICHEN" # ENV: RMM_JWT_SECRET
# API-Keys fuer Agent und Frontend-Zugriff
api_keys: # ENV: RMM_API_KEY (wird zusaetzlich angehaengt)
- "SELBST_GEWAEHLTER_API_KEY"

View File

@ -1,6 +1,7 @@
package config package config
import ( import (
"fmt"
"os" "os"
"github.com/cynfo/rmm-backend/db" "github.com/cynfo/rmm-backend/db"
@ -41,7 +42,7 @@ func Load(path string) (*Config, error) {
return nil, err return nil, err
} }
// Env overrides // Env overrides — alle Werte koennen via ENV ueberschrieben werden (z.B. Docker)
if v := os.Getenv("RMM_LISTEN_ADDR"); v != "" { if v := os.Getenv("RMM_LISTEN_ADDR"); v != "" {
cfg.ListenAddr = v cfg.ListenAddr = v
} }
@ -51,12 +52,32 @@ func Load(path string) (*Config, error) {
if v := os.Getenv("RMM_TLS_KEY"); v != "" { if v := os.Getenv("RMM_TLS_KEY"); v != "" {
cfg.TLSKey = v cfg.TLSKey = v
} }
if v := os.Getenv("RMM_JWT_SECRET"); v != "" {
cfg.JWTSecret = v
}
if v := os.Getenv("RMM_API_KEY"); v != "" {
cfg.APIKeys = append(cfg.APIKeys, v)
}
if v := os.Getenv("RMM_DB_HOST"); v != "" { if v := os.Getenv("RMM_DB_HOST"); v != "" {
cfg.Database.Host = v cfg.Database.Host = v
} }
if v := os.Getenv("RMM_DB_PORT"); v != "" {
if port := 0; len(v) > 0 {
fmt.Sscanf(v, "%d", &port)
if port > 0 {
cfg.Database.Port = port
}
}
}
if v := os.Getenv("RMM_DB_USER"); v != "" {
cfg.Database.User = v
}
if v := os.Getenv("RMM_DB_PASSWORD"); v != "" { if v := os.Getenv("RMM_DB_PASSWORD"); v != "" {
cfg.Database.Password = v cfg.Database.Password = v
} }
if v := os.Getenv("RMM_DB_NAME"); v != "" {
cfg.Database.DBName = v
}
return cfg, nil return cfg, nil
} }

49
docker-compose.yml Normal file
View File

@ -0,0 +1,49 @@
services:
db:
image: timescale/timescaledb:latest-pg17
restart: unless-stopped
environment:
POSTGRES_DB: rmm
POSTGRES_USER: rmm
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U rmm"]
interval: 5s
timeout: 5s
retries: 10
backend:
build: ./backend
restart: unless-stopped
ports:
- "8443:8443"
environment:
RMM_DB_HOST: db
RMM_DB_PASSWORD: ${DB_PASSWORD}
RMM_JWT_SECRET: ${JWT_SECRET}
RMM_API_KEY: ${API_KEY}
volumes:
- certs:/app/certs
depends_on:
db:
condition: service_healthy
frontend:
build:
context: ./frontend
args:
VITE_BACKEND_HOST: ${BACKEND_HOST}
VITE_BACKEND_PORT: ${BACKEND_PORT:-8443}
VITE_API_KEY: ${API_KEY}
restart: unless-stopped
ports:
- "80:80"
depends_on:
- backend
volumes:
pgdata:
certs:

29
frontend/Dockerfile Normal file
View File

@ -0,0 +1,29 @@
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# Backend-URL wird zur Build-Zeit via ARG gesetzt
ARG VITE_BACKEND_HOST=localhost
ARG VITE_BACKEND_PORT=8443
ARG VITE_API_KEY=""
ENV VITE_BACKEND_HOST=$VITE_BACKEND_HOST \
VITE_BACKEND_PORT=$VITE_BACKEND_PORT \
VITE_API_KEY=$VITE_API_KEY
RUN npm run build
# ---
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

21
frontend/nginx.conf Normal file
View File

@ -0,0 +1,21 @@
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# SPA-Routing: alle Pfade auf index.html
location / {
try_files $uri $uri/ /index.html;
}
# Caching fuer Assets
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Kein Caching fuer index.html
location = /index.html {
add_header Cache-Control "no-cache";
}
}