| ... |
... |
@@ -1,11 +1,15 @@ |
| 1 |
|
-Tableau de bord d'accueil : liens vers tous les services auto-hébergés, avec leur état en direct et des widgets. |
|
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 |
3 |
== Fonctionnalités utilisées == |
| 4 |
4 |
|
| 5 |
|
-* Sections et liens vers les services, avec icônes stockées en local. |
|
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 |
6 |
* Indicateur d'état de chaque service (vérification de disponibilité). |
| 7 |
|
-* Widgets de contenu dynamique, dont les cartes de statistiques GitHub fournies par une instance locale de github-readme-stats. |
| 8 |
|
-* Configuration modifiable depuis l'interface, enregistrée dans le fichier YAML. |
|
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. |
| 9 |
9 |
|
| 10 |
10 |
== Stack == |
| 11 |
11 |
|
| ... |
... |
@@ -19,7 +19,6 @@ |
| 19 |
19 |
- UID=1000 |
| 20 |
20 |
- GID=1000 |
| 21 |
21 |
- NODE_TLS_REJECT_UNAUTHORIZED=0 |
| 22 |
|
- - VUE_APP_BOB=<MOT_DE_PASSE> |
| 23 |
23 |
ports: |
| 24 |
24 |
- "4000:8080" |
| 25 |
25 |
volumes: |
| ... |
... |
@@ -39,33 +39,442 @@ |
| 39 |
39 |
external: true |
| 40 |
40 |
{{/code}} |
| 41 |
41 |
|
| 42 |
|
-* ##NODE_TLS_REJECT_UNAUTHORIZED=0## : le serveur de Dashy accepte les certificats non reconnus lors des vérifications d'état, nécessaire pour les services locaux en https avec un certificat auto-signé. |
| 43 |
|
-* ##VUE_APP_BOB## : mot de passe d'un utilisateur de l'authentification intégrée, référencé par son nom dans ##conf.yml## (##password: VUE_APP_BOB##). Il est lu à la construction de l'application : après modification, reconstruire (Config > Rebuild). |
|
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]]. |
| 44 |
44 |
|
|
52 |
+=== Exposition derrière SWAG === |
|
53 |
+ |
|
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]]. |
|
55 |
+ |
|
56 |
+{{code language="nginx"}} |
|
57 |
+server { |
|
58 |
+ listen 443 ssl; |
|
59 |
+ listen [::]:443 ssl; |
|
60 |
+ server_name <DOMAINE_DASHY>; |
|
61 |
+ |
|
62 |
+ include /config/nginx/ssl.conf; |
|
63 |
+ include /config/nginx/snippets/robots-default.conf; |
|
64 |
+ |
|
65 |
+ client_max_body_size 25m; |
|
66 |
+ |
|
67 |
+ # Authelia : tout le domaine exige une connexion |
|
68 |
+ include /config/nginx/authelia-server.conf; |
|
69 |
+ |
|
70 |
+ set $upstream_app dashy; |
|
71 |
+ set $upstream_port 8080; |
|
72 |
+ set $upstream_proto http; |
|
73 |
+ |
|
74 |
+ # Interface web du Pico W, relayée sous /pico/ |
|
75 |
+ location /pico/ { |
|
76 |
+ include /config/nginx/authelia-location.conf; |
|
77 |
+ proxy_pass http://<IP_PICO>/; |
|
78 |
+ include /config/nginx/proxy.conf; |
|
79 |
+ proxy_set_header Connection close; |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ # API de Dashy (sauvegarde de la config, vérifications d'état...) |
|
83 |
+ location ^~ /api/ { |
|
84 |
+ include /config/nginx/authelia-location.conf; |
|
85 |
+ include /config/nginx/proxy.conf; |
|
86 |
+ include /config/nginx/resolver.conf; |
|
87 |
+ proxy_pass $upstream_proto://$upstream_app:$upstream_port; |
|
88 |
+ } |
|
89 |
+ |
|
90 |
+ # Proxy CORS des widgets, traité par SWAG et non par Dashy |
|
91 |
+ location = /cors-proxy { |
|
92 |
+ include /config/nginx/authelia-location.conf; |
|
93 |
+ set $target $http_target_url; |
|
94 |
+ if ($target = "") { return 400; } |
|
95 |
+ |
|
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; } |
|
100 |
+ |
|
101 |
+ proxy_ssl_server_name on; |
|
102 |
+ proxy_ssl_verify off; |
|
103 |
+ |
|
104 |
+ set $auth_header ""; |
|
105 |
+ set $accept_header ""; |
|
106 |
+ set $ocs_header ""; |
|
107 |
+ set $host_override ""; |
|
108 |
+ |
|
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 |
|
118 |
+ |
|
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 |
|
132 |
+ |
|
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 |
|
140 |
+ |
|
141 |
+ ngx.var.host_override = (port ~= "" and port ~= nil) and (host .. ":" .. port) or host |
|
142 |
+ |
|
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 |
|
153 |
+ end |
|
154 |
+ } |
|
155 |
+ |
|
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; |
|
161 |
+ |
|
162 |
+ proxy_pass $target; |
|
163 |
+ } |
|
164 |
+ |
|
165 |
+ # Application (SPA Vue en mode history) |
|
166 |
+ location / { |
|
167 |
+ include /config/nginx/authelia-location.conf; |
|
168 |
+ include /config/nginx/proxy.conf; |
|
169 |
+ include /config/nginx/resolver.conf; |
|
170 |
+ proxy_pass $upstream_proto://$upstream_app:$upstream_port; |
|
171 |
+ |
|
172 |
+ # Une 404 de l'upstream sur /login, /settings... renvoie l'index de l'app |
|
173 |
+ proxy_intercept_errors on; |
|
174 |
+ error_page 404 = @dashy_fallback; |
|
175 |
+ } |
|
176 |
+ |
|
177 |
+ location @dashy_fallback { |
|
178 |
+ proxy_set_header X-Original-URI $request_uri; |
|
179 |
+ proxy_pass $upstream_proto://$upstream_app:$upstream_port/; |
|
180 |
+ } |
|
181 |
+} |
|
182 |
+{{/code}} |
|
183 |
+ |
|
184 |
+Fonctionnement du proxy CORS : |
|
185 |
+ |
|
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. |
|
193 |
+ |
| 45 |
45 |
=== Accès === |
| 46 |
46 |
|
| 47 |
|
-* Consultation : publique, sans authentification. |
| 48 |
|
-* Modification de la configuration : connexion en deux temps, d'abord Authelia, qui redirige ensuite vers la connexion de Dashy (utilisateur défini dans ##conf.yml##, mot de passe dans ##VUE_APP_BOB##). |
| 49 |
|
-{{info}} |
| 50 |
|
-En consultation publique, le contenu de ##conf.yml## reste lisible par tous : n'y mettre ni mot de passe, ni jeton, ni adresse de service qu'on ne veut pas rendre visible. |
| 51 |
|
-{{/info}} |
|
196 |
+L'accès se fait en deux étapes : |
| 52 |
52 |
|
|
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 |
+ |
|
203 |
+{{code language="yaml"}} |
|
204 |
+access_control: |
|
205 |
+ default_policy: deny |
|
206 |
+ rules: |
|
207 |
+ - domain: "<DOMAINE_DASHY>" |
|
208 |
+ policy: one_factor |
|
209 |
+{{/code}} |
|
210 |
+ |
|
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##. |
|
215 |
+ |
|
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}} |
|
217 |
+ |
| 53 |
53 |
== Configuration == |
| 54 |
54 |
|
| 55 |
55 |
Dans ##<APPDATA>/Dashy/user-data## : |
| 56 |
56 |
|
| 57 |
57 |
|=Élément|=Rôle |
| 58 |
|
-|##conf.yml##|Configuration principale : pages, sections, liens, widgets, thème |
| 59 |
|
-|##bookmark.yml##|Page secondaire de favoris |
| 60 |
|
-|##item-icons/##|Icônes des services, stockées en local |
|
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) |
| 61 |
61 |
|
| 62 |
|
-* ##appConfig.faviconApi: local## : aucune icône n'est récupérée sur un service externe. |
| 63 |
|
-* Vérifier la syntaxe après une modification à la main : |
|
231 |
+=== Fonctionnement multi-pages === |
| 64 |
64 |
|
|
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). |
|
234 |
+ |
|
235 |
+{{code language="yaml"}} |
|
236 |
+pages: |
|
237 |
+ - name: bookmarks |
|
238 |
+ path: bookmark.yml |
|
239 |
+ - name: cpu |
|
240 |
+ path: cpu.yml |
|
241 |
+ - name: news |
|
242 |
+ path: news.yml |
|
243 |
+{{/code}} |
|
244 |
+ |
|
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. |
|
249 |
+ |
|
250 |
+=== Récapitulatif des pages === |
|
251 |
+ |
|
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) |
|
257 |
+ |
|
258 |
+=== Fichiers non référencés === |
|
259 |
+ |
|
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. |
|
262 |
+ |
|
263 |
+=== Structure d'un fichier === |
|
264 |
+ |
|
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 |
+ |
| 65 |
65 |
{{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"}} |
| 66 |
66 |
docker exec dashy yarn validate-config |
| 67 |
67 |
{{/code}} |
| 68 |
68 |
|
|
395 |
+== Statistiques GitHub : github-readme-stats == |
|
396 |
+ |
|
397 |
+Instance locale de github-readme-stats, qui génère les cartes SVG de statistiques GitHub affichées par un widget de Dashy : statistiques du profil, langages les plus utilisés, dépôts épinglés. |
|
398 |
+ |
|
399 |
+{{code language="yaml"}} |
|
400 |
+services: |
|
401 |
+ github-readme-stats: |
|
402 |
+ image: github-readme-stats:local |
|
403 |
+ container_name: github-readme-stats |
|
404 |
+ restart: unless-stopped |
|
405 |
+ ports: |
|
406 |
+ - "9000:9000" |
|
407 |
+ environment: |
|
408 |
+ - PAT_1=${PAT_1} |
|
409 |
+ - CACHE_SECONDS=14400 |
|
410 |
+{{/code}} |
|
411 |
+ |
|
412 |
+* ##PAT_1## : jeton d'accès personnel GitHub, défini dans les variables de la stack Portainer. Jeton classique avec les permissions ##repo## pour compter les dépôts privés. |
|
413 |
+* ##CACHE_SECONDS## : durée de mise en cache des cartes (4 heures), pour limiter les appels à l'API GitHub. |
|
414 |
+* Accessible uniquement en local, sur le port 9000. |
|
415 |
+ |
|
416 |
+Principales adresses : |
|
417 |
+ |
|
418 |
+|=Carte|=Adresse |
|
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>## |
|
422 |
+ |
|
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]]). |
|
424 |
+ |
|
425 |
+=== Image locale === |
|
426 |
+ |
|
427 |
+Portainer ne sait pas construire une image dans une stack : l'image ##github-readme-stats:local## est construite sur l'hôte, depuis le dépôt cloné. Elle n'est donc pas mise à jour par Watchtower. Pour la mettre à jour, depuis le dossier du dépôt : |
|
428 |
+ |
|
429 |
+{{code language="bash"}} |
|
430 |
+git pull |
|
431 |
+docker build -t github-readme-stats:local . |
|
432 |
+{{/code}} |
|
433 |
+ |
|
434 |
+Puis redéployer la stack dans Portainer. |
|
435 |
+ |
|
436 |
+== Maintenance == |
|
437 |
+ |
|
438 |
+=== Après une migration ou une réinstallation d'un service === |
|
439 |
+ |
|
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##. |
|
450 |
+ |
|
451 |
+=== Recharger la configuration === |
|
452 |
+ |
|
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##. |
|
458 |
+ |
|
459 |
+=== Un widget reste vide === |
|
460 |
+ |
|
461 |
+Dans l'ordre : |
|
462 |
+ |
|
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 : |
|
474 |
+ |
|
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}} |
|
479 |
+ |
| 69 |
69 |
== Sauvegarde == |
| 70 |
70 |
|
| 71 |
|
-##<APPDATA>/Dashy/user-data## est sauvegardé par le script de [[Sauvegarde>>Wiki Batcave John.Infrastructure.Sauvegarde.WebHome]]. Dashy enregistre aussi une copie de la configuration précédente dans ce dossier à chaque sauvegarde depuis l'interface. |
|
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. |
|
483 |
+ |