Skip to content

Commit b6443f7

Browse files
committed
Initial revision.
1 parent ef2eb5c commit b6443f7

1 file changed

Lines changed: 228 additions & 0 deletions

File tree

docs/vpn-reverse-proxy-plan.md

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# ProjectForge: VPN-Betrieb mit nginx Reverse-Proxy
2+
3+
## Ziel
4+
5+
ProjectForge soll hinter ein VPN gestellt werden. Nur bestimmte Dienste bleiben ohne VPN von außen erreichbar. VPN-Nutzer greifen über den gleichen Hostnamen zu und haben vollen Zugriff.
6+
7+
## Architektur-Übersicht
8+
9+
```
10+
┌─────────────────────────┐
11+
Internet ──► nginx :443 ──┤ nur erlaubte Pfade ├──► ProjectForge (127.0.0.1:8080)
12+
│ │
13+
VPN-Netz ──► nginx :443 ──┤ alle Pfade ├──► ProjectForge (127.0.0.1:8080)
14+
└─────────────────────────┘
15+
```
16+
17+
nginx erkennt anhand der Source-IP (VPN-Subnetz), ob voller Zugriff oder nur öffentliche Pfade erlaubt sind.
18+
19+
---
20+
21+
## Öffentliche Pfade (ohne VPN erreichbar)
22+
23+
| Pfad | Dienst | Authentifizierung |
24+
|------|--------|-------------------|
25+
| `/carddav/**` | CardDAV Kontakte-Sync | HTTP Basic Auth (PF-Credentials) |
26+
| `/.well-known/carddav` | CardDAV Discovery | Redirect → `/carddav` |
27+
| `/export/ProjectForge.ics` | Kalender-Abonnements (iCal) | Verschlüsselter Token im Query-Parameter |
28+
| `/export/sms` | SMS-Gateway | API-Key (`publicRestCallAuthKey`) |
29+
| `/rsPublic/datatransfer/**` | Datentransfer für externe Partner | Access-Token + opt. Passwort |
30+
| `/rsPublic/systemStatus` | Health-Check / Monitoring | Keine (öffentlich) |
31+
| `/rsPublic/phoneLookup` | Telefon-Rückwärtssuche | API-Key (`phoneLookupKey`) |
32+
| `/rsPublic/logo.*` | Logo-Bilder für externe Seiten | Keine (öffentlich) |
33+
34+
## Nicht-öffentliche Pfade (nur VPN)
35+
36+
- `/rsPublic/login`, `/rsPublic/2FA/**`, `/rsPublic/passwordForgotten/**` – Login & Auth
37+
- `/rs/**` – Alle authentifizierten REST-Endpunkte
38+
- `/wa/**` – Wicket-Seiten (Legacy-UI)
39+
- `/react-app.html`, `/rsPublic/setup` – React-Hauptanwendung
40+
- Statische Assets (`/*.js`, `/*.css`, etc.)
41+
42+
---
43+
44+
## nginx-Konfiguration
45+
46+
### IP-Erkennung (VPN vs. Internet)
47+
48+
```nginx
49+
geo $is_vpn {
50+
default 0;
51+
10.0.0.0/8 1; # <-- VPN-Subnetz hier anpassen
52+
# 192.168.x.0/24 1; # weitere interne Netze bei Bedarf
53+
}
54+
```
55+
56+
> **Hinweis:** Falls ein vorgelagerter Load-Balancer existiert, muss stattdessen
57+
> `$http_x_forwarded_for` oder `realip_module` verwendet werden.
58+
59+
### Rate-Limiting (Brute-Force-Schutz)
60+
61+
```nginx
62+
limit_req_zone $binary_remote_addr zone=carddav:10m rate=10r/s;
63+
limit_req_zone $binary_remote_addr zone=datatransfer:10m rate=5r/s;
64+
```
65+
66+
### Server-Block
67+
68+
```nginx
69+
server {
70+
listen 443 ssl http2;
71+
server_name projectforge.example.com;
72+
73+
ssl_certificate /etc/ssl/certs/projectforge.crt;
74+
ssl_certificate_key /etc/ssl/private/projectforge.key;
75+
76+
# Gemeinsame Proxy-Header
77+
proxy_set_header Host $host;
78+
proxy_set_header X-Real-IP $remote_addr;
79+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
80+
proxy_set_header X-Forwarded-Proto $scheme;
81+
82+
# Standard Body-Size
83+
client_max_body_size 110m;
84+
85+
# ===========================================================
86+
# ÖFFENTLICHE PFADE (immer erreichbar)
87+
# ===========================================================
88+
89+
# CardDAV (inkl. WebDAV-Methoden PROPFIND, REPORT, etc.)
90+
location /carddav {
91+
limit_req zone=carddav burst=20 nodelay;
92+
proxy_pass http://127.0.0.1:8080;
93+
}
94+
95+
# CardDAV Discovery
96+
location /.well-known/carddav {
97+
return 301 /carddav;
98+
}
99+
100+
# Kalender-Abonnements (iCal)
101+
location = /export/ProjectForge.ics {
102+
proxy_pass http://127.0.0.1:8080;
103+
}
104+
105+
# SMS-Gateway
106+
location = /export/sms {
107+
proxy_pass http://127.0.0.1:8080;
108+
}
109+
110+
# Datentransfer (externes Teilen)
111+
location /rsPublic/datatransfer {
112+
limit_req zone=datatransfer burst=10 nodelay;
113+
client_max_body_size 100m;
114+
proxy_pass http://127.0.0.1:8080;
115+
}
116+
117+
# System-Status / Monitoring
118+
location = /rsPublic/systemStatus {
119+
proxy_pass http://127.0.0.1:8080;
120+
}
121+
122+
# Telefon-Lookup
123+
location = /rsPublic/phoneLookup {
124+
proxy_pass http://127.0.0.1:8080;
125+
}
126+
127+
# Logo-Bilder
128+
location ~ ^/rsPublic/logo\.(jpg|png|gif)$ {
129+
proxy_pass http://127.0.0.1:8080;
130+
}
131+
132+
# ===========================================================
133+
# ALLES ANDERE: NUR VPN
134+
# ===========================================================
135+
136+
location / {
137+
if ($is_vpn = 0) {
138+
return 403;
139+
}
140+
proxy_pass http://127.0.0.1:8080;
141+
}
142+
}
143+
144+
# HTTP → HTTPS Redirect
145+
server {
146+
listen 80;
147+
server_name projectforge.example.com;
148+
return 301 https://$host$request_uri;
149+
}
150+
```
151+
152+
---
153+
154+
## ProjectForge-Konfiguration
155+
156+
In `/home/projectforge/ProjectForge/projectforge.properties`:
157+
158+
```properties
159+
# Nur auf localhost binden (nur über nginx erreichbar)
160+
server.address=127.0.0.1
161+
162+
# Domain auf die öffentliche URL setzen (für E-Mail-Links etc.)
163+
projectforge.domain=https://projectforge.example.com
164+
```
165+
166+
**Keine Code-Änderungen in ProjectForge erforderlich.**
167+
168+
---
169+
170+
## Voraussetzungen
171+
172+
- [ ] VPN-Infrastruktur vorhanden (WireGuard, OpenVPN, etc.)
173+
- [ ] VPN-Subnetz bekannt (für `geo`-Block)
174+
- [ ] TLS-Zertifikat für `projectforge.example.com`
175+
- [ ] nginx installiert und als Systemd-Service konfiguriert
176+
- [ ] Firewall: Port 443 offen, Port 8080 nur von localhost
177+
178+
---
179+
180+
## Härtung (empfohlen)
181+
182+
| Maßnahme | Beschreibung |
183+
|----------|--------------|
184+
| Rate-Limiting | Siehe oben (CardDAV, Datentransfer) |
185+
| Fail2Ban | Log-Parsing für fehlgeschlagene Basic-Auth-Versuche auf `/carddav` |
186+
| TLS-Härtung | TLS 1.2+, starke Cipher-Suites, HSTS |
187+
| IP-Allowlist für SMS | `/export/sms` optional auf bekannte IPs einschränken |
188+
| Monitoring | `/rsPublic/systemStatus` als Health-Endpoint für Uptime-Monitoring nutzen |
189+
190+
---
191+
192+
## Testplan
193+
194+
### Von außerhalb des VPN:
195+
196+
```bash
197+
# Muss blockiert werden (403):
198+
curl -s -o /dev/null -w "%{http_code}" https://projectforge.example.com/
199+
curl -s -o /dev/null -w "%{http_code}" https://projectforge.example.com/rsPublic/login
200+
201+
# Muss funktionieren (200/301):
202+
curl -s -o /dev/null -w "%{http_code}" https://projectforge.example.com/rsPublic/systemStatus
203+
curl -s -o /dev/null -w "%{http_code}" https://projectforge.example.com/.well-known/carddav
204+
curl -s -o /dev/null -w "%{http_code}" https://projectforge.example.com/export/ProjectForge.ics?q=TEST
205+
206+
# CardDAV-Client (macOS/iOS Kontakte) verbinden → muss funktionieren
207+
# Datentransfer-Link öffnen → muss funktionieren
208+
```
209+
210+
### Aus dem VPN:
211+
212+
```bash
213+
# Alles muss funktionieren:
214+
curl -s -o /dev/null -w "%{http_code}" https://projectforge.example.com/
215+
curl -s -o /dev/null -w "%{http_code}" https://projectforge.example.com/rsPublic/login
216+
217+
# Vollständiger Login und Nutzung der Anwendung testen
218+
```
219+
220+
---
221+
222+
## Offene Punkte zur Klärung
223+
224+
1. **VPN-Subnetz:** Welches Subnetz verwenden die VPN-Clients? (für `geo`-Block)
225+
2. **TLS-Zertifikat:** Let's Encrypt (Certbot) oder internes CA?
226+
3. **Bestehende nginx-Instanz:** Gibt es bereits einen nginx auf dem Server oder muss einer aufgesetzt werden?
227+
4. **Load-Balancer:** Sitzt ein weiterer Proxy/LB vor nginx? (beeinflusst IP-Erkennung)
228+
5. **Fail2Ban:** Bereits im Einsatz? Dann kann CardDAV-Jail ergänzt werden.

0 commit comments

Comments
 (0)