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
12 KiB
12 KiB
Home lab
%%{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
%%{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
%%{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
%%{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
%%{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