docs(adr) — stockage objet MinIO : qui déclare quoi, et qui détient quoi
Trois questions indépendantes, tranchées lors du branchement de Kadans sur MinIO (2026-07-26) : qui déclare les buckets d'une app, qui détient les identifiants capables de les créer, et comment l'app lit les siens. La décision de fond est du fondateur : CHACUN SON PÉRIMÈTRE. Une application déclare ses buckets depuis son propre dépôt ; `tools` fournit le serveur, un module de standardisation et un compte de provisionnement — pas la liste. Une première version faisait tout porter par l'infra partagée : à ce rythme, chaque bucket de chaque app devenait une PR sur le dépôt commun. L'ADR consigne aussi les trois identités et leurs portées (root / provisionneur / compte de service), pourquoi la lecture des identifiants est une propriété inconditionnelle de la plateforme plutôt qu'une déclaration par app, et pourquoi les octets ne transitent pas par l'API — avec les conséquences que ça impose (endpoint public, CORS aux origines exactes, pas de basic-auth sur l'ingress S3). Les alternatives écartées sont listées avec leur motif, dont deux que j'avais moi-même proposées et qui étaient plus faibles. Deux limites assumées y figurent : le provisionneur est un secret PARTAGÉ entre rôles CI (sa compromission permet de créer des buckets, pas de lire des objets), et les noms d'actions d'administration MinIO n'ont pas été éprouvés contre le serveur au moment d'écrire. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01CoafGWmRVESaWX819USUUA
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
# Home lab
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#e67e22'}}}%%
|
||||
graph LR
|
||||
%% Styles
|
||||
classDef internet fill:#f9f9f9,stroke:#999,color:#333,font-size:12px;
|
||||
classDef arcodange fill:#e6e6e6,stroke:#555,color:#333,font-size:12px;
|
||||
classDef cluster fill:#d4d4d4,stroke:#777,color:#333,font-size:12px;
|
||||
|
||||
%% Internet
|
||||
subgraph "Internet / Le monde"
|
||||
direction TB
|
||||
DuckDNS["🌐 DuckDNS<br>(duckdns.org)"]:::internet
|
||||
GoogleCloud["☁️ Google Cloud<br>(cloud.google.com)"]:::internet
|
||||
Cloudflare["🛡️ Cloudflare<br>(cloudflare.com)"]:::internet
|
||||
OVHcloud["🖥️ OVHcloud<br>(ovhcloud.com)"]:::internet
|
||||
Zoho["📧 Zoho<br>(zoho.eu)"]:::internet
|
||||
CrowdSec["🔒 CrowdSec<br>(crowdsec.net)"]:::internet
|
||||
end
|
||||
|
||||
%% Bureaux Arcodange
|
||||
subgraph "Bureaux Arcodange"
|
||||
direction TB
|
||||
Livebox["📡 Livebox<br>(gateway)"]:::arcodange
|
||||
MacBook["💻 MacBook Pro M4"]:::arcodange
|
||||
|
||||
subgraph "Cluster Raspberry Pi"
|
||||
direction LR
|
||||
Pi1["🐧 pi1"]:::cluster
|
||||
Pi2["🐧 pi2"]:::cluster
|
||||
Pi3["🐧 pi3"]:::cluster
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Network
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#e67e22'}}}%%
|
||||
graph LR
|
||||
%% Styles
|
||||
classDef internet fill:#f9f9f9,stroke:#999,color:#333,font-size:12px;
|
||||
classDef arcodange fill:#e6e6e6,stroke:#555,color:#333,font-size:12px;
|
||||
classDef k3sServer fill:#ff9999,stroke:#cc0000,color:#333,font-size:12px;
|
||||
classDef k3sAgent fill:#99ff99,stroke:#00cc00,color:#333,font-size:12px;
|
||||
classDef traefik fill:#9999ff,stroke:#0000cc,color:#333,font-size:12px;
|
||||
classDef dns fill:#ffff99,stroke:#cccc00,color:#333,font-size:12px;
|
||||
classDef cloudflared fill:#ccccff,stroke:#6666cc,color:#333,font-size:12px;
|
||||
|
||||
%% Internet
|
||||
subgraph "Internet / Le monde"
|
||||
direction TB
|
||||
DuckDNS["🌐 DuckDNS<br>(duckdns.org)"]:::internet
|
||||
subgraph "Cloudflare"
|
||||
CloudflareService["🛡️ Cloudflare<br>(cloudflare.com)"]:::internet
|
||||
DNSNote@{ shape: text, label: "redirige *.arcodange.fr<br>→ Cloudflared Tunnel" }
|
||||
end
|
||||
end
|
||||
|
||||
%% Bureaux Arcodange
|
||||
subgraph "Bureaux Arcodange"
|
||||
direction TB
|
||||
Livebox["📡 Livebox<br>(passerelle)"]:::arcodange
|
||||
MacBook["💻 MacBook Pro M4"]:::arcodange
|
||||
|
||||
%% Cluster Raspberry Pi
|
||||
subgraph "Cluster Raspberry Pi"
|
||||
direction LR
|
||||
|
||||
%% Noeud pi1
|
||||
subgraph "pi1"
|
||||
Pi1["🐧 pi1"]:::k3sServer
|
||||
subgraph "K3s (Server)"
|
||||
Traefik["🚀 Traefik"]:::traefik
|
||||
Cloudflared["☁️ Cloudflared Tunnel"]:::cloudflared
|
||||
end
|
||||
PiHole1["🧹 Pi-hole"]:::dns
|
||||
NS1@{ shape: text, label: "nameserver : pi3" }
|
||||
DNSArcodange@{ shape: text, label: ".arcodange.lab<br>→ Traefik", color:#cccc00 }
|
||||
end
|
||||
|
||||
%% Noeud pi2
|
||||
subgraph "pi2"
|
||||
Pi2["🐧 pi2"]:::k3sAgent
|
||||
end
|
||||
|
||||
%% Noeud pi3
|
||||
subgraph "pi3"
|
||||
Pi3["🐧 pi3"]:::k3sAgent
|
||||
PiHole3["🧹 Pi-hole"]:::dns
|
||||
NS3@{ shape: text, label: "nameserver : pi1" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
%% Connexions
|
||||
%% Pi-hole → Livebox (DNS)
|
||||
PiHole1 -.->|DNS| Livebox
|
||||
PiHole3 -.->|DNS| Livebox
|
||||
|
||||
%% Cloudflare → Cloudflared Tunnel → Traefik
|
||||
CloudflareService -.-> Cloudflared
|
||||
Cloudflared --> Traefik
|
||||
|
||||
%% Livebox → Traefik (HTTPS)
|
||||
Livebox -->|redirige HTTPS| Traefik
|
||||
|
||||
```
|
||||
|
||||
## with Gitea
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#e67e22'}}}%%
|
||||
graph LR
|
||||
%% Styles
|
||||
classDef internet fill:#f9f9f9,stroke:#999,color:#333,font-size:12px;
|
||||
classDef arcodange fill:#e6e6e6,stroke:#555,color:#333,font-size:12px;
|
||||
classDef k3sServer fill:#ff9999,stroke:#cc0000,color:#333,font-size:12px;
|
||||
classDef k3sAgent fill:#99ff99,stroke:#00cc00,color:#333,font-size:12px;
|
||||
classDef traefik fill:#9999ff,stroke:#0000cc,color:#333,font-size:12px;
|
||||
classDef dns fill:#ffff99,stroke:#cccc00,color:#333,font-size:12px;
|
||||
classDef cloudflared fill:#ccccff,stroke:#6666cc,color:#333,font-size:12px;
|
||||
classDef gitea fill:#ffcc99,stroke:#cc9966,color:#333,font-size:12px;
|
||||
classDef cms fill:#99ffcc,stroke:#66cc99,color:#333,font-size:12px;
|
||||
classDef domainFR fill:#99ccff,stroke:#0066cc,color:#333,font-size:12px;
|
||||
classDef domainLAB fill:#ccff99,stroke:#66cc66,color:#333,font-size:12px;
|
||||
|
||||
%% Internet
|
||||
subgraph "Internet / Le monde"
|
||||
direction TB
|
||||
DuckDNS["🌐 DuckDNS<br>(duckdns.org)"]:::internet
|
||||
subgraph "Cloudflare"
|
||||
CloudflareService["🛡️ Cloudflare<br>(cloudflare.com)"]:::internet
|
||||
DNSNote@{ shape: text, label: "redirige *.arcodange.fr<br>→ Cloudflared Tunnel" }
|
||||
end
|
||||
end
|
||||
|
||||
%% Bureaux Arcodange
|
||||
subgraph "Bureaux Arcodange"
|
||||
direction TB
|
||||
Livebox["📡 Livebox<br>(passerelle)"]:::arcodange
|
||||
MacBook["💻 MacBook Pro M4"]:::arcodange
|
||||
|
||||
%% Cluster Raspberry Pi
|
||||
subgraph "Cluster Raspberry Pi"
|
||||
direction LR
|
||||
|
||||
%% Noeud pi1
|
||||
subgraph "pi1"
|
||||
Pi1["🐧 pi1"]:::k3sServer
|
||||
subgraph "K3s (Server)"
|
||||
Traefik["🚀 Traefik"]:::traefik
|
||||
Cloudflared["☁️ Cloudflared Tunnel"]:::cloudflared
|
||||
end
|
||||
PiHole1["🧹 Pi-hole"]:::dns
|
||||
GiteaRunner1["🏃 Gitea Act Runner"]:::gitea
|
||||
NS1@{ shape: text, label: "nameserver : pi3" }
|
||||
DNSArcodange@{ shape: text, label: ".arcodange.lab<br>→ Traefik", color:#cccc00 }
|
||||
end
|
||||
|
||||
%% Noeud pi2
|
||||
subgraph "pi2"
|
||||
Pi2["🐧 pi2"]:::k3sAgent
|
||||
Gitea["🦊 Gitea"]:::gitea
|
||||
subgraph "K3s (Agent)"
|
||||
CMS["📝 CMS"]:::cms
|
||||
end
|
||||
end
|
||||
|
||||
%% Noeud pi3
|
||||
subgraph "pi3"
|
||||
Pi3["🐧 pi3"]:::k3sAgent
|
||||
PiHole3["🧹 Pi-hole"]:::dns
|
||||
GiteaRunner3["🏃 Gitea Act Runner"]:::gitea
|
||||
NS3@{ shape: text, label: "nameserver : pi1" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
%% Connexions Pi-hole → Livebox (DNS)
|
||||
PiHole1 -.->|DNS| Livebox
|
||||
PiHole3 -.->|DNS| Livebox
|
||||
|
||||
%% Cloudflare → Cloudflared Tunnel → Traefik
|
||||
CloudflareService -.-> Cloudflared
|
||||
Cloudflared --> Traefik
|
||||
|
||||
%% Livebox → Traefik (HTTPS)
|
||||
Livebox -->|redirige HTTPS| Traefik
|
||||
|
||||
%% Gitea Act Runner → Gitea
|
||||
GiteaRunner1 -.->|communication| Gitea
|
||||
GiteaRunner3 -.->|communication| Gitea
|
||||
|
||||
%% Domains → CMS via Traefik
|
||||
Traefik -->|cms-rec.arcodange.fr| CMS:::domainFR
|
||||
Traefik -->|www.arcodange.lab| CMS:::domainLAB
|
||||
|
||||
```
|
||||
|
||||
### with security
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#05e2ff1c'}}}%%
|
||||
graph LR
|
||||
%% Styles
|
||||
classDef internet fill:#f9f9f9,stroke:#999,color:#333,font-size:12px;
|
||||
classDef arcodange fill:#e6e6e6,stroke:#555,color:#333,font-size:12px;
|
||||
classDef k3sServer fill:#ff9999,stroke:#cc0000,color:#333,font-size:12px;
|
||||
classDef k3sAgent fill:#99ff99,stroke:#00cc00,color:#333,font-size:12px;
|
||||
classDef traefik fill:#9999ff,stroke:#0000cc,color:#333,font-size:12px;
|
||||
classDef dns fill:#ffff99,stroke:#cccc00,color:#333,font-size:12px;
|
||||
classDef cloudflared fill:#ccccff,stroke:#6666cc,color:#333,font-size:12px;
|
||||
classDef gitea fill:#ffcc99,stroke:#cc9966,color:#333,font-size:12px;
|
||||
classDef cms fill:#99ffcc,stroke:#66cc99,color:#333,font-size:12px;
|
||||
classDef domainFR fill:#99ccff,stroke:#0066cc,color:#333,font-size:12px;
|
||||
classDef domainLAB fill:#ccff99,stroke:#66cc66,color:#333,font-size:12px;
|
||||
classDef crowdsec fill:#ff99cc,stroke:#cc6699,color:#333,font-size:12px;
|
||||
classDef security fill:#ffcccc,stroke:#cc9999,color:#333,font-size:12px;
|
||||
|
||||
%% Internet
|
||||
subgraph "Internet / Le monde"
|
||||
direction TB
|
||||
subgraph "Cloudflare"
|
||||
CloudflareService["🛡️ Cloudflare<br>(cloudflare.com)"]:::internet
|
||||
DNSNote@{ shape: text, label: "redirige *.arcodange.fr<br>→ Cloudflared Tunnel" }
|
||||
end
|
||||
end
|
||||
|
||||
%% Bureaux Arcodange
|
||||
subgraph "Bureaux Arcodange"
|
||||
direction TB
|
||||
Livebox["📡 Livebox<br>(passerelle)"]:::arcodange
|
||||
DNSArcodange@{ shape: text, label: ".arcodange.lab<br>→ Traefik" }
|
||||
|
||||
%% Cluster Raspberry Pi
|
||||
subgraph "Cluster Raspberry Pi"
|
||||
direction LR
|
||||
|
||||
%% Noeud pi1
|
||||
subgraph "pi1"
|
||||
Pi1["🐧 pi1"]:::k3sServer
|
||||
subgraph "K3s (Server)"
|
||||
Traefik["🚀 Traefik"]:::traefik
|
||||
Cloudflared["☁️ Cloudflared Tunnel"]:::cloudflared
|
||||
end
|
||||
PiHole1["🧹 Pi-hole"]:::dns
|
||||
GiteaRunner1["🏃 Gitea Act Runner"]:::gitea
|
||||
NS1@{ shape: text, label: "nameserver : pi3" }
|
||||
end
|
||||
|
||||
%% Noeud pi2
|
||||
subgraph "pi2"
|
||||
Pi2["🐧 pi2"]:::k3sAgent
|
||||
Gitea["🦊 Gitea"]:::gitea
|
||||
subgraph "K3s (Agent)"
|
||||
CMS["📝 CMS"]:::cms
|
||||
end
|
||||
end
|
||||
|
||||
%% Noeud pi3
|
||||
subgraph "pi3"
|
||||
Pi3["🐧 pi3"]:::k3sAgent
|
||||
PiHole3["🧹 Pi-hole"]:::dns
|
||||
GiteaRunner3["🏃 Gitea Act Runner"]:::gitea
|
||||
subgraph "K3s (Agent)"
|
||||
CrowdSec["🔒 CrowdSec"]:::crowdsec
|
||||
end
|
||||
NS3@{ shape: text, label: "nameserver : pi1" }
|
||||
end
|
||||
end
|
||||
|
||||
%% Sécurité combinée
|
||||
subgraph "Sécurité"
|
||||
Turnstile["🛡️ Cloudflared Turnstile<br>(CAPTCHA)"]:::security
|
||||
BannedIPs["🚫 CrowdSec<br>(IP bannies)"]:::security
|
||||
end
|
||||
end
|
||||
|
||||
%% Connexions Pi-hole → Livebox (DNS)
|
||||
PiHole1 -.->|DNS| Livebox
|
||||
PiHole3 -.->|DNS| Livebox
|
||||
|
||||
%% Internet → Livebox
|
||||
CloudflareService --> Livebox
|
||||
|
||||
%% Gitea Act Runner → Gitea
|
||||
GiteaRunner1 -.->|communication| Gitea
|
||||
GiteaRunner3 -.->|communication| Gitea
|
||||
|
||||
%% Cloudflare → Cloudflared Tunnel → Sécurité → Traefik
|
||||
CloudflareService -.-> Cloudflared
|
||||
Cloudflared --> Turnstile
|
||||
Turnstile --> BannedIPs
|
||||
BannedIPs --> Traefik
|
||||
|
||||
%% Livebox → Sécurité → Traefik
|
||||
Livebox --> Turnstile
|
||||
|
||||
%% Domaines → Gitea/CMS via Traefik
|
||||
Traefik -->|gitea.arcodange.fr| Gitea:::domainFR
|
||||
Traefik -->|gitea.arcodange.lab| Gitea:::domainLAB
|
||||
Traefik -->|cms-rec.arcodange.fr| CMS:::domainFR
|
||||
Traefik -->|www.arcodange.lab| CMS:::domainLAB
|
||||
|
||||
```
|
||||
## CI/CD Architecture
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#e67e22'}}}%%
|
||||
graph TD
|
||||
%% Styles
|
||||
classDef gitea fill:#ffcc99,stroke:#cc9966,color:#333;
|
||||
classDef argocd fill:#99ffcc,stroke:#66cc99,color:#333;
|
||||
classDef vault fill:#ccccff,stroke:#6666cc,color:#333;
|
||||
classDef k3s fill:#ff9999,stroke:#cc0000,color:#333;
|
||||
classDef runner fill:#ffff99,stroke:#cccc00,color:#333;
|
||||
|
||||
%% Components
|
||||
Gitea["Gitea (pi2)"]:::gitea
|
||||
ArgoCD["ArgoCD (k3s)"]:::argocd
|
||||
Vault["Vault (k3s/tools)"]:::vault
|
||||
Runner1["Gitea Act Runner (pi1)"]:::runner
|
||||
Runner2["Gitea Act Runner (pi3)"]:::runner
|
||||
VaultOperator["Vault Secrets Operator (k3s/tools)"]:::vault
|
||||
k3s["k3s Cluster"]:::k3s
|
||||
WebApp["WebApp (k3s)"]:::k3s
|
||||
|
||||
%% Workflow
|
||||
Gitea -->|OIDC Auth| Vault
|
||||
Gitea -->|Trigger CI| Runner1
|
||||
Gitea -->|Trigger CI| Runner2
|
||||
Runner1 -->|Deploy to| k3s
|
||||
Runner2 -->|Deploy to| k3s
|
||||
ArgoCD -->|GitOps Sync| Gitea
|
||||
ArgoCD -->|Deploy Apps| k3s
|
||||
VaultOperator -->|Inject Secrets| WebApp
|
||||
Vault -->|Secrets| VaultOperator
|
||||
WebApp -->|Uses| VaultOperator
|
||||
|
||||
%% Annotations
|
||||
linkStyle 0,1,2,3,4,5,6,7 stroke:#999,stroke-width:1px;
|
||||
|
||||
%% Notes
|
||||
note1["Custom runner image includes<br>.lab CA cert"]
|
||||
Runner1 --> note1
|
||||
Runner2 --> note1
|
||||
|
||||
%% OIDC Workflow
|
||||
note2["OIDC Flow:<br>1. Runner executes vault_oauth__sh_b64<br>2. Obtains ID token from Gitea<br>3. Authenticates with Vault"]
|
||||
Runner1 --> note2
|
||||
Runner2 --> note2
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user