Update doc / change page deny
Terraform Apply / Terraform Apply (push) Successful in 2m26s

This commit is contained in:
2026-07-31 13:38:47 +02:00
parent f8201d9d06
commit 7edd1c9938
3 changed files with 357 additions and 71 deletions
+5 -42
View File
@@ -21,51 +21,12 @@ Dans un modèle classique, on ouvre des ports sur sa box ou on installe un VPN :
## Vue d'ensemble ## Vue d'ensemble
```mermaid ![Architecture Zero Trust tips-of-mine.org](docs/architecture.svg)
flowchart LR
subgraph POSTES["🖥️ Postes de travail"]
NAV["Navigateur"]
WARP["Agent WARP<br/>multi-OS"]
end
subgraph EDGE["☁️ Réseau Cloudflare"]
direction TB
ACCESS["🔐 Access<br/>authentification & politiques"]
GW["🛡️ Gateway<br/>filtrage DNS / HTTP / réseau"]
DENY["📄 Worker<br/>page de refus"]
end
subgraph IDP["🪪 Fournisseurs d'identité"]
AUTHENTIK["Authentik OIDC"]
AZUREAD["Azure Entra ID"]
GOOGLE["Google OAuth"]
end
subgraph HOME["🏠 Réseau domestique - 10.0.x.x"]
CFD["connecteur cloudflared"]
HA["Home Assistant"]
WEB["Apps web internes"]
SSHSRV["Serveur SSH"]
end
subgraph SITES["🏢 Sites distants d'entreprise"]
PARIS["Site Paris<br/>10.100.0.0/16"]
LYON["Site Lyon<br/>10.100.0.0/16<br/>même CIDR !"]
end
NAV -- "https://home.tips-of-mine.org" --> ACCESS
WARP -- "tout le trafic du poste" --> GW
ACCESS <-->|"vérification d'identité"| IDP
ACCESS -- "refus ➜ explication" --> DENY
ACCESS -- "tunnel (connexion sortante)" --> CFD
CFD --> HA & WEB & SSHSRV
GW -- "réseau privé virtuel" --> SITES
PARIS -."vnet-site-paris".- GW
LYON -."vnet-site-lyon".- GW
```
Point clé : les flèches vers le réseau domestique et les sites passent par des tunnels **initiés depuis l'intérieur**. Couper cloudflared = plus aucun accès possible, il n'existe pas de « porte » à attaquer. Point clé : les flèches vers le réseau domestique et les sites passent par des tunnels **initiés depuis l'intérieur**. Couper cloudflared = plus aucun accès possible, il n'existe pas de « porte » à attaquer.
> 🖊️ Le schéma est un SVG versionné dans [docs/architecture.svg](docs/architecture.svg) — éditable à la main (texte) ou dans Inkscape, et suivi dans les diffs comme le reste du code.
## Parcours d'un accès utilisateur ## Parcours d'un accès utilisateur
Ce qui se passe entre « je tape l'URL » et « je vois l'application » : Ce qui se passe entre « je tape l'URL » et « je vois l'application » :
@@ -274,6 +235,8 @@ Toutes alimentent les mêmes concepts : rule groups, politiques réutilisables,
| `Account` | `Zero Trust` | **Edit** | **Required** | | `Account` | `Zero Trust` | **Edit** | **Required** |
| `Account` | `Access: Apps and Policies` | **Edit** | **Required** | | `Account` | `Access: Apps and Policies` | **Edit** | **Required** |
| `Account` | `Access: Service Tokens` | **Edit** | **Required** | | `Account` | `Access: Service Tokens` | **Edit** | **Required** |
| `Account` | `Workers Scripts` | **Edit** | **Required** (page de refus) |
| `Zone` | `Workers Routes` | **Edit** | **Required** (page de refus) |
| `Account` | `Digital Experience Monitoring` | **Edit** | For `dex_enabled = true` | | `Account` | `Digital Experience Monitoring` | **Edit** | For `dex_enabled = true` |
| `Account` | `Zero Trust: DLP` | **Edit** | For `dlp_enabled = true` (+ abonnement DLP) | | `Account` | `Zero Trust: DLP` | **Edit** | For `dlp_enabled = true` (+ abonnement DLP) |
| `Account` | `Zero Trust: PII` | **Read** | **Recommended** | | `Account` | `Zero Trust: PII` | **Read** | **Recommended** |
+227
View File
@@ -0,0 +1,227 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1660 990" font-family="Segoe UI, Arial, sans-serif">
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#374151"/>
</marker>
<marker id="arrowOrange" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#f6821f"/>
</marker>
</defs>
<rect x="0" y="0" width="1660" height="990" fill="#ffffff"/>
<text x="30" y="45" font-size="22" font-weight="700" fill="#111827">Architecture Zero Trust — tips-of-mine.org</text>
<text x="30" y="68" font-size="12" fill="#6b7280">Aucun port entrant ouvert : tous les tunnels sont initiés depuis l'intérieur, chaque accès est authentifié et contextualisé.</text>
<!-- ══════════ CHAÎNE D'OUTILLAGE (haut) ══════════ -->
<rect x="380" y="30" width="200" height="64" rx="8" fill="#f9fafb" stroke="#6b7280"/>
<text x="480" y="55" font-size="13" font-weight="700" fill="#111827" text-anchor="middle">Gitea — repo &amp; CI/CD</text>
<text x="480" y="74" font-size="11" fill="#6b7280" text-anchor="middle">validate · apply · drift quotidien</text>
<rect x="640" y="30" width="160" height="64" rx="8" fill="#f9fafb" stroke="#6b7280"/>
<text x="720" y="55" font-size="13" font-weight="700" fill="#111827" text-anchor="middle">Terraform</text>
<text x="720" y="74" font-size="11" fill="#6b7280" text-anchor="middle">state S3 verrouillé</text>
<rect x="860" y="30" width="230" height="64" rx="8" fill="#f9fafb" stroke="#6b7280"/>
<text x="975" y="55" font-size="13" font-weight="700" fill="#111827" text-anchor="middle">Vault — secrets</text>
<text x="975" y="74" font-size="11" fill="#6b7280" text-anchor="middle">AppRole CI · miroir OpenBao</text>
<line x1="580" y1="62" x2="638" y2="62" stroke="#374151" stroke-width="1.5" marker-end="url(#arrow)"/>
<line x1="800" y1="62" x2="858" y2="62" stroke="#374151" stroke-width="1.5" marker-end="url(#arrow)"/>
<text x="828" y="52" font-size="10" fill="#6b7280" text-anchor="middle">lit</text>
<line x1="720" y1="94" x2="720" y2="158" stroke="#374151" stroke-width="1.5" marker-end="url(#arrow)"/>
<text x="730" y="130" font-size="11" fill="#6b7280">provisionne (API)</text>
<!-- ══════════ UTILISATEURS EXTERNES (gauche haut) ══════════ -->
<rect x="30" y="100" width="250" height="58" rx="8" fill="#ffffff" stroke="#374151"/>
<text x="155" y="123" font-size="13" font-weight="700" fill="#111827" text-anchor="middle">Utilisateurs — navigateur</text>
<text x="155" y="142" font-size="11" fill="#6b7280" text-anchor="middle">https://*.tips-of-mine.org</text>
<path d="M 280 129 L 895 129 L 895 218" fill="none" stroke="#374151" stroke-width="1.5" marker-end="url(#arrow)"/>
<text x="480" y="120" font-size="11" fill="#6b7280">1. requête HTTPS → authentification Access</text>
<!-- ══════════ POSTES DE TRAVAIL (gauche) ══════════ -->
<rect x="30" y="190" width="250" height="240" rx="10" fill="none" stroke="#374151" stroke-dasharray="6 4"/>
<text x="45" y="215" font-size="13" font-weight="700" fill="#111827">Postes de travail — agent WARP</text>
<rect x="45" y="230" width="220" height="38" rx="6" fill="#f9fafb" stroke="#9ca3af"/>
<text x="60" y="253" font-size="12" fill="#111827">💻 Windows — profil + postures</text>
<rect x="45" y="276" width="220" height="38" rx="6" fill="#f9fafb" stroke="#9ca3af"/>
<text x="60" y="299" font-size="12" fill="#111827">💻 macOS — profil + postures</text>
<rect x="45" y="322" width="220" height="38" rx="6" fill="#f9fafb" stroke="#9ca3af"/>
<text x="60" y="345" font-size="12" fill="#111827">💻 Linux — profil + postures</text>
<text x="45" y="388" font-size="10.5" fill="#6b7280">Enrôlement : MFA + domaine mail</text>
<text x="45" y="404" font-size="10.5" fill="#6b7280">Postures : WARP · pare-feu · disque chiffré · OS</text>
<text x="45" y="420" font-size="10.5" fill="#6b7280">Tests DEX depuis les postes</text>
<line x1="280" y1="300" x2="328" y2="300" stroke="#f6821f" stroke-width="2" marker-end="url(#arrowOrange)"/>
<text x="240" y="288" font-size="10.5" fill="#f6821f">tunnel WARP</text>
<!-- ══════════ PANNEAU POLITIQUES (gauche bas) ══════════ -->
<rect x="30" y="470" width="250" height="420" rx="10" fill="#ffffff" stroke="#374151"/>
<text x="45" y="497" font-size="14" font-weight="700" fill="#111827">POLITIQUES</text>
<text x="45" y="524" font-size="12" font-weight="700" fill="#d97706">Applications Access</text>
<text x="55" y="542" font-size="11" fill="#374151">• Home Network (défaut + service token)</text>
<text x="55" y="558" font-size="11" fill="#374151">• Intranet · Competition (MFA, posture)</text>
<text x="55" y="574" font-size="11" fill="#374151">• Browser SSH / VNC / RDP</text>
<text x="55" y="590" font-size="11" fill="#374151">• SSH classique par clé</text>
<text x="45" y="616" font-size="12" font-weight="700" fill="#16a34a">Infrastructure</text>
<text x="55" y="634" font-size="11" fill="#374151">• SSH certificats éphémères (CA)</text>
<text x="45" y="660" font-size="12" font-weight="700" fill="#2563eb">Enrôlement des postes</text>
<text x="55" y="678" font-size="11" fill="#374151">• MFA + domaine tips-of-mine.org</text>
<text x="45" y="704" font-size="12" font-weight="700" fill="#7c3aed">Rule groups</text>
<text x="55" y="722" font-size="11" fill="#374151">• Localisation (pays / continents)</text>
<text x="55" y="738" font-size="11" fill="#374151">• Versions d'OS (postures TF)</text>
<text x="55" y="754" font-size="11" fill="#374151">• Groupes SAML Authentik · Azure</text>
<text x="45" y="780" font-size="12" font-weight="700" fill="#dc2626">Gateway</text>
<text x="55" y="798" font-size="11" fill="#374151">• DNS : malware + liste Pi-hole</text>
<text x="55" y="814" font-size="11" fill="#374151">• L4 : anti-mouvement latéral</text>
<text x="55" y="830" font-size="11" fill="#374151">• HTTP : IA · catégories · DNI banque</text>
<text x="55" y="846" font-size="11" fill="#374151">• DLP (IBAN) · RBI — sous licence</text>
<line x1="280" y1="680" x2="328" y2="640" stroke="#9ca3af" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#arrow)"/>
<!-- ══════════ CLOUDFLARE (centre) ══════════ -->
<rect x="330" y="160" width="760" height="520" rx="14" fill="#fff7ed" stroke="#f6821f" stroke-width="2.5"/>
<path d="M 360 196 q 6 -14 22 -12 q 6 -12 20 -8 q 12 -14 26 -4 q 16 -4 18 12 q 14 4 10 14 l -96 0 q -6 -1 0 -2 z" fill="#f6821f"/>
<text x="470" y="196" font-size="17" font-weight="700" fill="#111827">Cloudflare — Zero Trust</text>
<text x="1070" y="196" font-size="11" fill="#9a3412" text-anchor="end">équipe : tips-of-mine.cloudflareaccess.com</text>
<!-- QUOI -->
<rect x="355" y="220" width="215" height="200" rx="8" fill="#ffffff" stroke="#fdba74"/>
<text x="462" y="244" font-size="12.5" font-weight="700" fill="#9a3412" text-anchor="middle">QUOI</text>
<rect x="370" y="256" width="185" height="44" rx="6" fill="#fff7ed" stroke="#fdba74"/>
<text x="462" y="274" font-size="11" fill="#111827" text-anchor="middle">Vérifier l'identité</text>
<text x="462" y="290" font-size="10" fill="#6b7280" text-anchor="middle">multi-IdP · MFA obligatoire</text>
<rect x="370" y="308" width="185" height="44" rx="6" fill="#fff7ed" stroke="#fdba74"/>
<text x="462" y="326" font-size="11" fill="#111827" text-anchor="middle">Vérifier le poste</text>
<text x="462" y="342" font-size="10" fill="#6b7280" text-anchor="middle">posture en continu</text>
<rect x="370" y="360" width="185" height="44" rx="6" fill="#fff7ed" stroke="#fdba74"/>
<text x="462" y="378" font-size="11" fill="#111827" text-anchor="middle">Accorder selon contexte</text>
<text x="462" y="394" font-size="10" fill="#6b7280" text-anchor="middle">groupe · pays · appareil</text>
<!-- COMMENT -->
<rect x="590" y="220" width="215" height="200" rx="8" fill="#ffffff" stroke="#fdba74"/>
<text x="697" y="244" font-size="12.5" font-weight="700" fill="#9a3412" text-anchor="middle">COMMENT</text>
<rect x="605" y="256" width="185" height="44" rx="6" fill="#fff7ed" stroke="#fdba74"/>
<text x="697" y="274" font-size="11" fill="#111827" text-anchor="middle">Reverse proxy</text>
<text x="697" y="290" font-size="10" fill="#6b7280" text-anchor="middle">rien d'exposé à l'origine</text>
<rect x="605" y="308" width="185" height="44" rx="6" fill="#fff7ed" stroke="#fdba74"/>
<text x="697" y="326" font-size="11" fill="#111827" text-anchor="middle">Routes privées</text>
<text x="697" y="342" font-size="10" fill="#6b7280" text-anchor="middle">réseaux virtuels par site</text>
<rect x="605" y="360" width="185" height="44" rx="6" fill="#fff7ed" stroke="#fdba74"/>
<text x="697" y="378" font-size="11" fill="#111827" text-anchor="middle">Terminal navigateur</text>
<text x="697" y="394" font-size="10" fill="#6b7280" text-anchor="middle">SSH · VNC · RDP</text>
<!-- Access / Gateway -->
<rect x="830" y="220" width="235" height="90" rx="8" fill="#ffffff" stroke="#f6821f" stroke-width="1.8"/>
<text x="947" y="248" font-size="13" font-weight="700" fill="#111827" text-anchor="middle">Cloudflare Access</text>
<text x="947" y="268" font-size="10.5" fill="#6b7280" text-anchor="middle">authentification &amp; politiques</text>
<text x="947" y="284" font-size="10.5" fill="#6b7280" text-anchor="middle">apps · service tokens · mTLS</text>
<rect x="830" y="330" width="235" height="90" rx="8" fill="#ffffff" stroke="#f6821f" stroke-width="1.8"/>
<text x="947" y="358" font-size="13" font-weight="700" fill="#111827" text-anchor="middle">Gateway</text>
<text x="947" y="378" font-size="10.5" fill="#6b7280" text-anchor="middle">filtrage DNS · réseau L4 · HTTP</text>
<text x="947" y="394" font-size="10.5" fill="#6b7280" text-anchor="middle">inspection TLS (certificat déployé)</text>
<!-- Worker + observabilité -->
<rect x="355" y="450" width="450" height="60" rx="8" fill="#ffffff" stroke="#fdba74"/>
<text x="580" y="474" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">Worker — page de refus professionnelle</text>
<text x="580" y="492" font-size="10.5" fill="#6b7280" text-anchor="middle">denied.tips-of-mine.org · réf. incident (Ray ID) · support / ticketing</text>
<line x1="892" y1="310" x2="805" y2="460" stroke="#9ca3af" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#arrow)"/>
<text x="862" y="405" font-size="10" fill="#6b7280">si refus</text>
<rect x="830" y="450" width="235" height="60" rx="8" fill="#ffffff" stroke="#fdba74"/>
<text x="947" y="474" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">Observabilité</text>
<text x="947" y="492" font-size="10.5" fill="#6b7280" text-anchor="middle">DEX · Logpush → S3/R2 · logs Gateway</text>
<!-- App launcher -->
<rect x="355" y="530" width="710" height="60" rx="8" fill="#ffffff" stroke="#fdba74"/>
<text x="710" y="554" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">App Launcher — portail unique des applications autorisées</text>
<text x="710" y="572" font-size="10.5" fill="#6b7280" text-anchor="middle">Home Assistant · Intranet · Competition · Browser SSH/VNC/RDP</text>
<text x="710" y="650" font-size="11" font-style="italic" fill="#9a3412" text-anchor="middle">Chaque requête = identité + appareil + contexte vérifiés, à chaque fois — jamais de confiance implicite</text>
<!-- ══════════ IdP (bas centre) ══════════ -->
<rect x="330" y="720" width="760" height="180" rx="10" fill="none" stroke="#7c3aed" stroke-dasharray="6 4"/>
<text x="350" y="746" font-size="13" font-weight="700" fill="#7c3aed">FOURNISSEURS D'IDENTITÉ (briques interchangeables)</text>
<rect x="355" y="762" width="225" height="115" rx="8" fill="#f5f3ff" stroke="#7c3aed"/>
<text x="467" y="788" font-size="12.5" font-weight="700" fill="#111827" text-anchor="middle">Authentik — OIDC</text>
<text x="467" y="808" font-size="10.5" fill="#16a34a" font-weight="700" text-anchor="middle">✔ actif</text>
<text x="467" y="828" font-size="10.5" fill="#6b7280" text-anchor="middle">MFA · claims groupes SAML</text>
<text x="467" y="844" font-size="10.5" fill="#6b7280" text-anchor="middle">Contractors · IT Admin · Sales…</text>
<rect x="610" y="762" width="225" height="115" rx="8" fill="#ffffff" stroke="#a78bfa"/>
<text x="722" y="788" font-size="12.5" font-weight="700" fill="#111827" text-anchor="middle">Azure Entra ID</text>
<text x="722" y="808" font-size="10.5" fill="#6b7280" text-anchor="middle">⏸ optionnel — azure_idp_enabled</text>
<text x="722" y="828" font-size="10.5" fill="#6b7280" text-anchor="middle">groupes Entra · Conditional Access</text>
<rect x="865" y="762" width="200" height="115" rx="8" fill="#ffffff" stroke="#a78bfa"/>
<text x="965" y="788" font-size="12.5" font-weight="700" fill="#111827" text-anchor="middle">Google OAuth</text>
<text x="965" y="808" font-size="10.5" fill="#6b7280" text-anchor="middle">⏸ optionnel — google_idp_enabled</text>
<text x="965" y="828" font-size="10.5" fill="#6b7280" text-anchor="middle">+ code email (OTP)</text>
<line x1="710" y1="718" x2="710" y2="682" stroke="#7c3aed" stroke-width="1.8" marker-end="url(#arrow)"/>
<text x="722" y="706" font-size="10.5" fill="#7c3aed">OIDC / SAML</text>
<!-- ══════════ RÉSEAU DOMESTIQUE (droite haut) ══════════ -->
<rect x="1130" y="160" width="500" height="250" rx="10" fill="#f0fdf4" stroke="#16a34a" stroke-width="2"/>
<text x="1150" y="188" font-size="14" font-weight="700" fill="#14532d">🏠 Réseau domestique — 10.0.x.0/24</text>
<rect x="1150" y="205" width="165" height="70" rx="8" fill="#ffffff" stroke="#16a34a"/>
<text x="1232" y="230" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">cloudflared</text>
<text x="1232" y="248" font-size="10" fill="#6b7280" text-anchor="middle">connecteur (sortant)</text>
<text x="1232" y="263" font-size="10" fill="#6b7280" text-anchor="middle">replicas HA possibles</text>
<rect x="1345" y="205" width="130" height="44" rx="6" fill="#ffffff" stroke="#86efac"/>
<text x="1410" y="223" font-size="11" fill="#111827" text-anchor="middle">Home Assistant</text>
<text x="1410" y="239" font-size="9.5" fill="#6b7280" text-anchor="middle">home.tips-of-mine.org</text>
<rect x="1490" y="205" width="125" height="44" rx="6" fill="#ffffff" stroke="#86efac"/>
<text x="1552" y="223" font-size="11" fill="#111827" text-anchor="middle">http-app</text>
<text x="1552" y="239" font-size="9.5" fill="#6b7280" text-anchor="middle">http-app.…org</text>
<rect x="1345" y="259" width="130" height="44" rx="6" fill="#ffffff" stroke="#86efac"/>
<text x="1410" y="277" font-size="11" fill="#111827" text-anchor="middle">secure-app</text>
<text x="1410" y="293" font-size="9.5" fill="#6b7280" text-anchor="middle">secure.…org + Access</text>
<rect x="1490" y="259" width="125" height="44" rx="6" fill="#ffffff" stroke="#86efac"/>
<text x="1552" y="277" font-size="11" fill="#111827" text-anchor="middle">Serveur SSH</text>
<text x="1552" y="293" font-size="9.5" fill="#6b7280" text-anchor="middle">ssh.…org — clé/cert</text>
<text x="1150" y="330" font-size="10.5" fill="#15803d">Pi-hole 10.0.4.2 — la liste publicitaire alimente aussi Gateway (MàJ quotidienne CI)</text>
<text x="1150" y="350" font-size="10.5" font-weight="700" fill="#15803d">⛔ Aucun port entrant ouvert sur la box</text>
<line x1="1148" y1="240" x2="1092" y2="240" stroke="#16a34a" stroke-width="2" marker-end="url(#arrow)"/>
<text x="1096" y="228" font-size="10" fill="#16a34a">tunnel sortant</text>
<!-- ══════════ SITES DISTANTS (droite milieu) ══════════ -->
<rect x="1130" y="440" width="500" height="190" rx="10" fill="none" stroke="#2563eb" stroke-dasharray="6 4"/>
<text x="1150" y="466" font-size="13" font-weight="700" fill="#1e40af">🏢 Sites distants d'entreprise (CIDR identiques)</text>
<rect x="1150" y="482" width="225" height="120" rx="8" fill="#eff6ff" stroke="#2563eb"/>
<text x="1262" y="506" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">Site Paris — siège</text>
<text x="1262" y="526" font-size="10.5" fill="#374151" text-anchor="middle">cloudflared · LAN 10.100.0.0/16</text>
<text x="1262" y="544" font-size="10.5" fill="#2563eb" text-anchor="middle">vnet-site-paris</text>
<text x="1262" y="562" font-size="10" fill="#6b7280" text-anchor="middle">serveurs · AD · imprimantes</text>
<rect x="1400" y="482" width="225" height="120" rx="8" fill="#eff6ff" stroke="#2563eb"/>
<text x="1512" y="506" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">Site Lyon — agence</text>
<text x="1512" y="526" font-size="10.5" fill="#374151" text-anchor="middle">cloudflared · LAN 10.100.0.0/16</text>
<text x="1512" y="544" font-size="10.5" fill="#2563eb" text-anchor="middle">vnet-site-lyon</text>
<text x="1512" y="562" font-size="10" fill="#dc2626" text-anchor="middle">⚠ même CIDR — isolé par vnet</text>
<text x="1150" y="620" font-size="10" fill="#6b7280">Variante WARP Connector (bidirectionnel) prête — IDs à renseigner</text>
<line x1="1148" y1="540" x2="1092" y2="520" stroke="#2563eb" stroke-width="1.8" marker-end="url(#arrow)"/>
<text x="1080" y="505" font-size="10" fill="#2563eb" text-anchor="end">un tunnel par site</text>
<!-- ══════════ CLOUDS (droite bas) ══════════ -->
<rect x="1130" y="660" width="500" height="130" rx="10" fill="none" stroke="#9ca3af" stroke-dasharray="6 4"/>
<text x="1150" y="686" font-size="13" font-weight="700" fill="#4b5563">☁️ Multi-cloud (exemples du catalogue)</text>
<rect x="1150" y="700" width="110" height="66" rx="8" fill="#f9fafb" stroke="#9ca3af"/>
<text x="1205" y="726" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">AWS</text>
<text x="1205" y="744" font-size="9.5" fill="#6b7280" text-anchor="middle">vpc-aws + route</text>
<rect x="1272" y="700" width="110" height="66" rx="8" fill="#f9fafb" stroke="#9ca3af"/>
<text x="1327" y="726" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">GCP</text>
<text x="1327" y="744" font-size="9.5" fill="#6b7280" text-anchor="middle">vpc-gcp + route</text>
<rect x="1394" y="700" width="110" height="66" rx="8" fill="#f9fafb" stroke="#9ca3af"/>
<text x="1449" y="726" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">Azure</text>
<text x="1449" y="744" font-size="9.5" fill="#6b7280" text-anchor="middle">vpc-azure + route</text>
<rect x="1516" y="700" width="110" height="66" rx="8" fill="#f9fafb" stroke="#9ca3af"/>
<text x="1571" y="726" font-size="12" font-weight="700" fill="#111827" text-anchor="middle">OVH</text>
<text x="1571" y="744" font-size="9.5" fill="#6b7280" text-anchor="middle">vpc-ovh + route</text>
<line x1="1148" y1="720" x2="1092" y2="640" stroke="#9ca3af" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#arrow)"/>
<!-- ══════════ LÉGENDE ══════════ -->
<rect x="30" y="920" width="580" height="50" rx="8" fill="#f9fafb" stroke="#e5e7eb"/>
<line x1="50" y1="945" x2="90" y2="945" stroke="#374151" stroke-width="1.8" marker-end="url(#arrow)"/>
<text x="100" y="949" font-size="10.5" fill="#374151">flux actif</text>
<line x1="180" y1="945" x2="220" y2="945" stroke="#9ca3af" stroke-width="1.4" stroke-dasharray="4 3" marker-end="url(#arrow)"/>
<text x="230" y="949" font-size="10.5" fill="#374151">optionnel / conditionnel</text>
<rect x="390" y="935" width="18" height="18" rx="4" fill="#fff7ed" stroke="#f6821f"/>
<text x="416" y="949" font-size="10.5" fill="#374151">edge Cloudflare</text>
<rect x="520" y="935" width="18" height="18" rx="4" fill="#f0fdf4" stroke="#16a34a"/>
<text x="546" y="949" font-size="10.5" fill="#374151">on-premise</text>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

+125 -29
View File
@@ -1,56 +1,149 @@
/** /**
* Page de refus d'accès personnalisée — servie sur denied.tips-of-mine.org * Page de refus d'accès — servie sur denied.tips-of-mine.org
* Toutes les applications Access du repo pointent leur custom_deny_url ici. * Toutes les applications Access du repo pointent leur custom_deny_url ici.
* Déployée par Terraform : Workers-Deny_Page.tf * Déployée par Terraform : Workers-Deny_Page.tf
*
* La page affiche une référence d'incident (le Ray ID Cloudflare de la
* requête) : l'utilisateur la communique au support, qui peut retrouver la
* requête exacte dans les logs Access/Gateway.
*/ */
addEventListener('fetch', (event) => { addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request)); event.respondWith(handleRequest(event.request));
}); });
const SUPPORT_EMAIL = '[email protected]';
const TICKETING_URL = 'https://ticket.tips-of-mine.org'; // adapter à l'outil de ticketing
async function handleRequest(request) { async function handleRequest(request) {
const rayId = request.headers.get('cf-ray') || 'indisponible';
const now = new Date().toLocaleString('fr-FR', { timeZone: 'Europe/Paris', dateStyle: 'long', timeStyle: 'medium' });
const html = `<!DOCTYPE html> const html = `<!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Accès refusé — Tips-Of-Mine</title> <meta name="robots" content="noindex">
<title>Acc&egrave;s refus&eacute; (403) &mdash; Tips-Of-Mine</title>
<style> <style>
:root { color-scheme: light dark; } :root {
body { color-scheme: light dark;
margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; --brand: #f6821f;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --bg: #f3f4f6; --card: #ffffff; --text: #111827; --muted: #6b7280;
background: #f5f5f5; color: #1a1a1a; --border: #e5e7eb; --detail-bg: #f9fafb;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
body { background: #16181d; color: #e8e8e8; } :root {
.card { background: #1f232b !important; box-shadow: 0 4px 24px rgba(0,0,0,.4) !important; } --bg: #101318; --card: #1a1f27; --text: #e5e7eb; --muted: #9ca3af;
--border: #2b3240; --detail-bg: #151a21;
}
}
* { box-sizing: border-box; }
body {
margin: 0; min-height: 100vh; display: flex; flex-direction: column;
align-items: center; justify-content: center; padding: 1.5rem;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
background: var(--bg); color: var(--text);
} }
.card { .card {
background: #fff; border-radius: 12px; box-shadow: 0 4px 24px rgba(0,0,0,.08); width: 100%; max-width: 560px; background: var(--card);
max-width: 520px; margin: 1rem; padding: 2.5rem; text-align: center; border: 1px solid var(--border); border-radius: 14px; overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,.10);
} }
.icon { font-size: 3rem; } .brand {
h1 { font-size: 1.4rem; margin: .75rem 0 .25rem; } display: flex; align-items: center; gap: .75rem;
p { line-height: 1.55; opacity: .85; } padding: 1rem 1.75rem; border-bottom: 1px solid var(--border);
.hint { }
text-align: left; font-size: .9rem; border-left: 3px solid #f6821f; .brand-mark {
padding: .5rem .9rem; margin-top: 1.25rem; opacity: .9; width: 34px; height: 34px; border-radius: 8px; background: var(--brand);
display: flex; align-items: center; justify-content: center; flex: none;
}
.brand-name { font-weight: 700; letter-spacing: .01em; }
.brand-sub { font-size: .78rem; color: var(--muted); }
.body { padding: 1.9rem 1.75rem; }
.status {
display: inline-flex; align-items: center; gap: .45rem;
font-size: .8rem; font-weight: 600; color: #b91c1c;
background: rgba(220, 38, 38, .10); border: 1px solid rgba(220, 38, 38, .25);
padding: .3rem .7rem; border-radius: 999px;
}
@media (prefers-color-scheme: dark) { .status { color: #fca5a5; } }
h1 { font-size: 1.35rem; margin: .9rem 0 .4rem; }
p.lead { margin: 0; color: var(--muted); line-height: 1.6; }
.details {
margin-top: 1.4rem; border: 1px solid var(--border); border-radius: 10px;
background: var(--detail-bg); font-size: .85rem;
}
.details div {
display: flex; justify-content: space-between; gap: 1rem;
padding: .55rem .9rem; border-top: 1px solid var(--border);
}
.details div:first-child { border-top: 0; }
.details dt { color: var(--muted); }
.details dd { margin: 0; font-family: ui-monospace, Consolas, monospace; }
ul.checks { margin: 1.2rem 0 0; padding-left: 1.15rem; color: var(--muted); line-height: 1.8; font-size: .92rem; }
.actions { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: 1.6rem; }
.btn {
display: inline-block; padding: .6rem 1.1rem; border-radius: 8px;
font-size: .92rem; font-weight: 600; text-decoration: none;
}
.btn-primary { background: var(--brand); color: #fff; }
.btn-secondary { border: 1px solid var(--border); color: var(--text); }
.footer {
padding: .9rem 1.75rem; border-top: 1px solid var(--border);
font-size: .78rem; color: var(--muted);
display: flex; justify-content: space-between; flex-wrap: wrap; gap: .5rem;
} }
a { color: #f6821f; }
</style> </style>
</head> </head>
<body> <body>
<main class="card"> <main class="card">
<div class="icon">&#128683;</div> <div class="brand">
<h1>Acc&egrave;s refus&eacute;</h1> <div class="brand-mark">
<p>Votre identit&eacute; a bien &eacute;t&eacute; v&eacute;rifi&eacute;e, mais elle ne remplit pas <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
les conditions d'acc&egrave;s de cette application (groupe, posture du poste, MFA ou localisation).</p> <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
<div class="hint"> </svg>
<strong>Que faire ?</strong><br> </div>
&bull; V&eacute;rifiez que vous &ecirc;tes connect&eacute; avec le bon compte&nbsp;;<br> <div>
&bull; V&eacute;rifiez que le client WARP est actif et votre poste conforme&nbsp;;<br> <div class="brand-name">Tips-Of-Mine</div>
&bull; Si le besoin est l&eacute;gitime, contactez l'administrateur&nbsp;: <div class="brand-sub">S&eacute;curit&eacute; des acc&egrave;s &mdash; Zero Trust</div>
<a href="mailto:[email protected]">[email protected]</a>. </div>
</div>
<div class="body">
<span class="status">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true">
<circle cx="12" cy="12" r="10"/><line x1="4.9" y1="4.9" x2="19.1" y2="19.1"/>
</svg>
Erreur 403 &mdash; Acc&egrave;s refus&eacute;
</span>
<h1>Vous n'&ecirc;tes pas autoris&eacute; &agrave; acc&eacute;der &agrave; cette application</h1>
<p class="lead">Votre identit&eacute; a bien &eacute;t&eacute; v&eacute;rifi&eacute;e, mais elle ne remplit pas
les conditions d'acc&egrave;s d&eacute;finies pour cette ressource&nbsp;: groupe d'autorisation,
conformit&eacute; du poste de travail, authentification multifacteur ou localisation.</p>
<div class="details" role="group" aria-label="D&eacute;tails techniques">
<div><dt>R&eacute;f&eacute;rence incident</dt><dd>${rayId}</dd></div>
<div><dt>Horodatage</dt><dd>${now}</dd></div>
<div><dt>Code</dt><dd>HTTP 403 &mdash; Forbidden</dd></div>
</div>
<ul class="checks">
<li>V&eacute;rifiez que vous &ecirc;tes connect&eacute; avec votre compte professionnel&nbsp;;</li>
<li>V&eacute;rifiez que le client WARP est actif et que votre poste est conforme&nbsp;;</li>
<li>Si votre besoin d'acc&egrave;s est l&eacute;gitime, contactez le support en joignant la <strong>r&eacute;f&eacute;rence incident</strong> ci-dessus.</li>
</ul>
<div class="actions">
<a class="btn btn-primary" href="${TICKETING_URL}">Ouvrir un ticket</a>
<a class="btn btn-secondary" href="mailto:${SUPPORT_EMAIL}?subject=Demande%20d%27acc%C3%A8s%20refus%C3%A9e%20-%20R%C3%A9f.%20${encodeURIComponent(rayId)}">Contacter le support</a>
</div>
</div>
<div class="footer">
<span>&copy; Tips-Of-Mine &mdash; Direction des Syst&egrave;mes d'Information</span>
<span>${SUPPORT_EMAIL}</span>
</div> </div>
</main> </main>
</body> </body>
@@ -58,6 +151,9 @@ async function handleRequest(request) {
return new Response(html, { return new Response(html, {
status: 403, status: 403,
headers: { 'content-type': 'text/html; charset=utf-8' }, headers: {
'content-type': 'text/html; charset=utf-8',
'cache-control': 'no-store',
},
}); });
} }