From b201d3a13e59da09b8a36d6a245c79ecf935e54b Mon Sep 17 00:00:00 2001 From: Daniel Mann Date: Thu, 19 Mar 2026 14:24:12 +0000 Subject: [PATCH] Initial: Traefik + cloudflared + Gitea auf dama.casa --- .env.example | 14 ++++ .gitignore | 15 ++++ Makefile | 52 ++++++++++++ README.md | 79 +++++++++++++++++++ services/cloudflared/.env.example | 1 + services/cloudflared/config/config.yaml | 9 +++ services/cloudflared/docker-compose.yaml | 15 ++++ services/gitea/docker-compose.yaml | 31 ++++++++ services/traefik/.env.example | 1 + .../traefik/config/dynamic/middlewares.yaml | 20 +++++ services/traefik/config/traefik.yaml.template | 43 ++++++++++ services/traefik/docker-compose.yaml | 31 ++++++++ 12 files changed, 311 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 services/cloudflared/.env.example create mode 100644 services/cloudflared/config/config.yaml create mode 100644 services/cloudflared/docker-compose.yaml create mode 100644 services/gitea/docker-compose.yaml create mode 100644 services/traefik/.env.example create mode 100644 services/traefik/config/dynamic/middlewares.yaml create mode 100644 services/traefik/config/traefik.yaml.template create mode 100644 services/traefik/docker-compose.yaml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..eacb4c7 --- /dev/null +++ b/.env.example @@ -0,0 +1,14 @@ +# Aktive Domain – für Umstieg: home2.dnlm.de → home.dnlm.de ändern +# Danach: make setup && make all-up +DOMAIN=dama.casa + +# Cloudflare API Token (Zone:DNS:Edit für dama.casa) +# Dashboard → My Profile → API Tokens → Create Token → "Edit zone DNS" Template +CF_DNS_API_TOKEN= + +# Cloudflare Tunnel Token (Zero Trust → Networks → Tunnels → Tunnel erstellen) +CLOUDFLARE_TUNNEL_TOKEN= + +# Traefik Dashboard Basic Auth +# Generieren: echo $(htpasswd -nb admin PASSWORT) | sed -e 's/\$/\$\$/g' +TRAEFIK_DASHBOARD_USER=admin:$$apr1$$... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f16da59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Secrets +.env +*.env + +# Generierte Traefik-Config (wird via `make setup` aus Template erzeugt) +services/traefik/config/traefik.yaml + +# Traefik TLS-Zertifikate +**/data/acme.json + +# Service-Daten und Credentials +**/data/ + +# Aber .env.example behalten +!.env.example diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e071739 --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +ENV_FILE := $(ROOT_DIR)/.env + +-include $(ENV_FILE) +export + +# docker compose mit Root-.env für einen Service aufrufen +define compose + docker compose --env-file $(ENV_FILE) -f $(ROOT_DIR)/services/$(1)/docker-compose.yaml +endef + +.PHONY: setup traefik-up traefik-down gitea-up gitea-down cloudflared-up cloudflared-down all-up all-down + +## Einmalige Einrichtung (nach erstem `cp .env.example .env`) +setup: + @echo "→ Docker-Netzwerk 'proxy' anlegen..." + docker network create proxy 2>/dev/null || true + @echo "→ acme.json vorbereiten..." + mkdir -p $(ROOT_DIR)/services/traefik/data + touch $(ROOT_DIR)/services/traefik/data/acme.json + chmod 600 $(ROOT_DIR)/services/traefik/data/acme.json + @echo "→ traefik.yaml aus Template generieren (DOMAIN=$(DOMAIN))..." + envsubst < $(ROOT_DIR)/services/traefik/config/traefik.yaml.template \ + > $(ROOT_DIR)/services/traefik/config/traefik.yaml + @echo "✓ Setup abgeschlossen" + +## Traefik +traefik-up: + $(call compose,traefik) up -d + +traefik-down: + $(call compose,traefik) down + +## Gitea +gitea-up: + $(call compose,gitea) up -d + +gitea-down: + $(call compose,gitea) down + +## Cloudflare Tunnel +cloudflared-up: + $(call compose,cloudflared) up -d + +cloudflared-down: + $(call compose,cloudflared) down + +## Alle Services starten (Reihenfolge: Traefik → cloudflared → Gitea) +all-up: traefik-up cloudflared-up gitea-up + +## Alle Services stoppen +all-down: gitea-down cloudflared-down traefik-down diff --git a/README.md b/README.md new file mode 100644 index 0000000..b4bbd09 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# dell01 + +Docker-Infrastruktur für Dell01 (OptiPlex 3060 Micro, Ubuntu 24.04). + +## Architektur + +``` +Internet + └── Cloudflare Edge + └── Cloudflare Tunnel (cloudflared) + └── Traefik (Wildcard-Cert via Cloudflare DNS-Challenge) + └── Alle Docker-Container +``` + +## Ersteinrichtung + +```bash +cp .env.example .env +# .env befüllen (alle Variablen, siehe Kommentare) +make setup +make all-up +``` + +### .env vorbereiten + +| Variable | Beschreibung | +|---|---| +| `DOMAIN` | Aktive Domain, z.B. `home2.dnlm.de` | +| `INWX_USERNAME` | INWX Login-Name | +| `INWX_PASSWORD` | INWX Passwort | +| `CLOUDFLARE_TUNNEL_TOKEN` | Zero Trust → Networks → Tunnels → Tunnel erstellen | +| `TRAEFIK_DASHBOARD_USER` | `echo $(htpasswd -nb admin PW) \| sed -e 's/\$/\$\$/g'` | + +### Cloudflare Tunnel konfigurieren + +Tunnel anlegen: Zero Trust → Networks → Tunnels → Token kopieren. + +Public Hostname im Dashboard: `*.${DOMAIN}` → `https://traefik:443` + +Alternativ (ohne Cloudflare-Zone): Tunnel-UUID aus dem Dashboard nehmen und +bei INWX manuell setzen: +``` +*.home2 CNAME .cfargotunnel.com +``` +Dann ingress in `services/cloudflared/config.yaml` konfigurieren (statt Dashboard). + +## Domain-Umstieg (home2 → home) + +```bash +# In .env ändern: +DOMAIN=home.dnlm.de + +make setup # traefik.yaml neu generieren +make all-down +make all-up +``` + +## Services + +| Service | URL | Compose | +|-------------|------------------------------|----------------------------------| +| Traefik | traefik.home2.dnlm.de | services/traefik/ | +| Gitea | gitea.home2.dnlm.de | services/gitea/ | +| cloudflared | – | services/cloudflared/ | + +## Neuen Service hinzufügen + +Nur Traefik-Labels in die `docker-compose.yaml`: + +```yaml +labels: + - "traefik.enable=true" + - "traefik.http.routers.SERVICENAME.rule=Host(`SERVICENAME.home2.dnlm.de`)" + - "traefik.http.routers.SERVICENAME.entrypoints=websecure" + - "traefik.http.routers.SERVICENAME.tls=true" + - "traefik.http.services.SERVICENAME.loadbalancer.server.port=PORT" +``` + +Netzwerk `proxy` als external einbinden – fertig. diff --git a/services/cloudflared/.env.example b/services/cloudflared/.env.example new file mode 100644 index 0000000..d1e37cb --- /dev/null +++ b/services/cloudflared/.env.example @@ -0,0 +1 @@ +# Alle Variablen liegen in /.env.example (Root des Repos) diff --git a/services/cloudflared/config/config.yaml b/services/cloudflared/config/config.yaml new file mode 100644 index 0000000..3244642 --- /dev/null +++ b/services/cloudflared/config/config.yaml @@ -0,0 +1,9 @@ +tunnel: fbfb9bdc-f049-4c42-9e2a-622adb05f2c8 +credentials-file: /etc/cloudflared/credentials.json + +ingress: + - hostname: "*.dama.casa" + service: https://traefik:443 + originRequest: + noTLSVerify: true + - service: http_status:404 diff --git a/services/cloudflared/docker-compose.yaml b/services/cloudflared/docker-compose.yaml new file mode 100644 index 0000000..70809fe --- /dev/null +++ b/services/cloudflared/docker-compose.yaml @@ -0,0 +1,15 @@ +services: + cloudflared: + image: cloudflare/cloudflared:latest + container_name: cloudflared + restart: unless-stopped + command: tunnel --no-autoupdate --config /etc/cloudflared/config.yaml run + volumes: + - ./config/config.yaml:/etc/cloudflared/config.yaml:ro + - ./data/credentials.json:/etc/cloudflared/credentials.json:ro + networks: + - proxy + +networks: + proxy: + external: true diff --git a/services/gitea/docker-compose.yaml b/services/gitea/docker-compose.yaml new file mode 100644 index 0000000..ae30c1f --- /dev/null +++ b/services/gitea/docker-compose.yaml @@ -0,0 +1,31 @@ +services: + gitea: + image: gitea/gitea:latest + container_name: gitea + restart: unless-stopped + environment: + - USER_UID=1000 + - USER_GID=1000 + - GITEA__database__DB_TYPE=sqlite3 + - GITEA__server__ROOT_URL=https://gitea.${DOMAIN} + - GITEA__server__DOMAIN=gitea.${DOMAIN} + - GITEA__server__HTTP_PORT=3000 + - GITEA__server__SSH_DOMAIN=gitea.${DOMAIN} + - GITEA__server__SSH_PORT=2222 + volumes: + - ./data:/data + ports: + - "2222:22" + networks: + - proxy + labels: + - "traefik.enable=true" + - "traefik.http.routers.gitea.rule=Host(`gitea.${DOMAIN}`)" + - "traefik.http.routers.gitea.entrypoints=websecure" + - "traefik.http.routers.gitea.tls=true" + - "traefik.http.routers.gitea.tls.certresolver=cloudflare" + - "traefik.http.services.gitea.loadbalancer.server.port=3000" + +networks: + proxy: + external: true diff --git a/services/traefik/.env.example b/services/traefik/.env.example new file mode 100644 index 0000000..d1e37cb --- /dev/null +++ b/services/traefik/.env.example @@ -0,0 +1 @@ +# Alle Variablen liegen in /.env.example (Root des Repos) diff --git a/services/traefik/config/dynamic/middlewares.yaml b/services/traefik/config/dynamic/middlewares.yaml new file mode 100644 index 0000000..7b03e29 --- /dev/null +++ b/services/traefik/config/dynamic/middlewares.yaml @@ -0,0 +1,20 @@ +http: + middlewares: + # Basic Auth für Traefik Dashboard + # Generieren: echo $(htpasswd -nb user password) | sed -e 's/\$/\$\$/g' + traefik-auth: + basicAuth: + usersFile: /auth/traefik-users + + # Sicherheits-Header für alle Services + secure-headers: + headers: + sslRedirect: true + forceSTSHeader: true + stsIncludeSubdomains: true + stsPreload: true + stsSeconds: 31536000 + contentTypeNosniff: true + browserXssFilter: true + referrerPolicy: "strict-origin-when-cross-origin" + customFrameOptionsValue: "SAMEORIGIN" diff --git a/services/traefik/config/traefik.yaml.template b/services/traefik/config/traefik.yaml.template new file mode 100644 index 0000000..62f5306 --- /dev/null +++ b/services/traefik/config/traefik.yaml.template @@ -0,0 +1,43 @@ +api: + dashboard: true + +entryPoints: + web: + address: ":80" + http: + redirections: + entryPoint: + to: websecure + scheme: https + websecure: + address: ":443" + http: + tls: + certResolver: cloudflare + domains: + - main: "${DOMAIN}" + sans: + - "*.${DOMAIN}" + +certificatesResolvers: + cloudflare: + acme: + email: "mail@dnlm.de" + storage: /acme.json + dnsChallenge: + provider: cloudflare + resolvers: + - "1.1.1.1:53" + - "1.0.0.1:53" + +providers: + docker: + endpoint: "unix:///var/run/docker.sock" + exposedByDefault: false + network: proxy + file: + directory: /dynamic + watch: true + +log: + level: INFO diff --git a/services/traefik/docker-compose.yaml b/services/traefik/docker-compose.yaml new file mode 100644 index 0000000..d917fa9 --- /dev/null +++ b/services/traefik/docker-compose.yaml @@ -0,0 +1,31 @@ +services: + traefik: + image: traefik:v3.2 + container_name: traefik + restart: unless-stopped + ports: + - "80:80" + - "443:443" + environment: + - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} + - DOCKER_API_VERSION=1.41 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./config/traefik.yaml:/traefik.yaml:ro + - ./config/dynamic:/dynamic:ro + - ./data/acme.json:/acme.json + - ./data/traefik-users:/auth/traefik-users:ro + networks: + - proxy + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.${DOMAIN}`)" + - "traefik.http.routers.traefik-dashboard.entrypoints=websecure" + - "traefik.http.routers.traefik-dashboard.tls=true" + - "traefik.http.routers.traefik-dashboard.tls.certresolver=cloudflare" + - "traefik.http.routers.traefik-dashboard.service=api@internal" + - "traefik.http.routers.traefik-dashboard.middlewares=traefik-auth@file" + +networks: + proxy: + external: true