16 Commits

Author SHA1 Message Date
9650bd8b3c added tests and readme for eduroam initial smoketest 2026-01-19 18:35:51 +01:00
33112f6142 Add dev backend compose and nginx config 2026-01-14 19:55:36 +01:00
fc00c3f627 Add education-flagger submodule 2026-01-14 19:55:25 +01:00
436a81e2a8 chore: add Makefile for proxy/app lifecycle commands 2025-09-15 19:38:57 +02:00
fa6780d032 chore: update .gitignore for infra secrets and acme.json 2025-09-15 19:38:57 +02:00
19d41f3041 docs(infra): document new infra v2 structure and usage 2025-09-15 19:38:57 +02:00
a32e2da6c3 chore(infra): add env templates and bootstrap script; ignore secrets in git 2025-09-15 19:38:57 +02:00
062b30e379 feat(infra): add Nextcloud stack with MariaDB and Redis, Traefik labels 2025-09-15 19:38:57 +02:00
b186c22bf2 chore(infra): scaffold Traefik core (static+dynamic config, compose) 2025-09-15 19:38:57 +02:00
61853ac2cd update repo dependency 2025-03-19 14:42:56 +01:00
56e6a53f0a add backend overwrite compose file for laravel and sql 2025-03-19 14:41:47 +01:00
62a96dc243 improve kirby 2025-03-13 19:24:28 +01:00
d4f202f204 add readme to env folder 2025-03-05 15:21:52 +01:00
49badb74a7 different script approaches to start the app 2025-03-05 15:20:01 +01:00
71d080a87e added some fallback values 2025-03-05 15:19:42 +01:00
138525835d move adminer to develop 2025-03-05 15:11:14 +01:00
42 changed files with 1079 additions and 228 deletions

4
.gitignore vendored
View File

@@ -4,3 +4,7 @@ apps/proxy
apps/administration/*
apps/tools/app/*
env/secrets.env
infra/core/traefik/data/acme.json
infra/**/.env
infra/**/*.env.local
infra/secrets/*

3
.gitmodules vendored
View File

@@ -7,3 +7,6 @@
[submodule "apps/tools/invoiceninja/dockerfiles"]
path = apps/tools/invoiceninja/dockerfiles
url = https://github.com/invoiceninja/dockerfiles.git
[submodule "apps/security/Eduroam Analyzer/asn-updater"]
path = apps/security/Eduroam Analyzer/asn-updater
url = https://gitea.mindboost.team/mindboost/education-flagger.git

42
Makefile Normal file
View File

@@ -0,0 +1,42 @@
SHELL := /bin/bash
# Environment selection
ENV ?= development
COMMON_ENV := infra/env/$(ENV)/common.env
# Helper to pass env files if present
define with_env
$(foreach f,$(1),$(if $(wildcard $(f)),--env-file $(f),))
endef
.PHONY: bootstrap proxy-up proxy-down proxy-logs app-up app-down app-logs ps
bootstrap:
@bash scripts/infra/bootstrap.sh
proxy-up:
@docker compose -f infra/core/traefik/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/traefik/.env) up -d
proxy-down:
@docker compose -f infra/core/traefik/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/traefik/.env) down
proxy-logs:
@docker compose -f infra/core/traefik/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/traefik/.env) logs -f
# Usage: make app-up APP=nextcloud
APP ?=
app-up:
@test -n "$(APP)" || (echo "APP not set. Example: make app-up APP=nextcloud" && exit 1)
@docker compose -f infra/apps/$(APP)/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/$(APP)/.env) up -d
app-down:
@test -n "$(APP)" || (echo "APP not set. Example: make app-down APP=nextcloud" && exit 1)
@docker compose -f infra/apps/$(APP)/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/$(APP)/.env) down
app-logs:
@test -n "$(APP)" || (echo "APP not set. Example: make app-logs APP=nextcloud" && exit 1)
@docker compose -f infra/apps/$(APP)/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/$(APP)/.env) logs -f
ps:
@docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Networks}}'

View File

@@ -2,6 +2,30 @@
All the software used and hosted by mindboost organized in containers.
## New Infra (v2) Overview
This repo now includes a modular, bestpractice infrastructure under `infra/` to make replication and selective deployment easy. It is centered on Traefik as the reverse proxy with automatic TLS via Let's Encrypt, environment layering, and pickwhatyouneed application stacks.
- Core: `infra/core/traefik` — Traefik with HTTPS (ACME), dashboard, and sane defaults
- Apps: `infra/apps/<service>` — selfcontained stacks (e.g., `nextcloud`)
- Env: `infra/env/<environment>/common.env` — environment defaults (dev/prod)
- Secrets: `infra/secrets/` — local secret storage (ignored by git)
- Make targets: toplevel `Makefile` to bootstrap, start proxy, and start apps
Quickstart
- Copy `infra/env/development/common.env` and adjust domains and ACME email.
- Create the shared proxy network and ACME storage: `make bootstrap`
- Start Traefik: `make proxy-up`
- Start a service, e.g. Nextcloud: `make app-up APP=nextcloud`
Notes
- Traefik dashboard is exposed at `TRAEFIK_DASHBOARD_DOMAIN` with optional basic auth.
- Services connect to an external `proxy` network for routing, plus their own internal network.
- Each app has its own `.env.example`; copy to `.env` and adjust.
- The legacy `apps/` structure remains as-is; new infra is additive and can coexist.
## Project Structure
./apps/
@@ -128,12 +152,6 @@ Our infrastructure uses a two-tier network model to enhance security and isolate
- These networks are not directly accessible from the internet and provide secure communication between public and internal services.
- Examples: backend_network, database_network, etc.
Service Network Configuration:
- Frontend: Connected to proxy_network and backend_network
- Backend API: Connected to backend_network and database_network
- Database: Connected only to database_network
- Traefik: Connected only to proxy_network
This structure ensures that:
- The proxy (Traefik) can route traffic to public-facing services.
- Internal services (like databases) are not directly accessible from the proxy network.
@@ -163,7 +181,7 @@ The `volumes/` folder contains subdirectories for different volumes used by vari
Each subdirectory corresponds to a specific service or group of services, containing the persistent data that needs to be preserved across container restarts or redeployments.
When configuring Docker Compose files, reference these volume paths to ensure data persistence. For example:
When configuring Docker Compose files, reference these volume paths to ensure data persistence.
```yaml
volumes:
@@ -193,4 +211,4 @@ These scripts can be run from the command line to perform various tasks related
To use a script, navigate to the scripts directory and run:
```bash
./script-name.sh
./script-name.sh

View File

@@ -43,6 +43,6 @@ services:
volumes:
backend_redis_data:
driver: local
name: "${INFRASTRUCTURE_LABEL}_backend_redis_data"
name: "${INFRASTRUCTURE_LABEL:-default}_backend_redis_data"

View File

@@ -0,0 +1,8 @@
services:
adminer:
profiles: ["all", "database", "backend", "adminer", "app"]
image: adminer
container_name: ${INFRASTRUCTURE_LABEL:-default}-adminer-${ENVIRONMENT:-development}
restart: always
ports:
- ${ADMINER_PORT:-0}:8080

View File

@@ -0,0 +1,20 @@
services:
adminer:
profiles: ["all", "database", "backend", "adminer", "app"]
image: adminer
container_name: ${INFRASTRUCTURE_LABEL:-default}-adminer-${ENVIRONMENT:-development}
restart: always
ports:
- ${ADMINER_PORT:-0}:8080
networks:
- database
- proxy
labels:
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.rule=Host(`${ADMINER_DOMAIN:-adminer.local}`)"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.tls=true"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-http_resolver}"
- 'traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.service=adminer'
- "traefik.http.adminer.cloud.loadbalancer.server.port=8080"
- "traefik.docker.network=${TRAEFIK_NETWORK:-default}"

View File

@@ -11,7 +11,7 @@ services:
depends_on:
- gitea_db
labels:
- "traefik.enable=${TRAEFIK_ENABLE}"
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.gitea.entrypoints=${TRAEFIK_ENTRYPOINT}"
- "traefik.http.routers.gitea.rule=(Host(`${GITEA_DOMAIN})`)"
- "traefik.http.routers.gitea.tls=true"

View File

@@ -0,0 +1,11 @@
# MaxMind (create a free GeoLite2 license key in your MaxMind account)
MAXMIND_LICENSE_KEY=your_maxmind_license_key
# PeeringDB (optional; reduces rate limits)
PDB_API_KEY=your_peeringdb_api_key
# existing Traefik/proxy network name (must already exist)
PROXY_NETWORK=proxy
# update interval in seconds (30 days)
UPDATE_INTERVAL_SECONDS=2592000

View File

@@ -0,0 +1 @@
.env

View File

@@ -0,0 +1,16 @@
FROM golang:1.22-alpine AS build
WORKDIR /src
COPY go.mod ./
RUN go mod download
COPY main.go ./
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/asn-header-service
FROM alpine:3.20
RUN adduser -D -H -u 10001 app
USER 10001
WORKDIR /app
COPY --from=build /out/asn-header-service /app/asn-header-service
EXPOSE 8080
ENV ADDR=:8080
ENTRYPOINT ["/app/asn-header-service"]

View File

@@ -0,0 +1,89 @@
# NREN / ASN Detection Service
Dieses Projekt stellt einen **minimalen Microservice** bereit, um **Hochschul- und Forschungsnetzwerke (NRENs)** anhand der **Autonomous System Number (ASN)** zu erkennen.
Der Zweck ist es, **Anfragen aus Hochschulnetzen (z. B. eduroam)** zu identifizieren, um **Research-bezogene Services kostenlos oder bevorzugt bereitzustellen**.
Das System dient ausschließlich der **Netzwerk-Klassifikation** und **ersetzt keine Authentifizierung**.
---
## Ziel
- Erkennen, ob eine Anfrage aus einem **Hochschul- oder Forschungsnetz** stammt
- Bereitstellung eines **Header-Hinweises** für nachgelagerte Services
- Grundlage für Entscheidungen wie:
- kostenfreie Research-Features
- angepasste UI-Hinweise
- alternative Rate-Limits
---
## Funktionsweise (Kurzfassung)
```
Client
→ Traefik
→ ForwardAuth
→ ASN Detection Service
→ Header wird ergänzt
```
1. Die Client-IP wird ermittelt
2. Die zugehörige ASN wird lokal nachgeschlagen
3. Die ASN wird mit einer NREN-ASN-Liste verglichen
4. Das Ergebnis wird als HTTP-Header zurückgegeben
---
## Datenquellen
- **GeoLite2 ASN (MaxMind)**
- kostenlos
- lokal
- monatliche Aktualisierung
- **NREN-ASN-Liste**
- abgeleitet aus PeeringDB
- Kategorie: `Research and Education`
- monatliche Aktualisierung
---
## Bereitgestellte Header
| Header | Beschreibung |
|------|-------------|
| `X-ASN` | ASN der Client-IP |
| `X-ASN-ORG` | Organisation (optional) |
| `X-NREN` | `1` wenn ASN zu einem Hochschul-/Forschungsnetz gehört, sonst `0` |
---
## Integration
Der Service wird als **Traefik ForwardAuth Middleware** eingebunden.
Die Header werden über `authResponseHeaders` an die eigentliche Anwendung weitergereicht.
Der Service ist **nicht öffentlich exponiert** und kommuniziert ausschließlich über das interne Docker-Netzwerk.
---
## Update-Strategie
- monatliche Aktualisierung der ASN-Daten
- keine externen Requests während der Anfrageverarbeitung
---
## Einschränkungen
- Die Erkennung ist **heuristisch**
- Es gibt **keine Garantie**, dass jede Anfrage aus einem Hochschulnetz erkannt wird
- Die Information darf **nicht als Authentifizierungsmerkmal** verwendet werden
---
## Zusammenfassung
Dieses Projekt ermöglicht eine **performante, datenschutzfreundliche Erkennung von Hochschulnetzen**, um **Research-Angebote kontextabhängig bereitzustellen**, ohne Nutzer zu identifizieren oder externe Dienste zur Laufzeit zu kontaktieren.

View File

@@ -0,0 +1,89 @@
# NREN / ASN Detection Service
Dieses Projekt stellt einen **minimalen Microservice** bereit, um **Hochschul- und Forschungsnetzwerke (NRENs)** anhand der **Autonomous System Number (ASN)** zu erkennen.
Der Zweck ist es, **Anfragen aus Hochschulnetzen (z. B. eduroam)** zu identifizieren, um **Research-bezogene Services kostenlos oder bevorzugt bereitzustellen**.
Das System dient ausschließlich der **Netzwerk-Klassifikation** und **ersetzt keine Authentifizierung**.
---
## Ziel
- Erkennen, ob eine Anfrage aus einem **Hochschul- oder Forschungsnetz** stammt
- Bereitstellung eines **Header-Hinweises** für nachgelagerte Services
- Grundlage für Entscheidungen wie:
- kostenfreie Research-Features
- angepasste UI-Hinweise
- alternative Rate-Limits
---
## Funktionsweise (Kurzfassung)
```
Client
→ Traefik
→ ForwardAuth
→ ASN Detection Service
→ Header wird ergänzt
```
1. Die Client-IP wird ermittelt
2. Die zugehörige ASN wird lokal nachgeschlagen
3. Die ASN wird mit einer NREN-ASN-Liste verglichen
4. Das Ergebnis wird als HTTP-Header zurückgegeben
---
## Datenquellen
- **GeoLite2 ASN (MaxMind)**
- kostenlos
- lokal
- monatliche Aktualisierung
- **NREN-ASN-Liste**
- abgeleitet aus PeeringDB
- Kategorie: `Research and Education`
- monatliche Aktualisierung
---
## Bereitgestellte Header
| Header | Beschreibung |
|------|-------------|
| `X-ASN` | ASN der Client-IP |
| `X-ASN-ORG` | Organisation (optional) |
| `X-NREN` | `1` wenn ASN zu einem Hochschul-/Forschungsnetz gehört, sonst `0` |
---
## Integration
Der Service wird als **Traefik ForwardAuth Middleware** eingebunden.
Die Header werden über `authResponseHeaders` an die eigentliche Anwendung weitergereicht.
Der Service ist **nicht öffentlich exponiert** und kommuniziert ausschließlich über das interne Docker-Netzwerk.
---
## Update-Strategie
- monatliche Aktualisierung der ASN-Daten
- keine externen Requests während der Anfrageverarbeitung
---
## Einschränkungen
- Die Erkennung ist **heuristisch**
- Es gibt **keine Garantie**, dass jede Anfrage aus einem Hochschulnetz erkannt wird
- Die Information darf **nicht als Authentifizierungsmerkmal** verwendet werden
---
## Zusammenfassung
Dieses Projekt ermöglicht eine **performante, datenschutzfreundliche Erkennung von Hochschulnetzen**, um **Research-Angebote kontextabhängig bereitzustellen**, ohne Nutzer zu identifizieren oder externe Dienste zur Laufzeit zu kontaktieren.

View File

@@ -0,0 +1,36 @@
services:
asn-header:
build: .
container_name: asn-header
restart: unless-stopped
env_file: .env
environment:
MMDB_PATH: /data/GeoLite2-ASN.mmdb
ASN_LIST_PATH: /data/nren_asns.txt
ADDR: ":8080"
volumes:
- asn_data:/data:ro
networks:
- proxy
asn-updater:
build: ./asn-updater
container_name: asn-updater
restart: unless-stopped
env_file: .env
environment:
OUT_DIR: /data
PDB_INFO_TYPE: "Research and Education"
INTERVAL_SECONDS: "${UPDATE_INTERVAL_SECONDS}"
volumes:
- asn_data:/data
networks:
- proxy
networks:
proxy:
external: true
name: ${PROXY_NETWORK}
volumes:
asn_data:

View File

@@ -0,0 +1,6 @@
module asn-header-service
go 1.22
require github.com/oschwald/maxminddb-golang v1.13.1

View File

@@ -0,0 +1,158 @@
package main
import (
"bufio"
"log"
"net"
"net/http"
"os"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/oschwald/maxminddb-golang"
)
type asnRecord struct {
ASN uint `maxminddb:"autonomous_system_number"`
Org string `maxminddb:"autonomous_system_organization"`
}
type server struct {
db *maxminddb.Reader
nrenASNs map[uint]struct{}
ready atomic.Bool
versionTag string
}
func loadASNSet(path string) (map[uint]struct{}, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
set := make(map[uint]struct{}, 4096)
sc := bufio.NewScanner(f)
for sc.Scan() {
line := strings.TrimSpace(sc.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
v, err := strconv.ParseUint(line, 10, 32)
if err != nil {
continue
}
set[uint(v)] = struct{}{}
}
return set, sc.Err()
}
func firstForwardedFor(r *http.Request) string {
xff := r.Header.Get("X-Forwarded-For")
if xff == "" {
return ""
}
parts := strings.Split(xff, ",")
if len(parts) == 0 {
return ""
}
return strings.TrimSpace(parts[0])
}
func remoteIP(r *http.Request) string {
// Prefer XFF (because Traefik is proxy)
ip := firstForwardedFor(r)
if ip != "" {
return ip
}
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err == nil {
return host
}
return r.RemoteAddr
}
func (s *server) authHandler(w http.ResponseWriter, r *http.Request) {
if !s.ready.Load() {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
ipStr := remoteIP(r)
parsed := net.ParseIP(ipStr)
if parsed == nil {
// Always 200: we enrich, not block
w.Header().Set("X-NREN", "0")
w.WriteHeader(http.StatusOK)
return
}
var rec asnRecord
if err := s.db.Lookup(parsed, &rec); err != nil || rec.ASN == 0 {
w.Header().Set("X-NREN", "0")
w.WriteHeader(http.StatusOK)
return
}
w.Header().Set("X-ASN", strconv.FormatUint(uint64(rec.ASN), 10))
if rec.Org != "" {
// optional: keep it short; some org strings can be long
w.Header().Set("X-ASN-ORG", rec.Org)
}
_, ok := s.nrenASNs[rec.ASN]
if ok {
w.Header().Set("X-NREN", "1")
} else {
w.Header().Set("X-NREN", "0")
}
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("X-Service", s.versionTag)
w.WriteHeader(http.StatusOK)
}
func main() {
mmdbPath := getenv("MMDB_PATH", "/data/GeoLite2-ASN.mmdb")
asnListPath := getenv("ASN_LIST_PATH", "/data/nren_asns.txt")
addr := getenv("ADDR", ":8080")
version := getenv("VERSION_TAG", "asn-header-service")
db, err := maxminddb.Open(mmdbPath)
if err != nil {
log.Fatalf("failed to open mmdb: %v", err)
}
defer db.Close()
set, err := loadASNSet(asnListPath)
if err != nil {
log.Fatalf("failed to load asn list: %v", err)
}
s := &server{db: db, nrenASNs: set, versionTag: version}
s.ready.Store(true)
mux := http.NewServeMux()
mux.HandleFunc("/auth", s.authHandler)
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })
srv := &http.Server{
Addr: addr,
Handler: mux,
ReadHeaderTimeout: 2 * time.Second,
}
log.Printf("listening on %s (asn_count=%d)", addr, len(set))
log.Fatal(srv.ListenAndServe())
}
func getenv(k, def string) string {
v := strings.TrimSpace(os.Getenv(k))
if v == "" {
return def
}
return v
}

View File

@@ -26,7 +26,4 @@ services:
volumes:
kirbycms_data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/docker-volumes/website/kirbycms # Neuer fester Speicherort

View File

@@ -1 +1 @@
USERID=0
USERID=${USERID:-0}

114
dev-fpm.docker-compose.yml Normal file
View File

@@ -0,0 +1,114 @@
version: "3.8"
services:
mariadb_webapp_dev:
image: docker.io/bitnami/mariadb:11.1
container_name: ${DEV_COMPOSE_PREFIX:-dev}-mariadb
hostname: ${DEV_DB_HOST:-mariadb-webapp-dev}
environment:
MARIADB_USER: ${MARIADB_USER}
MARIADB_DATABASE: ${MARIADB_DATABASE}
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
networks:
- dev_backend
volumes:
- mindboost_mariadb_data_dev:/var/lib/mysql
laravel-redis-dev:
image: redis:alpine
container_name: ${DEV_COMPOSE_PREFIX:-dev}-redis
hostname: ${DEV_REDIS_HOST:-laravel-redis-dev}
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
networks:
- dev_backend
restart: unless-stopped
volumes:
- ./data/redis-dev:/data
laravel_backend_dev:
image: ${BACKEND_IMAGE}
container_name: ${DEV_COMPOSE_PREFIX:-dev}-backend
environment:
APP_ENV: ${APP_ENV:-production}
APP_NAME: ${APP_NAME:-Mindboost Backend Dev}
APP_URL: https://${DEV_BACKEND_DOMAIN}
FRONTEND_URL: https://${DEV_FRONTEND_DOMAIN}
DB_CONNECTION: mysql
DB_HOST: ${DEV_DB_HOST:-mariadb-webapp-dev}
DB_PORT: ${DB_PORT:-3306}
DB_DATABASE: ${MARIADB_DATABASE}
DB_USERNAME: ${MARIADB_USER}
DB_PASSWORD: ${MARIADB_PASSWORD}
REDIS_HOST: ${DEV_REDIS_HOST:-laravel-redis-dev}
REDIS_PASSWORD: ${REDIS_PASSWORD}
REDIS_PORT: ${REDIS_PORT:-6379}
CACHE_DRIVER: redis
QUEUE_CONNECTION: redis
SESSION_DRIVER: redis
volumes:
- ${BACKEND_CODE_PATH:-./apps/backend/src}:/app
- ${BACKEND_PUBLIC_PATH:-./apps/backend/src/public}:/var/www/public
- ${BACKEND_ENV_FILE:-./env/development/.env.backend}:/var/www/.env
- ./logs/backend-dev:/var/www/storage/logs
depends_on:
- mariadb_webapp_dev
- laravel-redis-dev
networks:
- dev_backend
laravel-nginx-dev:
image: nginx:alpine
container_name: ${DEV_COMPOSE_PREFIX:-dev}-nginx
volumes:
- ./nginx:/etc/nginx/conf.d:ro
- ${BACKEND_PUBLIC_PATH:-./apps/backend/src/public}:/var/www/public:ro
depends_on:
- laravel_backend_dev
labels:
- "traefik.enable=true"
- "traefik.http.routers.dev_backend_http.entrypoints=web"
- "traefik.http.routers.dev_backend_http.rule=Host(`${DEV_BACKEND_DOMAIN}`)"
- "traefik.http.routers.dev_backend_http.middlewares=traefik-https-redirect"
- "traefik.http.routers.dev_backend_https.entrypoints=websecure"
- "traefik.http.routers.dev_backend_https.rule=Host(`${DEV_BACKEND_DOMAIN}`)"
- "traefik.http.routers.dev_backend_https.tls=true"
- "traefik.http.routers.dev_backend_https.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
- "traefik.http.routers.dev_backend_https.service=dev_backend_service"
- "traefik.http.services.dev_backend_service.loadbalancer.server.port=80"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
networks:
- dev_backend
- proxy
nuxt_frontend_dev:
image: ${NUXT_IMAGE}
container_name: ${DEV_COMPOSE_PREFIX:-dev}-frontend
environment:
VUE_APP_BACKEND_HOST_ADDRESS: https://${DEV_BACKEND_DOMAIN}
NUXT_PUBLIC_BACKEND_URL: https://${DEV_BACKEND_DOMAIN}
networks:
- dev_backend
- proxy
depends_on:
- laravel_backend_dev
labels:
- "traefik.enable=true"
- "traefik.http.routers.dev_frontend_http.entrypoints=web"
- "traefik.http.routers.dev_frontend_http.rule=Host(`${DEV_FRONTEND_DOMAIN}`)"
- "traefik.http.routers.dev_frontend_http.middlewares=traefik-https-redirect"
- "traefik.http.routers.dev_frontend_https.entrypoints=websecure"
- "traefik.http.routers.dev_frontend_https.rule=Host(`${DEV_FRONTEND_DOMAIN}`)"
- "traefik.http.routers.dev_frontend_https.tls=true"
- "traefik.http.routers.dev_frontend_https.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
- "traefik.http.services.dev_frontend_https.loadbalancer.server.port=${VUE_INTERNAL_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
networks:
dev_backend:
driver: bridge
proxy:
external: true
volumes:
mindboost_mariadb_data_dev:

29
docs/infra.md Normal file
View File

@@ -0,0 +1,29 @@
Infrastructure v2
Goals
- Modular stacks you can pick individually (Nextcloud, etc.)
- Unified reverse proxy (Traefik) with automatic TLS
- Clear env layering and gitignored secrets
- Simple Make targets for a smooth DX
Layout
- infra/core/traefik: Traefik compose + static/dynamic config
- infra/apps/<service>: Selfcontained compose stacks and .env.example
- infra/env/<env>/common.env: Shared environment defaults per environment
- infra/secrets: Local secret files (ignored)
- scripts/infra/bootstrap.sh: Creates proxy network and ACME storage
Usage
1. cp infra/env/development/common.env infra/env/development/common.env (adjust values)
2. make bootstrap
3. make proxy-up
4. make app-up APP=nextcloud
Security
- Do not commit real secrets. Place them in local `.env` files or secret managers.
- Optionally protect Traefik dashboard with basic auth via `TRAEFIK_BASIC_AUTH_USERS`.

191
env/.env.all vendored
View File

@@ -37,194 +37,3 @@ SERVER_IP=127.0.0.1
# ✅ If needed, manually override variables in the shell or CLI.
#
#################################################################################################
## ______________________________________________________________________________________________
## SEVICE GROUP ADMINISTRATION
## ______________________________________________________________________________________________
# ----------------------------------
# Portainer
# ----------------------------------
PORTAINER_IMAGE=portainer/portainer-ce:latest
PORTAINER_DATA_PATH=../../../volumes/administration/portainer/data
## ______________________________________________________________________________________________
## SEVICE GROUP DATABASE
## ______________________________________________________________________________________________
# ----------------------------------
# Datenbank (MariaDB)
# ----------------------------------
MARIADB_USER=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
MARIADB_DATABASE=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
MARIADB_PASSWORD=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
MARIADB_ROOT_PASSWORD=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}-root
MARIADB_PORT=3306
MARIADB_HOST=${INFRASTRUCTURE_LABEL:-default}_database_${ENVIRONMENT:-development}
## ______________________________________________________________________________________________
## SEVICE GROUP BACKEND
## ______________________________________________________________________________________________
# ----------------------------------
# Redis
# ----------------------------------
REDIS_PASSWORD=laravel-redis-passwort
REDIS_PORT=6379
# ----------------------------------
# Laravel Backend
# ----------------------------------
BACKEND_NETWORK=backend
APP_NAME="mindboost backend - Compose Deployment"
APP_URL=https://backend.local
LARAVEL_PORT=8000
LARAVEL_VITE_PORT=5173
DB_HOST=${MARIADB_HOST:-mariadb}
DB_PORT=${MARIADB_PORT:-3306}
DB_PASSWORD=${MARIADB_PASSWORD:-default}
DB_USERNAME=${MARIADB_USER:-default}
DB_DATABASE=${MARIADB_DATABASE:-default}
JWT_SECRET=zMtO8sgsnc4UixWSsYWE1pK9EdpNLzxNSoIPlUpTe6dDlarM3bu4cwM80tH3jA0F
# ----------------------------------
# Adminer
# ----------------------------------
ADMINER_PORT=8080
## ______________________________________________________________________________________________
## SEVICE GROUP DEVELOP
## ______________________________________________________________________________________________
# ----------------------------------
# GITEA AND GITEA DB
# ----------------------------------
USER_UID=1000
USER_GID=1000
GITEA_VOLUME_PATH=../../../volumes/develop/gitea/gitea
GITEA_DATABASE_VOLUME_PATH=../../../volumes/develop/gitea/gitea_db
GITEA_MYSQL_ROOT_PASSWORD=very-difficult-passwort-gitea
GITEA_MYSQL_USER=gitea
GITEA_MYSQL_PASSWORD=very-difficult-gitea
GITEA_MYSQL_DATABASE=gitea
GITEA_MYSQL_ALLOW_EMPTY_PASSWORD=true
## ______________________________________________________________________________________________
## SEVICE GROUP FRONTEND
## ______________________________________________________________________________________________
# ----------------------------------
# VUE APP
# ----------------------------------
BACKEND_URL="backend.local"
## ______________________________________________________________________________________________
## SEVICE GROUP PROXY
## ______________________________________________________________________________________________
# ----------------------------------
# TRAEFIK
# ----------------------------------
TRAEFIK_ENABLE=true
TRAEFIK_NETWORK=proxy
TRAEFIK_BASIC_AUTH_USERS=${ADMIN_USER:-admin}:${ADMIN_PASSWORD_HASH}
TRAEFIK_CERT_RESOLVER=
## Domains when TRAEFIK is ENABLED
PORTAINER_DOMAIN=portainer.local
FRONTEND_DOMAIN=frontend.local
FRONTEND_DOMAIN_2=app.frontend.local
BACKEND_DOMAIN=backend.local
WEBSITE_DOMAIN=web.local
GITEA_DOMAIN=gitea.local
LIMESURVEY_DOMAIN=survey.local
LINKSTACK_DOMAIN=linkstack.local
TRAEFIK_DOMAIN=traefik.local
CLOUD_DOMAIN=cloud.local
### TLS for Domains
PORTAINER_TLS_DOMAIN_MAIN=${PORTAINER_DOMAIN}
FRONTEND_TLS_DOMAIN_MAIN=${FRONTEND_DOMAIN}
FRONTEND_TLS_DOMAIN_SANS=${FRONTEND_DOMAIN_2}
BACKEND_TLS_DOMAIN_MAIN=${BACKEND_DOMAIN}
WEBSITE_TLS_DOMAIN_MAIN=${WEBSITE_DOMAIN}
GITEA_TLS_DOMAIN_MAIN=${GITEA_DOMAIN}
LIMESURVEY_TLS_DOMAIN_MAIN=${LIMESURVEY_DOMAIN}
LINKSTACK_TLS_DOMAIN_MAIN=${LINKSTACK_DOMAIN}
TRAEFIK_TLS_DOMAIN_MAIN=${TRAEFIK_DOMAIN}
CLOUD_TLS_DOMAIN_MAIN=${CLOUD_DOMAIN}
## MIDDLEWARES
TRAEFIK_HTTPS_REDIRECT_MIDDLEWARE=${INFRASTRUCTURE_LABEL:-default}-https-redirect
TRAEFIK_BASIC_AUTH_MIDDLEWARE=${INFRASTRUCTURE_LABEL:-default}-basic-auth
## ENTRYPOINTS
TRAEFIK_ENTRYPOINT=websecure
TRAEFIK_ENTRYPOINT_HTTP=web
## ______________________________________________________________________________________________
## SEVICE GROUP SECURITY
## ______________________________________________________________________________________________
# ----------------------------------
# WIREGUARD
# ----------------------------------
WG_DEFAULT_ADDRESS=22.22.22.0
WG_HOST=${SERVER_IP:-127.0.0.1}
WG_LANG=de
## ______________________________________________________________________________________________
## SEVICE GROUP TOOLS
## ______________________________________________________________________________________________
# ----------------------------------
# NEXTCLOUD DB
# ----------------------------------
MYSQL_ROOT_PASSWORD=headpiece-constant1-denim-mindboost #SQL root Passwort eingeben
MYSQL_PASSWORD=idealist9-frayed-murkiness-mindboost #SQL Benutzer Passwort eingeben
MYSQL_DATABASE=nextcloud-mindboost #Datenbank Name
MYSQL_USER=mindboostcloud #SQL Nutzername
MYSQL_INITDB_SKIP_TZINFO=1
MARIADB_AUTO_UPGRADE=1
# ----------------------------------
# NEXTCLOUD CLOUD
# ----------------------------------
TRUSTED_PROXIES=172.16.255.254/16
OVERWRITEPROTOCOL=https
OVERWRITECLIURL=https://${CLOUD_DOMAIN:-cloud}
OVERWRITEHOST=${CLOUD_DOMAIN:-cloud}
REDIS_HOST=nextcloud-redis
REDIS_HOST_PASSWORD=redis-mindboost-passwort
## ______________________________________________________________________________________________
## SEVICE GROUP WEBSITE
## ______________________________________________________________________________________________
# ----------------------------------
# KIRBY CMS
# ----------------------------------
KIRBY_USER_ID=0

50
env/README.md vendored Normal file
View File

@@ -0,0 +1,50 @@
# 🔧 Environment Configuration Guide
## 🌍 Overview
This project uses **environment variables** to manage configuration across different environments (development, staging, production, etc.). These variables are loaded from `.env` files and can be overridden at multiple levels.
---
## 📌 **Environment Variable Priority (Lowest to Highest)**
| 🔢 Priority | 📄 Source | 🔍 Description |
|------------|-----------------------------|------------------------------------------------|
| 1**Fallback Values** | hardcoded defaults | Used only if no other configuration is provided |
| 2**Global Defaults** | `.env.all` | Shared settings for all services |
| 3**Service-Specific Overrides** | `.env.backend`, `.env.proxy`, etc. | Overrides `.env.all` with service-specific values |
| 4**Shell Environment Variables** | `export VAR=value` before running | Takes precedence over `.env` files |
| 5**CLI Overrides** | `docker compose --env-file` or `-e VAR=value` | **Highest priority** (for temporary overrides) |
---
## 🔄 **Overwriting Behavior**
- 🏗 **Variables defined in `.env.all`** override fallback values.
- 🏗 **Variables defined in `.env.<service>`** (e.g., `.env.backend`) override `.env.all`.
- 🔧 **Manually exported environment variables** in the shell take priority over `.env` files.
- 🚀 **Variables passed via CLI (`--env-file` or `-e VAR=value`)** override everything.
---
## 🚀 **Best Practices**
✔️ **Use `.env.all` for global configurations** (e.g., `ENVIRONMENT=development`, `INFRASTRUCTURE_LABEL=myinfra`).
✔️ **Use `.env.<service>` for service-specific configurations** (e.g., `.env.backend` for Laravel, `.env.database` for MariaDB).
✔️ **If needed, manually override variables in the shell** using `export VAR=value`.
✔️ **Use CLI `--env-file` for temporary overrides** in testing/debugging scenarios.
---
## 🏗 **Example File Structure**
```sh
/env/
├── .env.all # Global default variables
├── development/
│ ├── .env.backend # Backend service config for development
│ ├── .env.database # Database config for development
│ ├── .env.proxy # Proxy config for development
├── staging/
│ ├── .env.backend # Backend service config for staging
│ ├── .env.database # Database config for staging
├── production/
│ ├── .env.backend # Backend service config for production
│ ├── .env.database # Database config for production

31
env/development/portainer/backend.env vendored Normal file
View File

@@ -0,0 +1,31 @@
# ----------------------------------
# Redis
# ----------------------------------
REDIS_PASSWORD=laravel-redis-passwort
REDIS_PORT=6379
SERVER_IP=${SERVER_IP:-localhost}
# ----------------------------------
# Laravel Backend
# ----------------------------------
BACKEND_NETWORK=backend
APP_ENV=${ENVIRONMENT-local}
APP_NAME="mindboost backend - Compose Deployment"
APP_URL=https://backend.local
LARAVEL_PORT=8000
LARAVEL_VITE_PORT=5173
JWT_SECRET=zMtO8sgsnc4UixWSsYWE1pK9EdpNLzxNSoIPlUpTe6dDlarM3bu4cwM80tH3jA0F
# ----------------------------------
# Datenbank Zugriff - ! MUSS MIT .env.database übereinstimmen
# ----------------------------------
DB_HOST=database
DB_PORT=3306
DB_PASSWORD=1stronges-mindboostdb-passwort
DB_USERNAME=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
DB_DATABASE=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}

View File

@@ -0,0 +1,14 @@
# Nextcloud stack configuration
NEXTCLOUD_DOMAIN=cloud.example.com
# Database
NEXTCLOUD_DB_NAME=nextcloud
NEXTCLOUD_DB_USER=nextcloud
NEXTCLOUD_DB_PASSWORD=changeMe
NEXTCLOUD_DB_ROOT_PASSWORD=changeMeRoot
# PHP tuning
NEXTCLOUD_PHP_MEMORY_LIMIT=512M
NEXTCLOUD_PHP_UPLOAD_LIMIT=1024M

View File

@@ -0,0 +1,13 @@
Nextcloud Stack
Env vars required (copy .env.example to .env and adjust):
- NEXTCLOUD_DOMAIN: public domain for Nextcloud
- NEXTCLOUD_DB_NAME, NEXTCLOUD_DB_USER, NEXTCLOUD_DB_PASSWORD, NEXTCLOUD_DB_ROOT_PASSWORD
- Optional: NEXTCLOUD_PHP_MEMORY_LIMIT, NEXTCLOUD_PHP_UPLOAD_LIMIT
Usage
- Ensure the Traefik proxy stack is up and the external `${TRAEFIK_NETWORK}` network exists.
- Run: `docker compose --env-file ../../env/${ENV}/common.env --env-file ./.env -f docker-compose.yml up -d`

View File

@@ -0,0 +1,70 @@
services:
nextcloud:
image: nextcloud:28-apache
container_name: ${INFRASTRUCTURE_LABEL:-stack}-nextcloud
restart: unless-stopped
depends_on:
- db
- redis
environment:
- NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_DOMAIN}
- OVERWRITEHOST=${NEXTCLOUD_DOMAIN}
- OVERWRITEPROTOCOL=https
- OVERWRITECLIURL=https://${NEXTCLOUD_DOMAIN}
- REDIS_HOST=redis
- MYSQL_HOST=db
- MYSQL_DATABASE=${NEXTCLOUD_DB_NAME:-nextcloud}
- MYSQL_USER=${NEXTCLOUD_DB_USER:-nextcloud}
- MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
- PHP_MEMORY_LIMIT=${NEXTCLOUD_PHP_MEMORY_LIMIT:-512M}
- PHP_UPLOAD_LIMIT=${NEXTCLOUD_PHP_UPLOAD_LIMIT:-1024M}
volumes:
- nextcloud_data:/var/www/html
networks:
- nextcloud
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.nextcloud.rule=Host(`${NEXTCLOUD_DOMAIN}`)
- traefik.http.routers.nextcloud.entrypoints=websecure
- traefik.http.routers.nextcloud.tls=true
- traefik.http.routers.nextcloud.tls.certresolver=letsencrypt
- traefik.http.services.nextcloud.loadbalancer.server.port=80
- traefik.http.routers.nextcloud.middlewares=security-headers@file
- traefik.docker.network=${TRAEFIK_NETWORK:-proxy}
db:
image: mariadb:11
container_name: ${INFRASTRUCTURE_LABEL:-stack}-nextcloud-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${NEXTCLOUD_DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${NEXTCLOUD_DB_NAME:-nextcloud}
- MYSQL_USER=${NEXTCLOUD_DB_USER:-nextcloud}
- MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
volumes:
- db_data:/var/lib/mysql
networks:
- nextcloud
redis:
image: redis:7-alpine
container_name: ${INFRASTRUCTURE_LABEL:-stack}-nextcloud-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- nextcloud
volumes:
nextcloud_data:
db_data:
redis_data:
networks:
proxy:
external: true
nextcloud:
name: ${INFRASTRUCTURE_LABEL:-stack}-nextcloud

View File

@@ -0,0 +1,47 @@
services:
traefik:
image: traefik:v2.11
container_name: ${INFRASTRUCTURE_LABEL:-stack}-traefik
restart: unless-stopped
command:
- --providers.file.directory=/etc/traefik/dynamic
- --providers.file.watch=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=${TRAEFIK_NETWORK:-proxy}
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge=true
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
- --api.dashboard=true
- --log.level=${TRAEFIK_LOG_LEVEL:-INFO}
- --accesslog=true
ports:
- ${TRAEFIK_HTTP_PORT:-80}:80
- ${TRAEFIK_HTTPS_PORT:-443}:443
environment:
- TZ=${TZ:-UTC}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic:/etc/traefik/dynamic:ro
- ./data:/letsencrypt
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DASHBOARD_DOMAIN}`)
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.tls=true
- traefik.http.routers.traefik.tls.certresolver=letsencrypt
- traefik.http.routers.traefik.service=api@internal
- traefik.docker.network=${TRAEFIK_NETWORK:-proxy}
# Optional: protect dashboard with basic auth if TRAEFIK_BASIC_AUTH_USERS is set
- traefik.http.routers.traefik.middlewares=dashboard-basicauth@file
networks:
proxy:
external: true

View File

@@ -0,0 +1,25 @@
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
permanent: true
security-headers:
headers:
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: no-referrer-when-downgrade
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
dashboard-basicauth:
basicAuth:
users:
# Provide users via env TRAEFIK_BASIC_AUTH_USERS, format: user:hashedpassword
# Example to generate: htpasswd -nbB admin 'yourpassword'
# If env is empty, you can comment this middleware out from labels
- ${TRAEFIK_BASIC_AUTH_USERS:-}

View File

@@ -0,0 +1,6 @@
tls:
options:
default:
minVersion: VersionTLS12
sniStrict: true

View File

@@ -0,0 +1,30 @@
api:
dashboard: true
providers:
docker:
exposedByDefault: false
network: ${TRAEFIK_NETWORK:-proxy}
file:
directory: /etc/traefik/dynamic
watch: true
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
log:
level: ${TRAEFIK_LOG_LEVEL:-INFO}
accessLog: {}
certificatesResolvers:
letsencrypt:
acme:
email: ${ACME_EMAIL}
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web

14
infra/env/common.env.example vendored Normal file
View File

@@ -0,0 +1,14 @@
# Global/defaults
INFRASTRUCTURE_LABEL=mindboost
TZ=UTC
# Traefik / proxy
TRAEFIK_NETWORK=proxy
TRAEFIK_HTTP_PORT=80
TRAEFIK_HTTPS_PORT=443
TRAEFIK_LOG_LEVEL=INFO
ACME_EMAIL=you@example.com
TRAEFIK_DASHBOARD_DOMAIN=traefik.example.com
# Optional basic auth users for dashboard (format: user:hashed)
#TRAEFIK_BASIC_AUTH_USERS=admin:$2y$05$...

11
infra/env/development/common.env vendored Normal file
View File

@@ -0,0 +1,11 @@
# Development defaults (copy to production and adjust as needed)
INFRASTRUCTURE_LABEL=dev
TZ=UTC
TRAEFIK_NETWORK=proxy
TRAEFIK_HTTP_PORT=80
TRAEFIK_HTTPS_PORT=443
TRAEFIK_LOG_LEVEL=INFO
ACME_EMAIL=dev@example.com
TRAEFIK_DASHBOARD_DOMAIN=traefik.local

27
nginx/dev-backend.conf Normal file
View File

@@ -0,0 +1,27 @@
server {
listen 80;
server_name _;
root /var/www/public;
index index.php index.html;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass laravel_backend_dev:9000;
fastcgi_index index.php;
}
location ~ /\.(?!well-known).* {
deny all;
}
}

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
# Create external proxy network if it doesn't exist and prepare Traefik state
NETWORK_NAME=${TRAEFIK_NETWORK:-proxy}
ACME_FILE="infra/core/traefik/data/acme.json"
echo "[bootstrap] Ensuring external network '${NETWORK_NAME}' exists..."
if ! docker network ls --format '{{.Name}}' | grep -qx "${NETWORK_NAME}"; then
docker network create "${NETWORK_NAME}"
echo "[bootstrap] Created network '${NETWORK_NAME}'."
else
echo "[bootstrap] Network '${NETWORK_NAME}' already exists."
fi
echo "[bootstrap] Ensuring ACME storage exists with correct permissions..."
mkdir -p "$(dirname "${ACME_FILE}")"
touch "${ACME_FILE}"
chmod 600 "${ACME_FILE}"
echo "[bootstrap] ACME storage ready at ${ACME_FILE}."
echo "[bootstrap] Done."

0
scripts/setup/set-global-env.sh Normal file → Executable file
View File

0
scripts/setup/set-project-root.sh Normal file → Executable file
View File

View File

@@ -1,4 +1,8 @@
#!/bin/bash
source ../setup/set-project-root.sh
source ../setup/set-global-env.sh
source ../setup/set-proxy-env.sh
source ../setup/generate-secrets.sh
# Pfad zur .env.all Datei
ENV_FILE="../../env/.env.all"
@@ -13,7 +17,7 @@ ENVIRONMENT=$(get_env_var "ENVIRONMENT")
SERVER_IP=$(curl -s https://api.ipify.org)
# Liste aller Stacks
STACKS=("frontend" "database" "backend")
STACKS=("proxy" "frontend" "database" "backend")
# Liste aller Environments
ENVIRONMENTS=("development" "staging" "production")
@@ -44,7 +48,7 @@ else
fi
# Ausgabe der Variablen
echo "Deploying to:"
echo "Deploying to"
echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}"
echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}"
echo "-----------------------------------"
@@ -57,5 +61,5 @@ fi
# Ausführen des Docker Compose Befehls
docker compose -f ../../apps/docker-compose.all.yml --env-file ../../env/.env.all -p ${INFRASTRUCTURE:-my} --profile app up --remove-orphans $BUILD_OPTION
docker compose -f ../../apps/docker-compose.all.yml -p ${INFRASTRUCTURE:-my} --profile app up --remove-orphans $BUILD_OPTION

View File

@@ -3,7 +3,7 @@ set -e
# Funktion zur Überprüfung der Produktivumgebung
is_production() {
local prod_ip="85.215.56.185" # IP-Adresse deines Produktivservers
local prod_ip=${SERVER_IP:-127.0.0.1} # IP-Adresse deines Produktivservers
local current_ip
# Überprüfe das Betriebssystem
@@ -69,30 +69,27 @@ if ! docker ps --format '{{.Names}}' | grep -q 'traefik'; then
echo "Wir befinden uns in der Produktivumgebung."
echo "Starte Traefik und CrowdSec Bouncer mit docker-compose.traefik.prod.yml..."
env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an
docker compose -f ../apps/proxy/docker-compose.traefik.prod.yml up -d
docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
else
echo "Wir befinden uns in der lokalen Entwicklungsumgebung."
echo "Starte Traefik und CrowdSec Bouncer mit docker-compose.traefik.local.yml..."
env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an
docker compose -f ../apps/docker-compose.traefik.local.yml up -d
docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
fi
else
echo "Traefik läuft bereits. Aktualisiere die Konfiguration..."
if is_production; then
echo "Aktualisiere Traefik und CrowdSec Bouncer in der Produktivumgebung..."
docker compose -f ../apps/docker-compose.traefik.prod.yml up -d
docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
else
echo "Aktualisiere Traefik und CrowdSec Bouncer in der lokalen Umgebung..."
docker compose -f ../apps/docker-compose.traefik.local.yml up -d
docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
fi
fi
echo "Traefik und CrowdSec Bouncer Deployment abgeschlossen."
=================
echo "Prüfe, ob Traefik läuft..."
set_environment_variables
@@ -106,14 +103,14 @@ fi
if is_production; then
echo "Wir befinden uns in der Produktivumgebung."
echo "Starte/Aktualisiere Deployment mit docker-compose.prod.yml..."
echo "Starte/Aktualisiere Deployment mit docker-compose.yml..."
env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an
docker compose -f ../apps/docker-compose.prod.yml up -d
docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
else
echo "Wir befinden uns in der lokalen Entwicklungsumgebung."
echo "Starte/Aktualisiere lokale Version mit docker-compose.overwrite.yml..."
env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an
docker compose -f ../apps/docker-compose.overwrite.yml up -d
docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
fi
@@ -123,9 +120,9 @@ if ! docker ps --format '{{.Names}}' | grep -q 'traefik'; then
if is_production; then
echo "Wir befinden uns in der Produktivumgebung."
set_environment_variables
echo "Starte Deployment mit docker-compose.prod.yml..."
echo "Starte Deployment mit docker-compose.yml..."
env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an
docker compose -f ../apps/docker-compose.prod.yml up -d
docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
else
echo "Wir befinden uns in der lokalen Entwicklungsumgebung."
read -p "Möchtest du die lokale Version zum Debuggen (docker-compose.overwrite.yml) starten? (y/n): " answer
@@ -133,7 +130,7 @@ if ! docker ps --format '{{.Names}}' | grep -q 'traefik'; then
echo "Starte lokale Version..."
set_environment_variables
env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an
docker compose -f ../apps/docker-compose.overwrite.yml up -d
docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
else
echo "Deployment abgebrochen."
exit 1
@@ -145,15 +142,15 @@ else
if is_production; then
echo "Wir befinden uns in der Produktivumgebung."
set_environment_variables
echo "Aktualisiere Deployment mit docker-compose.prod.yml..."
echo "Aktualisiere Deployment mit docker-compose.yml..."
env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an
docker compose -f ../apps/docker-compose.prod.yml up -d
docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
else
echo "Wir befinden uns in der lokalen Entwicklungsumgebung."
set_environment_variables
echo "Aktualisiere lokale Version mit docker-compose.overwrite.yml..."
env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an
docker compose -f ../apps/docker-compose.overwrite.yml up -d
docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d
fi
fi

37
scripts/start/deploy.sh Executable file
View File

@@ -0,0 +1,37 @@
#!/bin/bash
#!/bin/bash
# Bestimme das Root-Verzeichnis des Git-Repos
ROOT_DIR=$(git rev-parse --show-toplevel 2>/dev/null)
# Falls das Repository nicht gefunden wurde, abbrechen
if [ -z "$ROOT_DIR" ]; then
echo "❌ Fehler: Kein Git-Repository gefunden!"
exit 1
fi
# Setze die Variable für die aktuelle Shell-Sitzung
echo "✅ ROOT_DIR gesetzt auf: $ROOT_DIR"
# Pfad zur .env.all Datei
ENV_FILE="${ROOT_DIR}/env/.env.all"
echo $ENV_FILE
# Funktion zum Auslesen von Variablen aus der .env.all Datei
get_env_var() {
grep "^$1=" "$ENV_FILE" | cut -d '=' -f2
}
# Auslesen der INFRASTRUCTURE und ENVIRONMENT Variablen
INFRASTRUCTURE=$(get_env_var "INFRASTRUCTURE_LABEL")
ENVIRONMENT=$(get_env_var "ENVIRONMENT")
SERVER_IP=$(curl -s https://api.ipify.org)
# Ausgabe der Variablen
echo "Deploying to:"
echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}"
echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}"
echo "-----------------------------------"
# Ausführen des Docker Compose Befehls
docker compose -f ../../apps/docker-compose.all.yml -p ${INFRASTRUCTURE:-my} --env-file ${ENV_FILE} --env-file ${ROOT_DIR}/env/${ENVIRONMENT:-development}/.env.proxy --profile app up --remove-orphans