feat(deploy): Standalone-Compose mit Host-Netzwerk für LXC und Traefik auf anderem Server

- docker-compose.standalone.yml: Build und Betrieb im Host-Netzwerk (Docker in unprivilegiertem
  LXC hat kein NAT für Bridge-Netze), Dienste über 127.0.0.1, Postgres nur lokal
- TRUST_PROXY akzeptiert die IP(s) des Reverse Proxys statt nur true/false
- Doku: Traefik-Datei-Konfiguration, Proxmox-Firewall, Ressourcen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
lucashahmannandClaude Opus 5.5 committed 2026-10-13 10:53:06 +02:00
1 parent d45d1821df
commit 9cb9531e78
5 files changed
+209 -6

No files matched your search

+10 -5
View File
@@ -1,9 +1,5 @@
import { z } from 'zod';
const bool = z
.enum(['true', 'false', '1', '0'])
.transform((v) => v === 'true' || v === '1');
const envSchema = z.object({
NODE_ENV: z.string().default('production'),
DATABASE_URL: z.string({ required_error: 'DATABASE_URL is required' }).min(1, 'DATABASE_URL is required'),
@@ -12,7 +8,16 @@ const envSchema = z.object({
PUBLIC_ORIGIN: z.string().url().optional(),
INGEST_HMAC_SECRET: z.string().min(32, 'INGEST_HMAC_SECRET must be at least 32 characters'),
SERVER_ID: z.string().default('atm10'),
TRUST_PROXY: bool.default('true'),
/** "true", "false" or the reverse proxy address(es)/CIDR, comma-separated: only they may set X-Forwarded-For. */
TRUST_PROXY: z
.string()
.default('true')
.transform((v): boolean | string[] => {
const s = v.trim().toLowerCase();
if (s === 'true' || s === '1') return true;
if (s === 'false' || s === '0' || s === '') return false;
return v.split(',').map((x) => x.trim()).filter(Boolean);
}),
SFTP_HOST: z.string().optional(),
SFTP_PORT: z.coerce.number().int().default(22),
SFTP_USER: z.string().optional(),
+12
View File
@@ -27,3 +27,15 @@ describe('config', () => {
expect(c.sftp).toBeNull();
});
});
describe('TRUST_PROXY', () => {
const base = { DATABASE_URL: 'postgres://x', INGEST_HMAC_SECRET: 'x'.repeat(32) };
it('accepts true/false', () => {
expect(loadConfig({ ...base, TRUST_PROXY: 'true' }).TRUST_PROXY).toBe(true);
expect(loadConfig({ ...base, TRUST_PROXY: 'false' }).TRUST_PROXY).toBe(false);
});
it('accepts the reverse proxy address(es), so only those may set X-Forwarded-For', () => {
expect(loadConfig({ ...base, TRUST_PROXY: '172.16.10.20' }).TRUST_PROXY).toEqual(['172.16.10.20']);
expect(loadConfig({ ...base, TRUST_PROXY: '172.16.10.20, 10.0.0.0/8' }).TRUST_PROXY).toEqual(['172.16.10.20', '10.0.0.0/8']);
});
});