Compare commits
10 Commits
54ddc0b902
...
b9d7383b21
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9d7383b21 | ||
|
|
d312a33d9e | ||
|
|
3afd5c8445 | ||
|
|
9d5f060cbd | ||
|
|
3916dc8bee | ||
|
|
be7cd32d45 | ||
|
|
65c0e6145b | ||
|
|
9035da89ce | ||
|
|
ebd3aecdb5 | ||
|
|
5fc46a8148 |
18
.env.example
Normal file
18
.env.example
Normal 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
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -38,6 +38,12 @@ frontend/dist/
|
||||
frontend/src/config.js
|
||||
!frontend/src/config.example.js
|
||||
|
||||
# .env Dateien haben echte Credentials — nie einchecken
|
||||
frontend/.env
|
||||
!frontend/.env.example
|
||||
.env
|
||||
!.env.example
|
||||
|
||||
# ========================
|
||||
# OS / Editor
|
||||
# ========================
|
||||
|
||||
182
README.md
182
README.md
@ -158,13 +158,179 @@ Client ──► Backend:ProxyPort ──► WebSocket (Binary) ──► Agent
|
||||
|
||||
## Schnellstart
|
||||
|
||||
### Docker (empfohlen)
|
||||
|
||||
Voraussetzung: Docker installiert (`curl -fsSL https://get.docker.com | sh`).
|
||||
|
||||
```bash
|
||||
make all # Backend + Agent + Updater bauen
|
||||
make certs # TLS-Zertifikate generieren
|
||||
make deploy-backend # Backend deployen
|
||||
git clone <repo-url> && cd rmm
|
||||
git checkout release
|
||||
cp .env.example .env # .env anpassen (IP-Adresse, Passwörter, API-Key)
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Agent auf OPNsense installieren → siehe [agent-bsd/README.md](agent-bsd/README.md)
|
||||
Fertig. Frontend unter `http://<BACKEND_HOST>`.
|
||||
|
||||
Vollständige Anleitung mit allen Details, Troubleshooting und Backup: **[docs/DOCKER.md](docs/DOCKER.md)**
|
||||
|
||||
---
|
||||
|
||||
### Manuell (ohne Docker)
|
||||
|
||||
|
||||
### 1. Voraussetzungen
|
||||
|
||||
**Backend-Server** (Linux, Debian 12+ empfohlen):
|
||||
```bash
|
||||
# PostgreSQL 17 + TimescaleDB
|
||||
apt install -y postgresql postgresql-client
|
||||
# TimescaleDB: https://docs.timescale.com/self-hosted/latest/install/
|
||||
```
|
||||
|
||||
**Build-System** (macOS oder Linux):
|
||||
```bash
|
||||
# Go 1.24+
|
||||
go version # muss >= 1.24 sein
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Konfiguration
|
||||
|
||||
**Backend:**
|
||||
```bash
|
||||
cp backend/config.yaml.example backend/config.yaml
|
||||
```
|
||||
```yaml
|
||||
# backend/config.yaml (wichtigste Felder)
|
||||
listen_addr: ":8443"
|
||||
db_dsn: "postgres://rmm:PASSWORT@localhost:5432/rmm?sslmode=disable"
|
||||
jwt_secret: "ZUFAELLIGER_STRING_MIN_32_ZEICHEN"
|
||||
api_key: "SELBST_GEWAEHLTER_API_KEY"
|
||||
```
|
||||
|
||||
> **Hinweis:** Den `api_key` legst du selbst fest — z.B. eine UUID oder ein langer zufaelliger String.
|
||||
> Denselben Wert traegst du gleich in die Frontend-`.env` ein. Du brauchst kein laufendes
|
||||
> System dafuer. Ueber die Einstellungen-Seite koennen spaeter weitere Keys (z.B. pro Agent)
|
||||
> hinzugefuegt werden.
|
||||
|
||||
**Frontend:**
|
||||
```bash
|
||||
cp frontend/.env.example frontend/.env
|
||||
```
|
||||
```env
|
||||
# frontend/.env
|
||||
VITE_BACKEND_HOST=192.168.1.100 # IP/Hostname deines Backend-Servers
|
||||
VITE_BACKEND_PORT=8443
|
||||
VITE_API_KEY=SELBST_GEWAEHLTER_API_KEY # identisch mit api_key aus backend/config.yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Datenbank anlegen
|
||||
|
||||
```bash
|
||||
# Als postgres-User auf dem Backend-Server:
|
||||
sudo -u postgres psql << 'SQL'
|
||||
CREATE USER rmm WITH PASSWORD 'PASSWORT';
|
||||
CREATE DATABASE rmm OWNER rmm;
|
||||
\c rmm
|
||||
CREATE EXTENSION IF NOT EXISTS timescaledb;
|
||||
SQL
|
||||
```
|
||||
|
||||
Die Tabellen werden beim ersten Backend-Start automatisch angelegt (Auto-Migration).
|
||||
|
||||
---
|
||||
|
||||
### 4. Bauen
|
||||
|
||||
```bash
|
||||
# Alles in einem Schritt:
|
||||
make all
|
||||
|
||||
# Oder einzeln:
|
||||
make backend # → build/rmm-backend (linux/amd64)
|
||||
make agent-bsd # → build/rmm-agent-bsd (freebsd/amd64)
|
||||
make agent-linux # → build/rmm-agent-linux (linux/amd64)
|
||||
make frontend # → frontend/dist/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. Backend deployen
|
||||
|
||||
```bash
|
||||
# Binary + Config auf den Server kopieren:
|
||||
scp build/rmm-backend root@<backend-server>:/opt/rmm/
|
||||
scp backend/config.yaml root@<backend-server>:/opt/rmm/
|
||||
|
||||
# Systemd-Service einrichten:
|
||||
cat > /etc/systemd/system/rmm-backend.service << 'EOF'
|
||||
[Unit]
|
||||
Description=RMM Backend
|
||||
After=network.target postgresql.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/opt/rmm/rmm-backend
|
||||
WorkingDirectory=/opt/rmm
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now rmm-backend
|
||||
systemctl status rmm-backend
|
||||
```
|
||||
|
||||
Der erste Start legt automatisch einen Admin-User an (Credentials werden ins Log geschrieben).
|
||||
|
||||
---
|
||||
|
||||
### 6. Frontend deployen
|
||||
|
||||
```bash
|
||||
# Build (mit .env-Konfiguration aus Schritt 2):
|
||||
cd frontend && npm install && npm run build
|
||||
|
||||
# Auf Web-Server kopieren (z.B. Nginx):
|
||||
scp -r frontend/dist/* root@<frontend-server>:/var/www/html/
|
||||
|
||||
# Nginx-Konfiguration (einfach):
|
||||
cat > /etc/nginx/sites-available/rmm << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
root /var/www/html;
|
||||
index index.html;
|
||||
location / { try_files $uri $uri/ /index.html; }
|
||||
}
|
||||
EOF
|
||||
ln -s /etc/nginx/sites-available/rmm /etc/nginx/sites-enabled/
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. Erste Anmeldung & 2FA
|
||||
|
||||
1. Frontend unter `http://<frontend-server>` aufrufen
|
||||
2. Mit dem beim ersten Backend-Start erstellten Admin-Account anmelden
|
||||
3. Einstellungen → **Zwei-Faktor-Authentifizierung** → Einrichten (empfohlen)
|
||||
4. TOTP-Secret in Aegis / Google Authenticator eingeben, ersten Code bestätigen
|
||||
|
||||
---
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
| Problem | Lösung |
|
||||
|---------|--------|
|
||||
| Backend startet nicht | `journalctl -u rmm-backend -f` — häufig DB-Verbindung oder Port belegt |
|
||||
| Agent verbindet nicht | API-Key in `agent/config.yaml` prüfen, TLS-Zertifikat ggf. als trusted markieren |
|
||||
| Frontend zeigt keine Daten | `VITE_BACKEND_HOST` und `VITE_API_KEY` in `.env` prüfen, neu bauen |
|
||||
| TimescaleDB fehlt | `CREATE EXTENSION timescaledb` in der rmm-Datenbank ausführen |
|
||||
|
||||
## Dateistruktur
|
||||
|
||||
@ -210,12 +376,12 @@ rmm/
|
||||
- **Persistente Agent-ID**: Ueberlebt Agent- und Backend-Neustarts
|
||||
- **FreeBSD-kompatibel**: Volle Pfade (/usr/bin/netstat etc.), daemon statt nohup
|
||||
|
||||
## Zugangsdaten
|
||||
## Zugangsdaten (nach Installation)
|
||||
|
||||
```
|
||||
Backend: https://your-backend:8443
|
||||
API-Key: YOUR_API_KEY
|
||||
Frontend: http://your-backend (admin / Start!123)
|
||||
Backend: https://<backend-ip>:8443
|
||||
API-Key: in backend/config.yaml → api_key
|
||||
Frontend: https://<frontend-ip> (Login: admin / Passwort beim ersten Start gesetzt)
|
||||
```
|
||||
|
||||
## Lizenz
|
||||
|
||||
31
backend/Dockerfile
Normal file
31
backend/Dockerfile
Normal 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"]
|
||||
@ -1,8 +1,23 @@
|
||||
# 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"
|
||||
tls_cert: "certs/server.crt"
|
||||
tls_key: "certs/server.key"
|
||||
db_path: "rmm.db"
|
||||
|
||||
api_keys:
|
||||
- "YOUR_API_KEY"
|
||||
# Datenbank
|
||||
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"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cynfo/rmm-backend/db"
|
||||
@ -41,7 +42,7 @@ func Load(path string) (*Config, error) {
|
||||
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 != "" {
|
||||
cfg.ListenAddr = v
|
||||
}
|
||||
@ -51,12 +52,32 @@ func Load(path string) (*Config, error) {
|
||||
if v := os.Getenv("RMM_TLS_KEY"); 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 != "" {
|
||||
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 != "" {
|
||||
cfg.Database.Password = v
|
||||
}
|
||||
if v := os.Getenv("RMM_DB_NAME"); v != "" {
|
||||
cfg.Database.DBName = v
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ func ensureTLS(certPath, keyPath string) error {
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("192.168.85.13")},
|
||||
IPAddresses: localIPs(),
|
||||
DNSNames: []string{"localhost", "rmm-backend"},
|
||||
}
|
||||
|
||||
@ -182,3 +182,28 @@ func ensureTLS(certPath, keyPath string) error {
|
||||
log.Printf("TLS-Zertifikat generiert: %s, %s", certPath, keyPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// localIPs sammelt alle lokalen IP-Adressen des Hosts für das TLS-Zertifikat
|
||||
func localIPs() []net.IP {
|
||||
ips := []net.IP{net.ParseIP("127.0.0.1")}
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return ips
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
addrs, _ := iface.Addrs()
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
if ip != nil && !ip.IsLoopback() && ip.To4() != nil {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ips
|
||||
}
|
||||
|
||||
@ -1,14 +1,24 @@
|
||||
#!/bin/bash
|
||||
# Frontend Build & Deploy
|
||||
# Umgebungsvariablen setzen:
|
||||
# REMOTE=root@<FRONTEND-SERVER>
|
||||
# VITE_BACKEND_HOST=<BACKEND-IP-ODER-HOSTNAME>
|
||||
# VITE_BACKEND_PORT=8443 (optional, default: 8443)
|
||||
# VITE_API_KEY=<API-KEY> (optional)
|
||||
|
||||
set -e
|
||||
FRONTEND_DIR="$(dirname "$0")/frontend"
|
||||
REMOTE="root@192.168.85.20"
|
||||
WEB_ROOT="/var/www/html"
|
||||
|
||||
echo "Building..."
|
||||
cd "$FRONTEND_DIR" && npm run build
|
||||
REMOTE="${REMOTE:-root@localhost}"
|
||||
WEBROOT="${WEBROOT:-/var/www/html}"
|
||||
|
||||
echo "Deploying..."
|
||||
ssh "$REMOTE" "rm -rf ${WEB_ROOT}/assets/*"
|
||||
scp -r dist/* "$REMOTE:${WEB_ROOT}/"
|
||||
ssh "$REMOTE" "chmod -R 755 ${WEB_ROOT}/assets && chmod 644 ${WEB_ROOT}/assets/* ${WEB_ROOT}/index.html ${WEB_ROOT}/vite.svg 2>/dev/null; nginx -s reload"
|
||||
echo "Done."
|
||||
echo "==> Baue Frontend..."
|
||||
VITE_BACKEND_HOST="${VITE_BACKEND_HOST}" \
|
||||
VITE_BACKEND_PORT="${VITE_BACKEND_PORT:-8443}" \
|
||||
VITE_API_KEY="${VITE_API_KEY}" \
|
||||
npm run build
|
||||
|
||||
echo "==> Deploye nach $REMOTE:$WEBROOT ..."
|
||||
ssh "$REMOTE" "rm -rf ${WEBROOT}/assets/*"
|
||||
scp -r dist/assets dist/index.html "$REMOTE:${WEBROOT}/"
|
||||
ssh "$REMOTE" "chown -R www-data:www-data ${WEBROOT}/ && chmod -R 755 ${WEBROOT}/"
|
||||
echo "==> Fertig"
|
||||
|
||||
48
docker-compose.yml
Normal file
48
docker-compose.yml
Normal file
@ -0,0 +1,48 @@
|
||||
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: ./frontend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
environment:
|
||||
BACKEND_HOST: ${BACKEND_HOST}
|
||||
BACKEND_PORT: ${BACKEND_PORT:-8443}
|
||||
API_KEY: ${API_KEY}
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
certs:
|
||||
@ -291,7 +291,7 @@ Siehe [FIRMWARE.md](FIRMWARE.md) fuer Details.
|
||||
# Peer anlegen
|
||||
curl -sk -H "X-API-Key: YOUR_API_KEY" -X POST \
|
||||
https://your-backend:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/wireguard/peers \
|
||||
-d '{"name":"VPN_MUELLER","allowed_ips":"0.0.0.0/0","dns":"10.172.100.210"}'
|
||||
-d '{"name":"VPN_MUELLER","allowed_ips":"0.0.0.0/0","dns":"<DNS-SERVER>"}'
|
||||
|
||||
# Peer mit eigenem Endpoint
|
||||
curl -sk -H "X-API-Key: YOUR_API_KEY" -X POST \
|
||||
|
||||
@ -620,7 +620,7 @@ Peer-Anlage: Keypair generiert auf Firewall, naechste freie IP automatisch, Endp
|
||||
# Peer anlegen
|
||||
curl -sk -H "X-API-Key: YOUR_API_KEY" -X POST \
|
||||
https://your-backend:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/wireguard/peers \
|
||||
-d '{"name":"VPN_MUELLER","allowed_ips":"0.0.0.0/0","dns":"10.172.100.210"}'
|
||||
-d '{"name":"VPN_MUELLER","allowed_ips":"0.0.0.0/0","dns":"<DNS-SERVER>"}'
|
||||
|
||||
# Alle Peers
|
||||
curl -sk -H "X-API-Key: YOUR_API_KEY" \
|
||||
|
||||
221
docs/DOCKER.md
Normal file
221
docs/DOCKER.md
Normal file
@ -0,0 +1,221 @@
|
||||
# Docker Installation
|
||||
|
||||
Diese Anleitung erklärt, wie du das RMM-System mit Docker auf einem Linux-Server installierst.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Linux-Server (Debian 12 / Ubuntu 22.04 oder neuer empfohlen)
|
||||
- Mindestens 2 GB RAM, 10 GB freier Speicherplatz
|
||||
- Docker und Docker Compose installiert
|
||||
|
||||
**Docker installieren** (falls noch nicht vorhanden):
|
||||
```bash
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
```
|
||||
|
||||
Das war es. Docker Compose ist seit Docker 2.x automatisch dabei (`docker compose`).
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Schritt 1 — Repository klonen
|
||||
|
||||
```bash
|
||||
git clone https://git.cynfo.net/christian/rmm2.git
|
||||
cd rmm2
|
||||
git checkout release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Schritt 2 — Konfigurationsdatei anlegen
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Öffne `.env` mit einem Texteditor und trage deine Werte ein:
|
||||
|
||||
```bash
|
||||
nano .env
|
||||
```
|
||||
|
||||
Die Datei sieht so aus:
|
||||
|
||||
```env
|
||||
# IP-Adresse oder Hostname dieses Servers
|
||||
# (die Adresse über die du später im Browser das Frontend erreichst)
|
||||
BACKEND_HOST=192.168.1.100
|
||||
|
||||
# Port des Backends — 8443 ist der Standard, nur ändern wenn nötig
|
||||
BACKEND_PORT=8443
|
||||
|
||||
# Datenbank-Passwort — frei wählbar, wird nur intern verwendet
|
||||
DB_PASSWORD=einSicheresPasswort123
|
||||
|
||||
# JWT-Secret — langer zufälliger String, mindestens 32 Zeichen
|
||||
# Tipp: openssl rand -hex 32
|
||||
JWT_SECRET=hierEinenLangenZufaelligenStringEintragen
|
||||
|
||||
# API-Key — frei wählbar, wird vom Frontend und den Agents verwendet
|
||||
# Tipp: openssl rand -hex 16
|
||||
API_KEY=hierEinenApiKeyEintragen
|
||||
```
|
||||
|
||||
> **Wichtig:** `BACKEND_HOST` ist die IP-Adresse oder der Hostname des Servers auf dem
|
||||
> Docker läuft — nicht `localhost`. Der Browser muss diese Adresse erreichen können,
|
||||
> um mit dem Backend zu kommunizieren.
|
||||
|
||||
---
|
||||
|
||||
### Schritt 3 — Starten
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Beim ersten Start lädt Docker die benötigten Basis-Images (postgres, nginx etc.) und
|
||||
baut dann das Backend und das Frontend. Das dauert beim ersten Mal 3-5 Minuten.
|
||||
|
||||
Danach läuft das System und startet automatisch nach einem Server-Reboot neu.
|
||||
|
||||
---
|
||||
|
||||
### Schritt 4 — Prüfen ob alles läuft
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
Die Ausgabe sollte so aussehen:
|
||||
|
||||
```
|
||||
NAME STATUS PORTS
|
||||
rmm-db-1 Up (healthy) 5432/tcp
|
||||
rmm-backend-1 Up 0.0.0.0:8443->8443/tcp
|
||||
rmm-frontend-1 Up 0.0.0.0:80->80/tcp
|
||||
```
|
||||
|
||||
Alle drei Services müssen `Up` zeigen. Wenn `db` noch `starting` zeigt, warte
|
||||
20-30 Sekunden — die Datenbank braucht beim allerersten Start etwas länger.
|
||||
|
||||
---
|
||||
|
||||
### Schritt 5 — Erster Login
|
||||
|
||||
Rufe das Frontend im Browser auf:
|
||||
|
||||
```
|
||||
http://<BACKEND_HOST>
|
||||
```
|
||||
|
||||
Beim ersten Start legt das Backend automatisch einen Admin-Account an.
|
||||
Die Zugangsdaten stehen im Backend-Log:
|
||||
|
||||
```bash
|
||||
docker compose logs backend | grep -i "admin\|password\|passwort"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Häufige Aufgaben
|
||||
|
||||
### Logs anschauen
|
||||
|
||||
```bash
|
||||
# Alle Services
|
||||
docker compose logs -f
|
||||
|
||||
# Nur Backend
|
||||
docker compose logs -f backend
|
||||
|
||||
# Nur die letzten 50 Zeilen
|
||||
docker compose logs --tail=50 backend
|
||||
```
|
||||
|
||||
### System stoppen und neu starten
|
||||
|
||||
```bash
|
||||
# Stoppen (Daten bleiben erhalten)
|
||||
docker compose down
|
||||
|
||||
# Starten
|
||||
docker compose up -d
|
||||
|
||||
# Neu starten (z.B. nach Konfigurationsänderung)
|
||||
docker compose restart backend
|
||||
```
|
||||
|
||||
### System aktualisieren
|
||||
|
||||
Nach einem `git pull` musst du die Images neu bauen:
|
||||
|
||||
```bash
|
||||
git pull
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Konfiguration ändern (`.env` wurde bearbeitet)
|
||||
|
||||
```bash
|
||||
# Nach Änderung von BACKEND_HOST, API_KEY etc.:
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Docker Compose erkennt automatisch, dass sich die ENV-Werte geändert haben
|
||||
und startet die betroffenen Container neu.
|
||||
|
||||
---
|
||||
|
||||
## Datensicherung
|
||||
|
||||
Die Datenbank liegt in einem Docker Volume (`pgdata`). Um ein Backup zu erstellen:
|
||||
|
||||
```bash
|
||||
# Backup erstellen
|
||||
docker compose exec db pg_dump -U rmm rmm > rmm-backup-$(date +%Y%m%d).sql
|
||||
|
||||
# Backup wiederherstellen
|
||||
docker compose exec -T db psql -U rmm rmm < rmm-backup-20260101.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Backend startet nicht
|
||||
|
||||
```bash
|
||||
docker compose logs backend
|
||||
```
|
||||
|
||||
Häufige Ursachen:
|
||||
- **Datenbank noch nicht bereit**: Warte 30 Sekunden, dann `docker compose restart backend`
|
||||
- **Port 8443 belegt**: Prüfe mit `ss -tlnp | grep 8443` was den Port belegt
|
||||
|
||||
### Frontend zeigt „Verbindung fehlgeschlagen"
|
||||
|
||||
- Prüfe ob `BACKEND_HOST` in `.env` die richtige IP-Adresse ist
|
||||
- Prüfe ob Port 8443 in der Firewall freigegeben ist
|
||||
- Teste direkt: `curl -sk https://<BACKEND_HOST>:8443/api/v1/health`
|
||||
|
||||
### Datenbank-Volume zurücksetzen (Achtung: löscht alle Daten)
|
||||
|
||||
```bash
|
||||
docker compose down -v # -v löscht auch Volumes
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ports
|
||||
|
||||
| Port | Service | Beschreibung |
|
||||
|------|---------|-------------|
|
||||
| 80 | Frontend | Web-Oberfläche (Browser) |
|
||||
| 8443 | Backend | REST-API + WebSocket (Agents + Frontend) |
|
||||
|
||||
Port 80 und 8443 müssen von deinem Netzwerk erreichbar sein.
|
||||
Port 5432 (Datenbank) ist nur intern verfügbar.
|
||||
@ -51,7 +51,7 @@ Dann `config.js` anpassen:
|
||||
|
||||
```js
|
||||
// frontend/src/config.js
|
||||
export const BACKEND_HOST = '192.168.85.13' // IP des Backend-Servers (für direkte Links, z.B. Tunnel-Ports)
|
||||
export const BACKEND_HOST = '<BACKEND-IP>' // IP des Backend-Servers (für direkte Links, z.B. Tunnel-Ports)
|
||||
export const BACKEND_URL = `https://${BACKEND_HOST}:8443`
|
||||
export const API_KEY = 'DEIN_API_KEY' // aus backend/config.yaml → api_keys[0]
|
||||
```
|
||||
@ -66,7 +66,7 @@ Für die **lokale Entwicklung** (`npm run dev`) muss zusätzlich `vite.config.js
|
||||
// vite.config.js → server.proxy
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://192.168.85.13:8443', // Backend-IP anpassen
|
||||
target: 'https://<BACKEND-IP>:8443', // Backend-IP anpassen
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true, // WebSocket-Proxy aktivieren (Web Terminal, Agent-WS)
|
||||
@ -127,7 +127,7 @@ server {
|
||||
location /api/ {
|
||||
client_max_body_size 50m;
|
||||
|
||||
proxy_pass https://192.168.85.13:8443;
|
||||
proxy_pass https://<BACKEND-IP>:8443;
|
||||
proxy_ssl_verify off;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
@ -166,7 +166,7 @@ rm -f /etc/nginx/sites-enabled/default
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
> **Backend-IP anpassen:** `192.168.85.13:8443` durch die tatsächliche Backend-Adresse ersetzen.
|
||||
> **Backend-IP anpassen:** `<BACKEND-IP>:8443` durch die tatsächliche Backend-Adresse ersetzen.
|
||||
|
||||
---
|
||||
|
||||
@ -228,7 +228,7 @@ API-Calls werden per Proxy an das Backend weitergeleitet — `vite.config.js` an
|
||||
// vite.config.js
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://192.168.85.13:8443', // Backend-IP anpassen
|
||||
target: 'https://<BACKEND-IP>:8443', // Backend-IP anpassen
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true, // WebSocket-Proxy (Web Terminal, Agent-WS)
|
||||
|
||||
@ -8,14 +8,28 @@ Einrichtung von PostgreSQL mit TimescaleDB auf dem RMM-Backend-Server (Debian 13
|
||||
|
||||
## 1. PostgreSQL installieren
|
||||
|
||||
> **Wichtig für Debian 13 (Trixie):** Das Standard-Debian-Repository liefert PostgreSQL 17.8,
|
||||
> TimescaleDB benötigt aber mindestens 17.9. Deshalb muss zuerst das **offizielle
|
||||
> PostgreSQL-Apt-Repository** eingerichtet werden — das liefert immer die aktuelle Version.
|
||||
|
||||
```bash
|
||||
apt update
|
||||
apt install -y postgresql postgresql-contrib postgresql-common
|
||||
apt install -y curl gnupg
|
||||
|
||||
# Offizielles PostgreSQL-Repository einrichten
|
||||
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
|
||||
| gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg
|
||||
|
||||
echo "deb https://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" \
|
||||
> /etc/apt/sources.list.d/pgdg.list
|
||||
|
||||
apt update
|
||||
apt install -y postgresql-17
|
||||
|
||||
systemctl enable postgresql
|
||||
systemctl start postgresql
|
||||
|
||||
# Version prüfen (Trixie liefert PG17)
|
||||
# Version prüfen — muss 17.9 oder neuer zeigen
|
||||
psql --version
|
||||
```
|
||||
|
||||
@ -24,17 +38,15 @@ psql --version
|
||||
## 2. TimescaleDB installieren
|
||||
|
||||
```bash
|
||||
# Repository einrichten
|
||||
apt install -y curl gnupg
|
||||
# TimescaleDB-Repository einrichten
|
||||
echo "deb https://packagecloud.io/timescale/timescaledb/debian/ bookworm main" \
|
||||
> /etc/apt/sources.list.d/timescaledb.list
|
||||
curl -sL https://packagecloud.io/timescale/timescaledb/gpgkey | \
|
||||
gpg --dearmor > /etc/apt/trusted.gpg.d/timescaledb.gpg
|
||||
curl -sL https://packagecloud.io/timescale/timescaledb/gpgkey \
|
||||
| gpg --dearmor > /etc/apt/trusted.gpg.d/timescaledb.gpg
|
||||
apt update
|
||||
|
||||
# Passende Version zu PG17 installieren
|
||||
apt install -y timescaledb-2-loader-postgresql-17=2.25.1~debian12-1708 \
|
||||
timescaledb-2-postgresql-17=2.25.1~debian12-1708
|
||||
# TimescaleDB für PostgreSQL 17 installieren
|
||||
apt install -y timescaledb-2-postgresql-17
|
||||
|
||||
# In postgresql.conf aktivieren
|
||||
sed -i "s/#shared_preload_libraries = ''/shared_preload_libraries = 'timescaledb'/" \
|
||||
@ -43,6 +55,14 @@ sed -i "s/#shared_preload_libraries = ''/shared_preload_libraries = 'timescaledb
|
||||
systemctl restart postgresql
|
||||
```
|
||||
|
||||
> **Falls der Install mit einem Abhängigkeitsfehler fehlschlägt** (`postgresql-17 >= 17.9 required`):
|
||||
> Das bedeutet, dass noch das Debian-eigene PostgreSQL 17.8 installiert ist.
|
||||
> Lösung:
|
||||
> ```bash
|
||||
> apt remove -y postgresql* && apt autoremove -y
|
||||
> # Dann Schritt 1 (PostgreSQL aus dem pgdg-Repo) wiederholen
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
## 3. Datenbank und User anlegen
|
||||
|
||||
11
frontend/.env.example
Normal file
11
frontend/.env.example
Normal file
@ -0,0 +1,11 @@
|
||||
# RMM Frontend Konfiguration
|
||||
# Kopiere diese Datei nach .env und trage deine Werte ein
|
||||
|
||||
# IP/Hostname des RMM Backends (ohne https://, ohne Port)
|
||||
VITE_BACKEND_HOST=your-backend-host
|
||||
|
||||
# Port des RMM Backends
|
||||
VITE_BACKEND_PORT=8443
|
||||
|
||||
# API-Key fuer den Frontend-Zugriff (aus den Backend-Einstellungen)
|
||||
VITE_API_KEY=your-api-key-here
|
||||
26
frontend/Dockerfile
Normal file
26
frontend/Dockerfile
Normal file
@ -0,0 +1,26 @@
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
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
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
# Konfiguration via ENV zur Laufzeit (kein Rebuild bei IP-Aenderung noetig)
|
||||
ENV BACKEND_HOST=localhost \
|
||||
BACKEND_PORT=8443 \
|
||||
API_KEY=""
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
@ -265,7 +265,7 @@ DELETE /api/v1/agents/{id}/wireguard/peers/{uuid} Peer loeschen
|
||||
# Peer anlegen (Auto: Keypair, naechste freie IP, Endpoint)
|
||||
curl -sk -H "Authorization: Bearer <TOKEN>" -X POST \
|
||||
https://your-backend:8443/api/v1/agents/6beb8dfd.../wireguard/peers \
|
||||
-d '{"name":"VPN_MUELLER","allowed_ips":"0.0.0.0/0","dns":"10.172.100.210"}'
|
||||
-d '{"name":"VPN_MUELLER","allowed_ips":"0.0.0.0/0","dns":"<DNS-SERVER>"}'
|
||||
# → {peer_config: "[Interface]\nPrivateKey=...\n[Peer]\n...", ...}
|
||||
|
||||
# Alle Peers
|
||||
|
||||
13
frontend/docker-entrypoint.sh
Normal file
13
frontend/docker-entrypoint.sh
Normal file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Generiert /usr/share/nginx/html/runtime-config.js aus ENV-Variablen
|
||||
# Wird beim Container-Start ausgefuehrt
|
||||
|
||||
cat > /usr/share/nginx/html/runtime-config.js << EOF
|
||||
window.__RMM_CONFIG__ = {
|
||||
backendHost: "${BACKEND_HOST:-localhost}",
|
||||
backendPort: "${BACKEND_PORT:-8443}",
|
||||
apiKey: "${API_KEY:-}"
|
||||
};
|
||||
EOF
|
||||
|
||||
exec nginx -g "daemon off;"
|
||||
@ -1,6 +1,8 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Runtime-Konfiguration (Docker): wird von nginx aus ENV-Vars generiert -->
|
||||
<script src="/runtime-config.js" onerror="window.__RMM_CONFIG__={}"></script>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
21
frontend/nginx.conf
Normal file
21
frontend/nginx.conf
Normal 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";
|
||||
}
|
||||
}
|
||||
3
frontend/public/runtime-config.js
Normal file
3
frontend/public/runtime-config.js
Normal file
@ -0,0 +1,3 @@
|
||||
// Lokaler Entwicklungs-Fallback — in Docker wird diese Datei durch
|
||||
// docker-entrypoint.sh mit echten Werten ueberschrieben
|
||||
window.__RMM_CONFIG__ = {};
|
||||
@ -1178,7 +1178,7 @@ function WireGuardTab({ agentId, sys }) {
|
||||
const [peers, setPeers] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [adding, setAdding] = useState(false)
|
||||
const [newPeer, setNewPeer] = useState({ name: '', dns: '10.172.100.210, 10.172.100.220' })
|
||||
const [newPeer, setNewPeer] = useState({ name: '', dns: '' })
|
||||
const [result, setResult] = useState(null)
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
@ -1213,7 +1213,7 @@ function WireGuardTab({ agentId, sys }) {
|
||||
})
|
||||
setResult(resp?.data || resp)
|
||||
setAdding(false)
|
||||
setNewPeer({ name: '', dns: '10.172.100.210, 10.172.100.220' })
|
||||
setNewPeer({ name: '', dns: '' })
|
||||
loadPeers()
|
||||
} catch (e) {
|
||||
setError(e.message)
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// Frontend-Konfiguration — VOR dem Build anpassen, dann als config.js speichern
|
||||
// config.js ist in .gitignore und wird NICHT eingecheckt
|
||||
// Frontend-Konfiguration — wird automatisch aus .env gelesen
|
||||
// Kopiere frontend/.env.example → frontend/.env und trage deine Werte ein
|
||||
// config.js und .env sind in .gitignore und werden NICHT eingecheckt
|
||||
|
||||
export const BACKEND_HOST = 'BACKEND_IP_ODER_HOSTNAME'
|
||||
export const BACKEND_URL = `https://${BACKEND_HOST}:8443`
|
||||
export const API_KEY = 'DEIN_API_KEY'
|
||||
export const BACKEND_HOST = import.meta.env.VITE_BACKEND_HOST || 'localhost'
|
||||
export const BACKEND_PORT = import.meta.env.VITE_BACKEND_PORT || '8443'
|
||||
export const BACKEND_URL = `https://${BACKEND_HOST}:${BACKEND_PORT}`
|
||||
export const API_KEY = import.meta.env.VITE_API_KEY || ''
|
||||
|
||||
@ -556,7 +556,7 @@ function SystemSettingsSection() {
|
||||
type="text"
|
||||
value={form.backend_url_internal || ''}
|
||||
onChange={(e) => setForm({ ...form, backend_url_internal: e.target.value })}
|
||||
placeholder="https://192.168.85.13:8443"
|
||||
placeholder="https://<BACKEND-IP>:8443"
|
||||
className="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-white text-sm focus:outline-none focus:border-orange-500 mt-1"
|
||||
/>
|
||||
<div className="text-[10px] text-gray-600 mt-0.5">Fuer Tunnel-Zugriff und lokale Verbindungen</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user