From 8c7eddbaee1c0bb16fcd03466a9145e0b1b8bb7c Mon Sep 17 00:00:00 2001 From: xiteng Date: Wed, 27 May 2026 19:51:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20SeaweedFS=20=E6=9B=BF=E4=BB=A3=20MinIO?= =?UTF-8?q?=EF=BC=8CAuthentik=20ForwardAuth=EF=BC=8C=E5=A4=9A=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增: - SeaweedFS v4.28 替代 MinIO (域名 file.xiteng.site) - Authentik Outpost (Uptime Kuma + SeaweedFS 独立实例) - ForwardAuth 为 Uptime Kuma 和 SeaweedFS 提供认证 - gpu-exporter.py 监控脚本 升级: - Traefik v3.6 → v3.7 - Authentik 固定版本 2026.5.0 + trusted proxy CIDR - HedgeDoc PG 11 → 16,添加 OIDC 认证 - Homepage 添加 OIDC 认证 安全: - Outpost token 迁移至 .env 引用(不再硬编码) - .gitignore 扩展覆盖数据/缓存/旧 MinIO - security.toml 清理注释掉的旧密钥 移除: - MinIO(已全部迁移至 SeaweedFS) --- .gitignore | 15 +++++++++ authentik/compose.yml | 7 +++- hedgedoc/compose.yml | 31 ++++++++++++++---- homepage/compose.yml | 6 ++++ homepage/gpu-exporter.py | 35 ++++++++++++++++++++ minio/compose.yml | 52 ------------------------------ outpost-seaweedfs/compose.yml | 17 ++++++++++ outpost/compose.yml | 29 +++++++++++++++++ seaweedfs/compose.yml | 60 +++++++++++++++++++++++++++++++++++ seaweedfs/security.toml | 3 ++ traefik/compose.yml | 2 +- uptime-kuma/compose.yml | 8 ++++- 12 files changed, 203 insertions(+), 62 deletions(-) create mode 100644 homepage/gpu-exporter.py delete mode 100644 minio/compose.yml create mode 100644 outpost-seaweedfs/compose.yml create mode 100644 outpost/compose.yml create mode 100644 seaweedfs/compose.yml create mode 100644 seaweedfs/security.toml diff --git a/.gitignore b/.gitignore index 28a16bc..32ed9a2 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,19 @@ frpc/frpc.toml # 忽略 Traefik 证书 traefik/letsencrypt/ + +# 忽略数据库持久化数据 +authentik/pgdata/ +authentik/redis/ +authentik/media/ +gitea/db_data/ +hedgedoc/data_pg11_backup/ + +# 忽略旧 MinIO(已迁移至 SeaweedFS) +minio/ + +# 忽略 SeaweedFS 运行时数据 +seaweedfs/data/ + +# 忽略 AI 工具 .aider* diff --git a/authentik/compose.yml b/authentik/compose.yml index 6fd3795..c28ef6b 100644 --- a/authentik/compose.yml +++ b/authentik/compose.yml @@ -1,6 +1,6 @@ services: authentik: - image: ghcr.io/goauthentik/server:latest + image: ghcr.io/goauthentik/server:2026.5.0 container_name: authentik restart: unless-stopped command: server @@ -14,6 +14,7 @@ services: AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_DB_PASSWORD} AUTHENTIK_AVATARS: none AUTHENTIK_LOG_LEVEL: info + AUTHENTIK_SECURITY__TRUSTED_PROXY_CIDR: 172.18.0.0/16 volumes: - ./media:/media - ./custom-templates:/templates @@ -25,6 +26,10 @@ services: - "traefik.http.routers.authentik.entrypoints=websecure" - "traefik.http.routers.authentik.tls=true" - "traefik.http.routers.authentik.tls.certresolver=cfresolver" + # 强制 X-Forwarded-Proto 为 https(Tunnel 走 HTTP:80 入口,Traefik 不知道外层是 HTTPS) + - "traefik.http.middlewares.force-https-headers.headers.customrequestheaders.X-Forwarded-Proto=https" + - "traefik.http.routers.authentik.middlewares=force-https-headers" + - "traefik.http.routers.authentik-http.middlewares=force-https-headers" # --- HTTP 入口 --- - "traefik.http.routers.authentik-http.rule=Host(`auth.xiteng.site`)" - "traefik.http.routers.authentik-http.service=authentik" diff --git a/hedgedoc/compose.yml b/hedgedoc/compose.yml index 32e725d..4b51696 100644 --- a/hedgedoc/compose.yml +++ b/hedgedoc/compose.yml @@ -1,8 +1,10 @@ services: - db: - image: postgres:11.6-alpine - env_file: - - ../.env + hedgedoc-db: + image: postgres:16-alpine + environment: + - POSTGRES_USER=$POSTGRES_USER + - POSTGRES_PASSWORD=$POSTGRES_PASSWORD + - POSTGRES_DB=$POSTGRES_DB container_name: hedgedoc_db restart: unless-stopped volumes: @@ -14,17 +16,32 @@ services: image: quay.io/hedgedoc/hedgedoc:latest container_name: hedgedoc restart: unless-stopped - env_file: - - ../.env environment: + - CMD_DB_HOST=hedgedoc_db + - CMD_DB_PORT=5432 + - CMD_DB_DATABASE=$POSTGRES_DB + - CMD_DB_USERNAME=$POSTGRES_USER + - CMD_DB_PASSWORD=$POSTGRES_PASSWORD + - CMD_DB_DIALECT=postgres - CMD_DOMAIN=notes.xiteng.site - CMD_URL_ADDPORT=false - CMD_ALLOW_ORIGIN=localhost,notes.xiteng.site - CMD_PROTOCOL_USESSL=true + - CMD_OAUTH2_PROVIDERNAME=Authentik + - CMD_OAUTH2_CLIENT_ID=hedgedoc-280baa8d + - CMD_OAUTH2_CLIENT_SECRET=0630f01b111d4ad68ff9b2d174028994decb5b55b51a4556bda24768d11b4586 + - CMD_OAUTH2_AUTHORIZATION_URL=https://auth.xiteng.site/application/o/authorize/ + - CMD_OAUTH2_TOKEN_URL=https://auth.xiteng.site/application/o/token/ + - CMD_OAUTH2_USER_PROFILE_URL=https://auth.xiteng.site/application/o/userinfo/ + - CMD_OAUTH2_SCOPE=openid profile email + - CMD_OAUTH2_ISSUER=https://auth.xiteng.site/application/o/hedgedoc/ + - CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR=preferred_username + - CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR=name + - CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR=email volumes: - ./uploads:/hedgedoc/public/uploads depends_on: - - db + - hedgedoc-db ports: - "3000:3000" labels: diff --git a/homepage/compose.yml b/homepage/compose.yml index 705b4f6..c69a33e 100644 --- a/homepage/compose.yml +++ b/homepage/compose.yml @@ -5,11 +5,17 @@ services: restart: unless-stopped volumes: - ./config:/app/config + - ./config/icons:/app/public/icons:ro - /var/run/docker.sock:/var/run/docker.sock:ro ports: - "3003:3000" environment: - HOMEPAGE_ALLOWED_HOSTS=home.xiteng.site,xiteng.site + - HOMEPAGE_AUTH_PROVIDER=oidc + - HOMEPAGE_AUTH_OIDC_ISSUER=https://auth.xiteng.site/application/o/homepage/ + - HOMEPAGE_AUTH_OIDC_CLIENT_ID=homepage-64bfd7a7 + - HOMEPAGE_AUTH_OIDC_CLIENT_SECRET=b5d710941dba4a4694a92fe7bc381f3c853a248b5114467d9b82b0953928326b + - HOMEPAGE_AUTH_OIDC_SCOPE=openid profile email labels: # --- 1. Traefik 路由设置 --- - "traefik.enable=true" diff --git a/homepage/gpu-exporter.py b/homepage/gpu-exporter.py new file mode 100644 index 0000000..fa0210d --- /dev/null +++ b/homepage/gpu-exporter.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Serve NVIDIA GPU stats as JSON for Homepage customapi widget.""" +import subprocess, json +from http.server import HTTPServer, BaseHTTPRequestHandler + +class GPUHandler(BaseHTTPRequestHandler): + def do_GET(self): + try: + r = subprocess.run( + ['nvidia-smi', '--query-gpu=utilization.gpu,memory.used,memory.total,temperature.gpu,fan.speed', + '--format=csv,noheader,nounits'], + capture_output=True, text=True, timeout=5 + ) + parts = [p.strip() for p in r.stdout.strip().split(',')] + data = { + 'gpu_util': parts[0] + '%', + 'mem_used': parts[1], + 'mem_total': parts[2], + 'mem': f'{parts[1]} / {parts[2]} MiB', + 'temp': parts[3] + '°C', + 'fan': parts[4] + '%', + } + except Exception as e: + data = {'error': str(e)} + + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.send_header('Access-Control-Allow-Origin', '*') + self.end_headers() + self.wfile.write(json.dumps(data).encode()) + + def log_message(self, format, *args): + pass # suppress logs + +HTTPServer(('0.0.0.0', 9999), GPUHandler).serve_forever() diff --git a/minio/compose.yml b/minio/compose.yml deleted file mode 100644 index 92e900d..0000000 --- a/minio/compose.yml +++ /dev/null @@ -1,52 +0,0 @@ -services: - minio: - image: minio/minio:latest - env_file: - - ../.env - container_name: minio - restart: unless-stopped - volumes: - - ./data:/data - command: server /data --console-address ":9001" - ports: - - "9000:9000" - - "9001:9001" - labels: - # --- Traefik: Console (9001) --- - - "traefik.enable=true" - - "traefik.http.routers.minio.rule=Host(`minio.xiteng.site`)" - - "traefik.http.services.minio.loadbalancer.server.port=9001" - - "traefik.http.routers.minio.entrypoints=websecure" - - "traefik.http.routers.minio.tls=true" - - "traefik.http.routers.minio.tls.certresolver=cfresolver" - # --- Traefik: HTTP 入口(cloudflared tunnel)--- - - "traefik.http.routers.minio-http.rule=Host(`minio.xiteng.site`)" - - "traefik.http.routers.minio-http.service=minio" - - "traefik.http.routers.minio-http.entrypoints=web" - # --- Traefik: API (9000) --- - - "traefik.http.routers.minio-api.rule=Host(`minio-api.xiteng.site`)" - - "traefik.http.services.minio-api.loadbalancer.server.port=9000" - - "traefik.http.routers.minio-api.entrypoints=websecure" - - "traefik.http.routers.minio-api.tls=true" - - "traefik.http.routers.minio-api.tls.certresolver=cfresolver" - # --- Traefik: API HTTP 入口 --- - - "traefik.http.routers.minio-api-http.rule=Host(`minio-api.xiteng.site`)" - - "traefik.http.routers.minio-api-http.service=minio-api" - - "traefik.http.routers.minio-api-http.entrypoints=web" - # --- Homepage --- - - "homepage.group=我的服务" - - "homepage.name=MinIO" - - "homepage.icon=minio" - - "homepage.href=https://minio.xiteng.site" - - "homepage.description=对象存储" - # --- AutoKuma --- - - "kuma.minio.http.name=MinIO" - - "kuma.minio.http.url=http://minio:9000" - - "kuma.minio.http.interval=60" - - "kuma.minio.http.max_retries=3" - networks: - - homelab_net - -networks: - homelab_net: - external: true diff --git a/outpost-seaweedfs/compose.yml b/outpost-seaweedfs/compose.yml new file mode 100644 index 0000000..c465f73 --- /dev/null +++ b/outpost-seaweedfs/compose.yml @@ -0,0 +1,17 @@ +services: + outpost: + image: ghcr.io/goauthentik/proxy:2026.5.0 + container_name: authentik-outpost-seaweedfs + restart: unless-stopped + environment: + AUTHENTIK_HOST: https://auth.xiteng.site + AUTHENTIK_TOKEN: ${AUTHENTIK_OUTPOST_SEAWEEDFS_TOKEN} + AUTHENTIK_INSECURE: "true" + ports: + - "9003:9000" + networks: + - homelab_net + +networks: + homelab_net: + external: true diff --git a/outpost/compose.yml b/outpost/compose.yml new file mode 100644 index 0000000..eacd506 --- /dev/null +++ b/outpost/compose.yml @@ -0,0 +1,29 @@ +services: + outpost: + image: ghcr.io/goauthentik/proxy:2026.5.0 + container_name: authentik-outpost + restart: unless-stopped + environment: + AUTHENTIK_HOST: https://auth.xiteng.site + AUTHENTIK_TOKEN: ${AUTHENTIK_OUTPOST_TOKEN} + AUTHENTIK_INSECURE: "true" + ports: + - "9002:9000" + - "9443:9443" + labels: + # --- Outpost 自身管理界面 --- + - "traefik.enable=true" + - "traefik.http.routers.outpost.rule=Host(`outpost.xiteng.site`)" + - "traefik.http.services.outpost.loadbalancer.server.port=9000" + - "traefik.http.routers.outpost.entrypoints=websecure" + - "traefik.http.routers.outpost.tls=true" + - "traefik.http.routers.outpost.tls.certresolver=cfresolver" + - "traefik.http.routers.outpost-http.rule=Host(`outpost.xiteng.site`)" + - "traefik.http.routers.outpost-http.service=outpost" + - "traefik.http.routers.outpost-http.entrypoints=web" + networks: + - homelab_net + +networks: + homelab_net: + external: true diff --git a/seaweedfs/compose.yml b/seaweedfs/compose.yml new file mode 100644 index 0000000..debdc72 --- /dev/null +++ b/seaweedfs/compose.yml @@ -0,0 +1,60 @@ +services: + seaweedfs: + image: chrislusf/seaweedfs:latest + container_name: seaweedfs + restart: unless-stopped + command: "server -s3 -s3.port=8333 -master.port=9333 -volume.port=8080 -filer -filer.port=8888 -dir=/data -s3.domainName=file.xiteng.site" + environment: + - WEED_SECURITY_CONFIG=/etc/seaweedfs/security.toml + volumes: + - ./data:/data + - ./security.toml:/etc/seaweedfs/security.toml:ro + ports: + - "9333:9333" # master + - "8081:8080" # volume + - "8888:8888" # filer + - "8333:8333" # s3 + labels: + - "traefik.enable=true" + # --- Traefik: S3 Console (带 Authentik ForwardAuth) --- + - "traefik.http.routers.minio.rule=Host(`file.xiteng.site`)" + - "traefik.http.services.minio.loadbalancer.server.port=8888" + - "traefik.http.routers.minio.entrypoints=websecure" + - "traefik.http.routers.minio.tls=true" + - "traefik.http.routers.minio.tls.certresolver=cfresolver" + - "traefik.http.routers.minio-http.rule=Host(`file.xiteng.site`)" + - "traefik.http.routers.minio-http.service=minio" + - "traefik.http.routers.minio-http.entrypoints=web" + # --- ForwardAuth:显式转发 Cookie 头给 outpost --- + - "traefik.http.middlewares.minio-scheme.headers.customrequestheaders.X-Forwarded-Proto=https" + - "traefik.http.middlewares.minio-auth.forwardauth.address=http://authentik-outpost-seaweedfs:9000/outpost.goauthentik.io/auth/traefik" + - "traefik.http.middlewares.minio-auth.forwardauth.trustForwardHeader=true" + - "traefik.http.middlewares.minio-auth.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name" + - "traefik.http.routers.minio.middlewares=minio-scheme,minio-auth" + - "traefik.http.routers.minio-http.middlewares=minio-scheme,minio-auth" + # --- Traefik: S3 API (8333) --- + - "traefik.http.routers.minio-api.rule=Host(`minio-api.xiteng.site`)" + - "traefik.http.services.minio-api.loadbalancer.server.port=8333" + - "traefik.http.routers.minio-api.entrypoints=websecure" + - "traefik.http.routers.minio-api.tls=true" + - "traefik.http.routers.minio-api.tls.certresolver=cfresolver" + - "traefik.http.routers.minio-api-http.rule=Host(`minio-api.xiteng.site`)" + - "traefik.http.routers.minio-api-http.service=minio-api" + - "traefik.http.routers.minio-api-http.entrypoints=web" + # --- Homepage --- + - "homepage.group=我的服务" + - "homepage.name=SeaweedFS" + - "homepage.icon=/icons/seaweedfs-local.png" + - "homepage.href=https://file.xiteng.site" + - "homepage.description=对象存储" + # --- AutoKuma --- + - "kuma.minio.http.name=SeaweedFS" + - "kuma.minio.http.url=http://seaweedfs:8888" + - "kuma.minio.http.interval=60" + - "kuma.minio.http.max_retries=3" + networks: + - homelab_net + +networks: + homelab_net: + external: true diff --git a/seaweedfs/security.toml b/seaweedfs/security.toml new file mode 100644 index 0000000..b63e565 --- /dev/null +++ b/seaweedfs/security.toml @@ -0,0 +1,3 @@ +# Homelab Security Configuration +# All JWT sections are commented out — filer UI auth is handled by Authentik ForwardAuth. +# See README.md for architecture details. diff --git a/traefik/compose.yml b/traefik/compose.yml index ce89331..f40e737 100644 --- a/traefik/compose.yml +++ b/traefik/compose.yml @@ -1,6 +1,6 @@ services: traefik: - image: traefik:v3.6 + image: traefik:v3.7 container_name: traefik restart: unless-stopped env_file: ../.env diff --git a/uptime-kuma/compose.yml b/uptime-kuma/compose.yml index 55081ec..153e24d 100644 --- a/uptime-kuma/compose.yml +++ b/uptime-kuma/compose.yml @@ -17,6 +17,12 @@ services: - "traefik.http.routers.uptime.rule=Host(`uptime.xiteng.site`)" - "traefik.http.services.uptime.loadbalancer.server.port=3001" - "traefik.http.routers.uptime.entrypoints=websecure" + # ForwardAuth via Authentik Outpost + - "traefik.http.middlewares.authentik-auth.forwardauth.address=http://authentik-outpost:9000/outpost.goauthentik.io/auth/traefik" + - "traefik.http.middlewares.authentik-auth.forwardauth.trustForwardHeader=true" + - "traefik.http.middlewares.authentik-auth.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name" + - "traefik.http.routers.uptime.middlewares=authentik-auth" + - "traefik.http.routers.uptime-http.middlewares=authentik-auth" - "traefik.http.routers.uptime-http.rule=Host(`uptime.xiteng.site`)" - "traefik.http.routers.uptime-http.service=uptime" - "traefik.http.routers.uptime-http.entrypoints=web" @@ -25,7 +31,7 @@ services: - "homepage.group=我的服务" - "homepage.name=Uptime Kuma" - "homepage.icon=uptime-kuma" - - "homepage.href=https://status.xiteng.site" + - "homepage.href=https://uptime.xiteng.site" - "homepage.description=网站监控工具" networks: - homelab_net