0 Votes

Modifications pour le document Dashy

Modifié par john le 2026/09/26 16:54

Depuis la version 5.1
modifié par john
sur 2026/09/26 16:54
Commentaire de modification : Il n'y a aucun commentaire pour cette version
À la version 4.1
modifié par john
sur 2026/09/26 16:22
Commentaire de modification : Il n'y a aucun commentaire pour cette version

Résumé

Détails

Propriétés de la Page
Contenu
... ... @@ -1,13 +1,18 @@
1 -Tableau de bord d'accueil du serveur principal : liens vers les services auto-hébergés avec leur état, et widgets. Il compte quatre pages : l'accueil (##conf.yml##) et trois pages secondaires.
1 +Tableau de bord d'accueil : liens vers tous les services auto-hébergés, avec leur état en direct et des widgets, répartis sur une page d'accueil et trois pages secondaires.
2 2  
3 -{{toc/}}
3 +== Fonctionnalités utilisées ==
4 4  
5 -Les valeurs propres à l'infrastructure sont remplacées par des marqueurs ##<ENTRE_CHEVRONS>## : ##<DOMAINE>##, adresses IP, comptes, mots de passe et clés.
5 +* Sections et liens vers les services, avec icônes stockées en local ou issues des jeux d'icônes intégrés.
6 +* Indicateur d'état de chaque service (vérification de disponibilité).
7 +* Configuration multi-pages : une page d'accueil (##conf.yml##) et trois pages secondaires déclarées dans sa section ##pages:##.
8 +* Widgets de contenu dynamique, dont certains interrogent des services locaux ou des API externes avec des identifiants (AdGuard Home, Nextcloud, API de vols et d'actualités).
9 +* Cartes de statistiques GitHub fournies par une instance locale de github-readme-stats.
10 +* Accès protégé par Authelia au niveau de SWAG : aucune page ni aucun fichier de configuration n'est servi sans connexion.
11 +* Proxy CORS personnalisé dans SWAG, qui remplace celui de Dashy pour les widgets en ##useProxy: true##.
12 +* Configuration modifiable depuis l'interface, enregistrée dans les fichiers YAML.
6 6  
7 -== Déploiement ==
14 +== Stack ==
8 8  
9 -=== Stack Portainer ===
10 -
11 11  {{code language="yaml"}}
12 12  services:
13 13   dashy:
... ... @@ -37,19 +37,22 @@
37 37   external: true
38 38  {{/code}}
39 39  
40 -* ##NODE_TLS_REJECT_UNAUTHORIZED=0## : Dashy accepte les certificats auto-signés des services locaux (vérifications d'état, proxy CORS).
41 -* ##user-data## contient toute la configuration. Dashy sert ce dossier à la racine du site.
42 -* Le port ##4000## donne un accès direct depuis le LAN, sans SWAG ni Authelia. SWAG n'en a pas besoin : il joint le conteneur par le réseau ##proxy##.
45 +* ##NODE_TLS_REJECT_UNAUTHORIZED=0## : le serveur de Dashy accepte les certificats non reconnus lors des vérifications d'état et dans son proxy CORS, nécessaire pour les services locaux en https avec un certificat auto-signé.
46 +* ##UID## / ##GID## : propriétaire des fichiers écrits dans ##user-data## (sauvegarde depuis l'interface).
47 +* Port ##4000## : accès direct depuis le LAN, sans SWAG ni Authelia. L'accès normal passe par SWAG, qui joint le conteneur par son nom sur le réseau ##proxy##, port interne ##8080##. Pour que tout accès passe par Authelia, supprimer cette publication de port : SWAG n'en a pas besoin.
48 +* Volume : tout le dossier ##user-data## est monté dans ##/app/user-data##. Dashy sert ce dossier à la racine du site : ##conf.yml##, les pages secondaires et ##item-icons/## sont accessibles par leur nom.
49 +* Réseau ##proxy## : réseau Docker externe partagé avec SWAG, à créer avant le premier déploiement (##docker network create proxy##).
50 +* Mises à jour de l'image : [[Watchtower>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Watchtower.WebHome]].
43 43  
44 -=== SWAG ===
52 +=== Exposition derrière SWAG ===
45 45  
46 -##<APPDATA>/swag/config/nginx/proxy-confs/30-dashy.subdomain.conf## :
54 +Fichier ##<APPDATA>/swag/config/nginx/proxy-confs/30-dashy.subdomain.conf##. Chaque ##location## inclut ##authelia-location.conf##, selon le même schéma que les autres services protégés. Voir aussi [[SWAG>>Wiki Batcave John.Infrastructure.ServeurPrincipal.SWAG.WebHome]].
47 47  
48 48  {{code language="nginx"}}
49 49  server {
50 50   listen 443 ssl;
51 51   listen [::]:443 ssl;
52 - server_name dash.<DOMAINE>;
60 + server_name <DOMAINE_DASHY>;
53 53  
54 54   include /config/nginx/ssl.conf;
55 55   include /config/nginx/snippets/robots-default.conf;
... ... @@ -56,15 +56,14 @@
56 56  
57 57   client_max_body_size 25m;
58 58  
59 - # Authelia devant tout le domaine
67 + # Authelia : tout le domaine exige une connexion
60 60   include /config/nginx/authelia-server.conf;
61 61  
62 - # Upstream Dashy
63 63   set $upstream_app dashy;
64 64   set $upstream_port 8080;
65 65   set $upstream_proto http;
66 66  
67 - # (facultatif) proxy vers /pico/
74 + # Interface web du Pico W, relayée sous /pico/
68 68   location /pico/ {
69 69   include /config/nginx/authelia-location.conf;
70 70   proxy_pass http://<IP_PICO>/;
... ... @@ -72,7 +72,7 @@
72 72   proxy_set_header Connection close;
73 73   }
74 74  
75 - # API de Dashy (sauvegarde de la config, vérifications d'état)
82 + # API de Dashy (sauvegarde de la config, vérifications d'état...)
76 76   location ^~ /api/ {
77 77   include /config/nginx/authelia-location.conf;
78 78   include /config/nginx/proxy.conf;
... ... @@ -79,90 +79,83 @@
79 79   include /config/nginx/resolver.conf;
80 80   proxy_pass $upstream_proto://$upstream_app:$upstream_port;
81 81   }
89 +
90 + # Proxy CORS des widgets, traité par SWAG et non par Dashy
82 82   location = /cors-proxy {
83 83   include /config/nginx/authelia-location.conf;
84 - # Dashy envoie la cible dans ce header
85 - set $target $http_target_url;
86 - if ($target = "") { return 400; }
93 + set $target $http_target_url;
94 + if ($target = "") { return 400; }
87 87  
88 - add_header Access-Control-Allow-Origin "*" always;
89 - add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
90 - add_header Access-Control-Allow-Headers "*" always;
91 - if ($request_method = OPTIONS) { return 204; }
96 + add_header Access-Control-Allow-Origin "*" always;
97 + add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
98 + add_header Access-Control-Allow-Headers "*" always;
99 + if ($request_method = OPTIONS) { return 204; }
92 92  
93 - # TLS (certs auto-signés possibles sur les cibles LAN, ex: Nextcloud)
94 - proxy_ssl_server_name on;
95 - proxy_ssl_verify off;
101 + proxy_ssl_server_name on;
102 + proxy_ssl_verify off;
96 96  
97 - # Par défaut, on ne fait pas confiance à l'input
98 - set $auth_header "";
99 - set $accept_header "";
100 - set $ocs_header "";
101 - set $host_override "";
104 + set $auth_header "";
105 + set $accept_header "";
106 + set $ocs_header "";
107 + set $host_override "";
102 102  
103 - access_by_lua_block {
104 - local target = ngx.var.target
109 + access_by_lua_block {
110 + local target = ngx.var.target
111 + local host, port = target:match("^https?://([^/:]+):?(%d*)")
112 + if not host then
113 + ngx.status = 400
114 + ngx.header["Content-Type"] = "application/json"
115 + ngx.say('{"error":"Target-URL invalide"}')
116 + return ngx.exit(400)
117 + end
105 105  
106 - local host, port = target:match("^https?://([^/:]+):?(%d*)")
107 - if not host then
108 - ngx.status = 400
109 - ngx.header["Content-Type"] = "application/json"
110 - ngx.say('{"error":"Target-URL invalide"}')
111 - return ngx.exit(400)
112 - end
119 + -- Cibles LAN autorisées (widgets Dashy internes)
120 + local allowed_internal = { ["<IP_SERVEUR>"] = true, ["<IP_ADGUARD>"] = true, ["<IP_PICO>"] = true }
121 + local function is_private_ip(h)
122 + if h == "localhost" then return true end
123 + local a, b = h:match("^(%d+)%.(%d+)%.%d+%.%d+$")
124 + if not a then return false end
125 + a, b = tonumber(a), tonumber(b)
126 + if a == 127 or a == 10 or a == 0 then return true end
127 + if a == 172 and b >= 16 and b <= 31 then return true end
128 + if a == 192 and b == 168 then return true end
129 + if a == 169 and b == 254 then return true end
130 + return false
131 + end
113 113  
114 - -- Cibles LAN explicitement autorisées (widgets Dashy internes)
115 - local allowed_internal = { ["<IP_SERVEUR>"] = true, ["<IP_ADGUARD>"] = true, ["<IP_PICO>"] = true }
116 - local function is_private_ip(h)
117 - if h == "localhost" then return true end
118 - local a, b = h:match("^(%d+)%.(%d+)%.%d+%.%d+$")
119 - if not a then return false end -- pas une IPv4 littérale -> traité comme hôte public
120 - a, b = tonumber(a), tonumber(b)
121 - if a == 127 or a == 10 or a == 0 then return true end
122 - if a == 172 and b >= 16 and b <= 31 then return true end -- 172.16.0.0/12 (docker inclus)
123 - if a == 192 and b == 168 then return true end
124 - if a == 169 and b == 254 then return true end -- link-local / metadata cloud
125 - return false
126 - end
133 + -- Anti-SSRF : LAN, loopback et metadata bloqués sauf liste blanche
134 + if is_private_ip(host) and not allowed_internal[host] then
135 + ngx.status = 403
136 + ngx.header["Content-Type"] = "application/json"
137 + ngx.say('{"error":"Cible interne non autorisée"}')
138 + return ngx.exit(403)
139 + end
127 127  
128 - -- Anti-SSRF : on bloque tout le LAN/loopback/metadata, sauf les cibles explicitement whitelistées
129 - if is_private_ip(host) and not allowed_internal[host] then
130 - ngx.status = 403
131 - ngx.header["Content-Type"] = "application/json"
132 - ngx.say('{"error":"Cible interne non autorisée"}')
133 - return ngx.exit(403)
134 - end
141 + ngx.var.host_override = (port ~= "" and port ~= nil) and (host .. ":" .. port) or host
135 135  
136 - ngx.var.host_override = (port ~= "" and port ~= nil) and (host .. ":" .. port) or host
137 -
138 - -- Parse le JSON envoyé dans customheaders
139 - local cjson = require "cjson.safe"
140 - local hdrs = ngx.req.get_headers()
141 - local ch = hdrs["customheaders"]
142 - if ch then
143 - local obj = cjson.decode(ch)
144 - if obj then
145 - if obj["Authorization"] then ngx.var.auth_header = obj["Authorization"] end
146 - if obj["Accept"] then ngx.var.accept_header = obj["Accept"] end
147 - if obj["OCS-APIREQUEST"] ~= nil then
148 - ngx.var.ocs_header = tostring(obj["OCS-APIREQUEST"])
143 + -- En-têtes envoyés par Dashy en JSON dans CustomHeaders
144 + local cjson = require "cjson.safe"
145 + local ch = ngx.req.get_headers()["customheaders"]
146 + if ch then
147 + local obj = cjson.decode(ch)
148 + if obj then
149 + if obj["Authorization"] then ngx.var.auth_header = obj["Authorization"] end
150 + if obj["Accept"] then ngx.var.accept_header = obj["Accept"] end
151 + if obj["OCS-APIREQUEST"] ~= nil then ngx.var.ocs_header = tostring(obj["OCS-APIREQUEST"]) end
152 + end
149 149   end
150 - end
151 - end
152 - }
154 + }
153 153  
154 - # Injecte les vrais headers vers la cible
155 - proxy_set_header Authorization $auth_header;
156 - proxy_set_header Accept $accept_header;
157 - proxy_set_header OCS-APIREQUEST $ocs_header;
156 + proxy_set_header Authorization $auth_header;
157 + proxy_set_header Accept $accept_header;
158 + proxy_set_header OCS-APIREQUEST $ocs_header;
159 + proxy_set_header Host $host_override;
160 + proxy_set_header X-Forwarded-Proto https;
158 158  
159 - proxy_set_header Host $host_override;
160 - proxy_set_header X-Forwarded-Proto https;
162 + proxy_pass $target;
163 + }
161 161  
162 - proxy_pass $target;
163 -}
164 -
165 - # Application (SPA Vue en history mode)
165 + # Application (SPA Vue en mode history)
166 166   location / {
167 167   include /config/nginx/authelia-location.conf;
168 168   include /config/nginx/proxy.conf;
... ... @@ -169,13 +169,11 @@
169 169   include /config/nginx/resolver.conf;
170 170   proxy_pass $upstream_proto://$upstream_app:$upstream_port;
171 171  
172 - # --- Fallback SPA : si l’upstream renvoie 404 sur /login, /settings, etc.,
173 - # on renvoie l'index de l'app pour laisser Vue router côté client.
172 + # Une 404 de l'upstream sur /login, /settings... renvoie l'index de l'app
174 174   proxy_intercept_errors on;
175 175   error_page 404 = @dashy_fallback;
176 176   }
177 177  
178 - # Cible du fallback : renvoyer l'index de l'app en conservant les en-têtes
179 179   location @dashy_fallback {
180 180   proxy_set_header X-Original-URI $request_uri;
181 181   proxy_pass $upstream_proto://$upstream_app:$upstream_port/;
... ... @@ -183,41 +183,57 @@
183 183  }
184 184  {{/code}}
185 185  
186 -* Authelia protège tout le domaine, y compris les fichiers YAML, qui contiennent les identifiants des widgets.
187 -* ##/cors-proxy## remplace le proxy CORS de Dashy pour les widgets en ##useProxy: true##. Il ne relaie que les en-têtes ##Authorization##, ##Accept## et ##OCS-APIREQUEST##, et refuse les IP privées absentes de ##allowed_internal## : tout nouveau widget qui vise une IP du LAN doit y être ajouté.
188 -* ##/pico/## relaie l'interface web du Pico W.
189 -* ##@dashy_fallback## renvoie l'application sur les routes internes de Vue (##/login##...), que le serveur de Dashy renvoie en 404.
184 +Fonctionnement du proxy CORS :
190 190  
191 -=== Authelia ===
186 +* Un widget en ##useProxy: true## n'appelle pas l'API cible depuis le navigateur : il envoie la requête à ##/cors-proxy## sur le domaine de Dashy, avec l'adresse cible dans l'en-tête ##Target-URL## et ses propres en-têtes (authentification comprise) en JSON dans ##CustomHeaders##.
187 +* Derrière SWAG, c'est ce bloc ##location = /cors-proxy## qui répond, pas le proxy intégré de Dashy. Il ne transmet que ##Authorization##, ##Accept## et ##OCS-APIREQUEST## : un widget qui a besoin d'un autre en-tête doit être ajouté dans le bloc Lua.
188 +* Les adresses IP privées sont refusées (403), sauf celles de ##allowed_internal##. Tout nouveau widget qui vise une IP du LAN doit y être ajouté, puis SWAG rechargé. Un nom de domaine public passe sans liste blanche.
189 +* Les appels à ##/cors-proxy## partent de la page Dashy, sur le même domaine : le navigateur y joint le cookie de session Authelia, et ##/cors-proxy## n'est pas utilisable sans connexion.
190 +* Les requêtes ##OPTIONS## (pré-vol CORS) reçoivent leur 204 avant le contrôle Authelia, car les ##if## de nginx sont évalués avant ##auth_request##.
191 +* En accès direct sur le port ##4000##, c'est le proxy intégré de Dashy qui répond, sans liste blanche ni Authelia.
192 +* Nécessite le module Lua de nginx, présent dans l'image SWAG.
192 192  
193 -Règle dans la section ##access_control## de la configuration d'Authelia :
194 +=== Accès ===
194 194  
196 +L'accès se fait en deux étapes :
197 +
198 +1. **Authelia**, devant tout le domaine : sans session Authelia, SWAG redirige vers le portail Authelia. Cela couvre l'interface, ##conf.yml## et les pages secondaires, l'API de Dashy, ##/cors-proxy## et ##/pico/##.
199 +1. **Dashy**, une fois Authelia passé : son écran de connexion donne accès à l'éditeur de configuration au compte administrateur défini dans ##appConfig.auth.users## (##disableConfigurationForNonAdmin: true##). Voir [[Authentification>>||anchor="HAuthentification"]].
200 +
201 +Règle dans la configuration d'[[Authelia>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Authelia.WebHome]], section ##access_control## :
202 +
195 195  {{code language="yaml"}}
196 196  access_control:
197 197   default_policy: deny
198 198   rules:
199 - - domain: "dash.<DOMAINE>"
207 + - domain: "<DOMAINE_DASHY>"
200 200   policy: one_factor
201 201  {{/code}}
202 202  
203 -Aucune règle ##bypass## pour ce domaine ne doit la précéder : Authelia applique la première règle qui correspond.
211 +* La règle ne sert que si SWAG interroge Authelia : sans les ##include## d'Authelia dans le fichier SWAG, elle n'est jamais évaluée, quelle que soit sa ##policy##. Il faut les deux.
212 +* Une règle ##bypass## pour ce domaine, placée avant, annulerait la protection : Authelia applique la première règle qui correspond.
213 +* ##enableGuestAccess: true## ne concerne que les personnes déjà passées par Authelia : elles voient les pages sans se connecter à Dashy, mais ne peuvent pas modifier la configuration.
214 +* Le fichier Authelia ouvre aussi le CORS de ##/.well-known/openid-configuration## et ##/jwks.json## au domaine de Dashy, en prévision d'une connexion OIDC. Celle-ci n'est pas activée dans ##conf.yml##.
204 204  
205 -== Fichiers de configuration ==
216 +{{warning}}C'est Authelia qui protège les fichiers, pas Dashy : l'authentification de Dashy se fait dans le navigateur et masque seulement l'éditeur. ##conf.yml## et les pages secondaires contiennent les identifiants des widgets, les clés d'API, les adresses internes et le hash du mot de passe administrateur. Toute voie d'accès qui contourne Authelia (port ##4000##, autre proxy, règle ##bypass##) les rend lisibles. Vérifier après chaque modification de SWAG ou d'Authelia, en navigation privée : ##/conf.yml## doit renvoyer vers le portail Authelia.{{/warning}}
206 206  
218 +== Configuration ==
219 +
207 207  Dans ##<APPDATA>/Dashy/user-data## :
208 208  
209 -|=Page Dashy|=Fichier|=Sous-page|=Widgets avec identifiants
210 -|Accueil|##conf.yml##|[[Accueil>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.Accueil.WebHome]]|##adguard-stats##, ##adguard-filter-status##, ##adguard-top-domains## (compte AdGuard Home) ; ##nextcloud-system## (mot de passe d'application Nextcloud)
211 -|bookmarks|##bookmark.yml##|[[Bookmarks>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.Bookmarks.WebHome]]|—
212 -|cpu|##cpu.yml##|[[CPU>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.CPU.WebHome]]|—
213 -|news|##news.yml##|[[News>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.News.WebHome]]|##flight-data## (clé RapidAPI) ; ##news-headlines## (clé Currents API)
222 +|=Élément|=Rôle
223 +|##conf.yml##|Page d'accueil et configuration commune : ##appConfig##, ##pageInfo##, ##pages##, sections
224 +|##bookmark.yml##|Page secondaire //bookmarks// : favoris web
225 +|##cpu.yml##|Page secondaire //cpu// : supervision Glances
226 +|##news.yml##|Page secondaire //news// : capteurs, flux, actualités, crypto
227 +|##item-icons/##|Icônes des services et image de fond, stockées en local
228 +|##conf.yml.bak##, ##bookmark.yml.bak##|Copies manuelles, non utilisées par Dashy
229 +|##config-backups/##|Dossier de sauvegardes de Dashy (vide actuellement)
214 214  
215 -##item-icons/## contient les icônes locales et l'image de fond.
231 +=== Fonctionnement multi-pages ===
216 216  
217 -=== Multi-pages ===
233 +La page d'accueil est toujours ##conf.yml##. Les autres pages sont déclarées dans sa section ##pages:## : chaque entrée donne le nom affiché dans la barre de navigation et le chemin du fichier, relatif à ##user-data## (une URL distante est aussi acceptée).
218 218  
219 -La page d'accueil est ##conf.yml##. Elle déclare les pages secondaires dans ##pages:## : nom affiché dans la barre de navigation et chemin relatif à ##user-data##.
220 -
221 221  {{code language="yaml"}}
222 222  pages:
223 223   - name: bookmarks
... ... @@ -228,23 +228,153 @@
228 228   path: news.yml
229 229  {{/code}}
230 230  
231 -Chaque page secondaire ne contient que ##pageInfo##, ##sections## et la même liste ##pages:##, pour garder la navigation. ##appConfig## n'est lu que dans ##conf.yml## et s'applique à toutes les pages.
245 +* Une page secondaire ne contient que ##pageInfo## et ##sections## : ##appConfig## (thème, authentification, recherche...) est lu uniquement dans ##conf.yml## et s'applique à toutes les pages.
246 +* Chaque page secondaire répète la même liste ##pages:##, pour garder la navigation identique d'une page à l'autre. Aucune ne déclare de fichier supplémentaire.
247 +* Dans ##pageInfo.navLinks## des pages secondaires, un lien ##Home## vers ##/## ramène à l'accueil.
248 +* Ajouter une page : créer le fichier dans ##user-data##, l'ajouter dans ##pages:## de ##conf.yml## et des autres pages, puis recharger.
232 232  
233 -Fichiers présents mais non référencés : ##cpu2.yml## (ancienne copie de ##cpu.yml## à l'indentation cassée), ##conf.yml.bak## et ##bookmark.yml.bak##.
250 +=== Récapitulatif des pages ===
234 234  
235 -=== Éléments communs ===
252 +|=Page Dashy|=Fichier|=Sous-page XWiki|=Widgets utilisant des identifiants
253 +|Accueil|##conf.yml##|[[Accueil>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.Accueil.WebHome]]|##adguard-stats##, ##adguard-filter-status##, ##adguard-top-domains## (compte AdGuard Home), ##nextcloud-system## (mot de passe d'application Nextcloud). Les widgets ##image## dépendent du jeton GitHub de github-readme-stats, stocké côté serveur.
254 +|bookmarks|##bookmark.yml##|[[Bookmarks>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.Bookmarks.WebHome]]|Aucun widget
255 +|cpu|##cpu.yml##|[[CPU>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.CPU.WebHome]]|Aucun (Glances sans authentification)
256 +|news|##news.yml##|[[News>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.News.WebHome]]|##flight-data## (clé RapidAPI), ##news-headlines## (clé Currents API)
236 236  
237 -##appConfig## est dans ##conf.yml##, dont le fichier complet est sur la sous-page [[Accueil>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.Accueil.WebHome]].
258 +=== Fichiers non référencés ===
238 238  
239 -* Thème ##dracula##, image de fond servie depuis ##item-icons/##.
240 -* ##statusCheck: false## au niveau global : la vérification d'état est activée item par item.
241 -* ##faviconApi: allesedv## pour les items en ##icon: favicon##. ##enableFontAwesome: true## pour les icônes ##fas##, ##fab## et ##fad##. Les icônes ##hl-...## viennent du jeu homelab intégré.
242 -* ##widgetsAlwaysUseProxy: false## : chaque widget choisit avec ##useProxy## s'il passe par ##/cors-proxy##. Le réglage se met sur le widget : au niveau de la section, il est ignoré.
243 -* Authentification : un compte ##admin## défini par son nom et le hash SHA-256 de son mot de passe (##echo -n '<MOT_DE_PASSE>' ~| sha256sum##). ##enableGuestAccess: true## : une fois passé Authelia, on voit les pages sans se connecter à Dashy. ##disableConfigurationForNonAdmin: true## : seul l'admin modifie la configuration.
260 +* ##cpu2.yml## : ancienne copie de ##cpu.yml## avec une indentation à un espace qui casse la structure YAML. Déclarée nulle part, mais servie comme les autres fichiers. À supprimer.
261 +* ##conf.yml.bak## et ##bookmark.yml.bak## : copies de sauvegarde, non déclarées.
244 244  
245 -Vérifier la syntaxe après une modification à la main :
263 +=== Structure d'un fichier ===
246 246  
265 +Un fichier de page contient trois blocs de premier niveau, plus ##appConfig## et ##pages## pour ##conf.yml## :
266 +
267 +{{code language="yaml"}}
268 +pageInfo:
269 + title: <TITRE>
270 + description: <SOUS_TITRE>
271 + navLinks:
272 + - title: <LIBELLE>
273 + target: newtab
274 + path: https://<SITE_PERSO>
275 +appConfig:
276 + # conf.yml uniquement, voir ci-dessous
277 +pages:
278 + # conf.yml, recopié dans chaque page secondaire
279 +sections:
280 + - name: <NOM_SECTION>
281 + icon: <ICONE>
282 + displayData:
283 + sortBy: default # default | alphabetical | random
284 + rows: 1
285 + cols: 1
286 + collapsed: false
287 + hideForGuests: false
288 + items:
289 + - title: <NOM_SERVICE>
290 + description: <DESCRIPTION>
291 + icon: <ICONE>
292 + url: https://<DOMAINE_SERVICE>/
293 + target: newtab
294 + statusCheck: true
295 + statusCheckAllowInsecure: true
296 + id: <ID>
297 + widgets:
298 + - type: <TYPE_WIDGET>
299 + useProxy: true
300 + options:
301 + <OPTION>: <VALEUR>
302 + id: <ID>
303 +{{/code}}
304 +
305 +* ##id## : identifiant unique généré par l'interface. Facultatif en écriture manuelle : Dashy en génère un s'il manque.
306 +* ##icon## : formes utilisées dans cette configuration :
307 +** nom de fichier présent dans ##item-icons/## (##portainer.svg##) ;
308 +** ##hl-<nom>## : jeu d'icônes homelab intégré (##hl-jellyfin##) ;
309 +** classes Font Awesome (##fas fa-terminal##, ##fab fa-github##, ##fad fa-clock##), actif via ##enableFontAwesome: true## ;
310 +** ##favicon## : favicon du site récupéré par le service défini dans ##faviconApi## ;
311 +** emoji (##🛡️##) ou URL complète d'une image.
312 +* ##useProxy## se place sur le widget, pas sur la section : placé au niveau de la section, il est ignoré.
313 +
314 +=== appConfig et thème ===
315 +
316 +{{code language="yaml"}}
317 +appConfig:
318 + theme: dracula
319 + layout: auto
320 + iconSize: small
321 + backgroundImg: /item-icons/<IMAGE_FOND>
322 + startingView: default
323 + defaultOpeningMethod: newtab
324 + statusCheck: false
325 + statusCheckInterval: 0
326 + faviconApi: allesedv
327 + enableFontAwesome: true
328 + enableMaterialDesignIcons: false
329 + widgetsAlwaysUseProxy: false
330 + webSearch:
331 + searchEngine: duckduckgo
332 + openingMethod: newtab
333 + searchBangs: {}
334 + disableWebSearch: false
335 + auth:
336 + enableGuestAccess: true
337 + users:
338 + - user: <UTILISATEUR_DASHY>
339 + hash: <HASH_SHA256_DU_MOT_DE_PASSE>
340 + type: admin
341 + enableHeaderAuth: false
342 + headerAuth:
343 + userHeader: REMOTE_USER
344 + proxyWhitelist: []
345 + enableKeycloak: false
346 + hideComponents:
347 + hideHeading: false
348 + hideNav: false
349 + hideSearch: false
350 + hideSettings: false
351 + hideFooter: false
352 + showSplashScreen: false
353 + preventWriteToDisk: false
354 + preventLocalSave: false
355 + disableConfiguration: false
356 + disableConfigurationForNonAdmin: true
357 + allowConfigEdit: true
358 + enableServiceWorker: false
359 + disableContextMenu: false
360 + disableUpdateChecks: false
361 + disableSmartSort: false
362 + enableErrorReporting: false
363 +{{/code}}
364 +
365 +|=Clé|=Effet
366 +|##theme: dracula##|Thème sombre intégré, appliqué à toutes les pages
367 +|##backgroundImg##|Image de fond, servie depuis ##item-icons/##
368 +|##statusCheck: false##|Pas de vérification d'état globale : elle est activée item par item (##statusCheck: true##)
369 +|##faviconApi: allesedv##|Les icônes ##favicon## sont récupérées via le service externe allesedv
370 +|##widgetsAlwaysUseProxy: false##|Chaque widget choisit avec ##useProxy## s'il passe par ##/cors-proxy##
371 +|##webSearch##|Recherche web DuckDuckGo depuis la barre de recherche, en nouvel onglet
372 +|##preventWriteToDisk: false##|L'interface peut écrire dans les fichiers YAML du volume
373 +|##preventLocalSave: false##|Les modifications peuvent aussi être gardées dans le stockage local du navigateur
374 +|##disableConfigurationForNonAdmin: true##|Seul un compte ##type: admin## voit l'éditeur de configuration
375 +
376 +=== Authentification ===
377 +
378 +* Un seul compte, administrateur, défini par son nom et le hash SHA-256 de son mot de passe. Générer le hash :
379 +
247 247  {{code language="bash"}}
381 +echo -n '<MOT_DE_PASSE_DASHY>' | sha256sum
382 +{{/code}}
383 +
384 +* ##enableGuestAccess: true## : les visiteurs authentifiés par Authelia mais pas par Dashy voient toutes les pages. ##hideForGuests: true## sur une section la masque aux invités, sans la retirer des fichiers servis.
385 +* ##enableHeaderAuth## (utilisateur transmis par le reverse proxy dans ##REMOTE_USER##) et ##enableKeycloak## sont désactivés.
386 +
387 +=== Vérifier la syntaxe ===
388 +
389 +Après une modification à la main :
390 +
391 +{{code language="bash"}}
248 248  docker exec dashy yarn validate-config
249 249  {{/code}}
250 250  
... ... @@ -272,11 +272,11 @@
272 272  Principales adresses :
273 273  
274 274  |=Carte|=Adresse
275 -|Statistiques du profil|##/api?username=<UTILISATEUR>##
276 -|Langages les plus utilisés|##/api/top-langs?username=<UTILISATEUR>##
277 -|Dépôt épinglé|##/api/pin?username=<UTILISATEUR>&repo=<DEPOT>##
419 +|Statistiques du profil|##/api?username=<UTILISATEUR_GITHUB>##
420 +|Langages les plus utilisés|##/api/top-langs?username=<UTILISATEUR_GITHUB>##
421 +|Dépôt épinglé|##/api/pin?username=<UTILISATEUR_GITHUB>&repo=<DEPOT>##
278 278  
279 -Dans Dashy, les cartes s'affichent avec un widget de type ##image## qui pointe vers l'instance locale.
423 +Dans Dashy, les cartes s'affichent avec un widget de type ##image## qui pointe vers l'instance locale (voir la sous-page [[Accueil>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.Accueil.WebHome]]).
280 280  
281 281  === Image locale ===
282 282  
... ... @@ -291,34 +291,49 @@
291 291  
292 292  == Maintenance ==
293 293  
294 -=== Après une migration ou une réinstallation ===
438 +=== Après une migration ou une réinstallation d'un service ===
295 295  
296 -|=Service|=À refaire
297 -|Nextcloud|Créer un nouveau mot de passe d'application et le reporter dans ##nextcloud-system## : les mots de passe d'application ne suivent pas une migration.
298 -|AdGuard Home|Reporter le compte de l'interface web dans les trois widgets ##adguard-*##. Si l'IP change : ##hostname## et ##allowed_internal## de SWAG.
299 -|Pico W|Si l'IP change : ##news.yml## (item et script), ##location /pico/## et ##allowed_internal## de SWAG.
300 -|SWAG, Authelia|Remettre ##30-dashy.subdomain.conf## complet et la règle ##one_factor##, puis vérifier en navigation privée que ##/conf.yml## redirige vers Authelia.
301 -|Dashy|Restaurer ##user-data##.
440 +|=Service migré ou réinstallé|=À refaire dans Dashy
441 +|Nextcloud|Créer un nouveau mot de passe d'application et le reporter dans ##nextcloud-system## : les mots de passe d'application ne suivent pas une migration (sauvegarde restaurée, passage à AIO, nouvelle instance). Vérifier que le compte est toujours administrateur et que le nom d'utilisateur a gardé la même casse.
442 +|AdGuard Home|Reporter le nom d'utilisateur et le mot de passe de l'interface web dans les trois widgets ##adguard-*##. Si l'adresse IP change : mettre à jour ##hostname## et la liste ##allowed_internal## de SWAG.
443 +|Glances|Mettre à jour ##hostname## dans tous les widgets ##gl-*## de ##cpu.yml## si le domaine change.
444 +|github-readme-stats|Reconstruire l'image locale et recréer le jeton ##PAT_1## s'il a expiré.
445 +|Pico W|Mettre à jour son IP dans ##news.yml## (item et script du widget ##embed##), dans ##location /pico/## et dans ##allowed_internal## de SWAG.
446 +|SWAG|Recopier ##30-dashy.subdomain.conf## complet. Sans les ##include## d'Authelia, Dashy et ses fichiers de configuration redeviennent publics. Sans le bloc ##/cors-proxy##, les widgets en ##useProxy: true## passent par le proxy de Dashy, sans liste blanche.
447 +|Authelia|Vérifier que la règle ##one_factor## du domaine de Dashy est toujours présente et qu'aucune règle ##bypass## ne la précède.
448 +|Dashy|Restaurer ##user-data## (fichiers YAML et ##item-icons/##). Aucun identifiant n'est stocké ailleurs.
449 +|API externes (RapidAPI, Currents)|Si la clé est régénérée ou le compte recréé, reporter la nouvelle clé dans ##news.yml##.
302 302  
303 303  === Recharger la configuration ===
304 304  
305 -* Dashy relit les fichiers YAML à chaque chargement : après une modification à la main, rafraîchir la page. Si l'ancienne version reste affichée, elle vient du stockage local du navigateur : réinitialiser les paramètres locaux depuis le menu de configuration.
306 -* SWAG : ##docker exec swag nginx -t## puis ##docker exec swag nginx -s reload##. Authelia : ##docker restart authelia##.
453 +* Modification depuis l'interface : l'enregistrement sur le disque, depuis l'éditeur de configuration, écrit le fichier YAML de la page affichée.
454 +* Modification à la main : Dashy relit les fichiers YAML à chaque chargement, un rafraîchissement du navigateur suffit. Si l'ancienne configuration reste affichée, elle vient du stockage local du navigateur : réinitialiser les paramètres locaux depuis le menu de configuration, ou vider les données du site.
455 +* Changement dans la stack (variables d'environnement, image) : redéployer la stack dans Portainer.
456 +* Changement dans le fichier SWAG : ##docker exec swag nginx -t## puis ##docker exec swag nginx -s reload##.
457 +* Changement dans la configuration d'Authelia : ##docker restart authelia##.
307 307  
308 308  === Un widget reste vide ===
309 309  
310 -Dans les outils de développement du navigateur (F12, onglet Réseau), regarder la requête du widget :
461 +Dans l'ordre :
311 311  
312 -|=Symptôme|=Cause
313 -|Erreur CORS sur un appel direct|Passer le widget en ##useProxy: true##
314 -|##/cors-proxy## : 302 ou 401|Session Authelia expirée : recharger la page
315 -|##/cors-proxy## : 403 ##Cible interne non autorisée##|IP absente de ##allowed_internal##
316 -|##/cors-proxy## : 401 ou 403 de la cible|Identifiants faux ou révoqués, compte sans droits, ou en-tête non relayé par le bloc Lua
317 -|##/cors-proxy## : 502 ou 504|Service cible arrêté ou injoignable depuis SWAG
463 +1. Outils de développement du navigateur (F12), onglets Console et Réseau : repérer la requête du widget et son code de retour.
464 +1. Requête vers ##/cors-proxy## :
465 +1*. 302 vers le portail Authelia, ou 401 : session Authelia expirée ; recharger la page et se reconnecter ;
466 +1*. 400 ##Target-URL invalide## : ##hostname## mal formé (schéma ##http:~/~/## ou ##https:~/~/## obligatoire) ;
467 +1*. 403 ##Cible interne non autorisée## : IP du LAN absente de ##allowed_internal## dans SWAG ;
468 +1*. 401 ou 403 renvoyé par la cible : identifiants faux, mot de passe d'application révoqué, compte sans droits suffisants, ou en-tête d'authentification non transmis par le bloc Lua ;
469 +1*. 502 ou 504 : service cible arrêté ou injoignable depuis le conteneur SWAG.
470 +1. Requête directe (sans ##/cors-proxy##) bloquée avec une erreur CORS dans la console : passer le widget en ##useProxy: true##.
471 +1. Journaux : ##docker logs swag## et ##<APPDATA>/swag/config/log/nginx/error.log## pour le proxy, ##docker logs dashy## pour le serveur de Dashy.
472 +1. Vérifier la syntaxe du fichier avec ##yarn validate-config## : une erreur d'indentation peut vider toute une section.
473 +1. Tester l'API cible à la main depuis l'hôte, avec les mêmes identifiants. Exemple pour Nextcloud :
318 318  
319 -Journaux : ##docker logs dashy## et ##<APPDATA>/swag/config/log/nginx/error.log##.
475 +{{code language="bash"}}
476 +curl -u '<UTILISATEUR_NEXTCLOUD>:<MOT_DE_PASSE_APPLICATION>' -H 'OCS-APIREQUEST: true' \
477 + 'https://<DOMAINE_NEXTCLOUD>/ocs/v2.php/apps/serverinfo/api/v1/info?format=json'
478 +{{/code}}
320 320  
321 321  == Sauvegarde ==
322 322  
323 -##<APPDATA>/Dashy/user-data## est sauvegardé par le script de [[Sauvegarde>>Wiki Batcave John.Infrastructure.Sauvegarde.WebHome]], identifiants des widgets compris. Le fichier SWAG est aussi dans ##<APPDATA>##.
482 +##<APPDATA>/Dashy/user-data## est sauvegardé par le script de [[Sauvegarde>>Wiki Batcave John.Infrastructure.Sauvegarde.WebHome]]. Il contient toute la configuration, identifiants des widgets compris : la sauvegarde doit être protégée en conséquence. Le fichier SWAG ##30-dashy.subdomain.conf## est aussi dans ##<APPDATA>##, couvert par la même sauvegarde.
324 324