mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-23 06:58:14 +02:00
Accès refusé : l'interface dit pourquoi et comment le lever
Depuis la protection anti-site-tiers (0.14.0), un accès par nom de domaine derrière un reverse proxy, sans clé de pilotage, reçoit un 403 sur toute l'API. L'interface restait vide : chaque appel échouait en silence dans la console. - Au premier 403, un bandeau fixe affiche la raison donnée par le serveur et le correctif : LOKI_TRUSTED_HOSTS=<le nom affiché> dans les variables du conteneur, ou une clé de pilotage. - docker-compose.yml et docker-compose.unraid.yml exposent la variable LOKI_TRUSTED_HOSTS, commentée. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cf8d1cfaaa
commit
aebecd75bd
4 files changed
+54
No files matched your search
@@ -34,6 +34,11 @@ services:
|
|||||||
# lancement). Ex. LOKI_MODEL=Qwen3-14B-Q4_K_M.gguf après dépôt du
|
# lancement). Ex. LOKI_MODEL=Qwen3-14B-Q4_K_M.gguf après dépôt du
|
||||||
# fichier dans /mnt/user/appdata/loki/models.
|
# fichier dans /mnt/user/appdata/loki/models.
|
||||||
- LOKI_MODEL=
|
- LOKI_MODEL=
|
||||||
|
# Accès par NOM DE DOMAINE (reverse proxy, ex. loki.mondomaine.fr) sans
|
||||||
|
# clé de pilotage : liste ici le(s) nom(s), séparés par des virgules —
|
||||||
|
# sinon l'API répond 403 (protection contre les sites tiers / DNS
|
||||||
|
# rebinding). Inutile pour un accès par IP ou nom local.
|
||||||
|
- LOKI_TRUSTED_HOSTS=
|
||||||
volumes:
|
volumes:
|
||||||
# /data : config, base, mémoire, workspace, modèles téléchargés par l'UI
|
# /data : config, base, mémoire, workspace, modèles téléchargés par l'UI
|
||||||
- /mnt/user/appdata/loki/data:/data
|
- /mnt/user/appdata/loki/data:/data
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ services:
|
|||||||
- LOKI_MODEL=${LOKI_MODEL:-}
|
- LOKI_MODEL=${LOKI_MODEL:-}
|
||||||
- LOKI_CTX=${LOKI_CTX:-}
|
- LOKI_CTX=${LOKI_CTX:-}
|
||||||
- LOKI_NGL=${LOKI_NGL:-}
|
- LOKI_NGL=${LOKI_NGL:-}
|
||||||
|
# Accès par nom de domaine (reverse proxy) sans clé de pilotage : nom(s)
|
||||||
|
# autorisé(s), séparés par des virgules — sinon l'API répond 403.
|
||||||
|
- LOKI_TRUSTED_HOSTS=${LOKI_TRUSTED_HOSTS:-}
|
||||||
volumes:
|
volumes:
|
||||||
# /data = LOKI_HOME : config, base bbolt, mémoire, workspace, modèles téléchargés
|
# /data = LOKI_HOME : config, base bbolt, mémoire, workspace, modèles téléchargés
|
||||||
- ./data:/data
|
- ./data:/data
|
||||||
|
|||||||
@@ -3919,8 +3919,31 @@ async function jfetch(u, opts){
|
|||||||
const k = await askKeyOnce();
|
const k = await askKeyOnce();
|
||||||
if(k){ opts.headers = authHeaders(opts.headers); r = await fetch(u, opts); }
|
if(k){ opts.headers = authHeaders(opts.headers); r = await fetch(u, opts); }
|
||||||
}
|
}
|
||||||
|
// 403 de la garde anti-site-tiers (web_auth.go, crossSiteReject) : sans ce
|
||||||
|
// bandeau, l'interface restait vide sans dire pourquoi — chaque appel échouait
|
||||||
|
// en silence dans la console.
|
||||||
|
if(r.status === 403 && !accessBlockedShown){
|
||||||
|
r.clone().json().then(j=>{ if(j && j.error) showAccessBlocked(j.error); }).catch(()=>{});
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// showAccessBlocked : bandeau fixe expliquant le refus d'accès et comment le
|
||||||
|
// lever (nom de domaine derrière un reverse proxy, sans clé de pilotage).
|
||||||
|
let accessBlockedShown = false;
|
||||||
|
function showAccessBlocked(msg){
|
||||||
|
if(accessBlockedShown) return;
|
||||||
|
accessBlockedShown = true;
|
||||||
|
const d = document.createElement('div');
|
||||||
|
d.id = 'access-blocked';
|
||||||
|
d.setAttribute('role', 'alert');
|
||||||
|
d.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;padding:12px 16px;background:var(--err,#b3564d);color:#fff;font-size:13px;line-height:1.5';
|
||||||
|
const host = location.hostname;
|
||||||
|
d.innerHTML = '<b>Accès refusé par Loki</b> — ' + escHtml(msg) +
|
||||||
|
'<br>Derrière un reverse proxy : ajoute <code>LOKI_TRUSTED_HOSTS=' + escHtml(host) +
|
||||||
|
'</code> aux variables du conteneur puis recrée-le, ou définis une clé de pilotage (<code>docker exec -it loki loki set-web-key</code>).';
|
||||||
|
document.body.appendChild(d);
|
||||||
|
}
|
||||||
// askKeyOnce : UNE seule demande de clé à la fois, partagée par tous les appels.
|
// askKeyOnce : UNE seule demande de clé à la fois, partagée par tous les appels.
|
||||||
//
|
//
|
||||||
// Le chargement de l'UI lance une dizaine d'appels /api/* en parallèle, et le
|
// Le chargement de l'UI lance une dizaine d'appels /api/* en parallèle, et le
|
||||||
|
|||||||
@@ -33,8 +33,31 @@ async function jfetch(u, opts){
|
|||||||
const k = await askKeyOnce();
|
const k = await askKeyOnce();
|
||||||
if(k){ opts.headers = authHeaders(opts.headers); r = await fetch(u, opts); }
|
if(k){ opts.headers = authHeaders(opts.headers); r = await fetch(u, opts); }
|
||||||
}
|
}
|
||||||
|
// 403 de la garde anti-site-tiers (web_auth.go, crossSiteReject) : sans ce
|
||||||
|
// bandeau, l'interface restait vide sans dire pourquoi — chaque appel échouait
|
||||||
|
// en silence dans la console.
|
||||||
|
if(r.status === 403 && !accessBlockedShown){
|
||||||
|
r.clone().json().then(j=>{ if(j && j.error) showAccessBlocked(j.error); }).catch(()=>{});
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// showAccessBlocked : bandeau fixe expliquant le refus d'accès et comment le
|
||||||
|
// lever (nom de domaine derrière un reverse proxy, sans clé de pilotage).
|
||||||
|
let accessBlockedShown = false;
|
||||||
|
function showAccessBlocked(msg){
|
||||||
|
if(accessBlockedShown) return;
|
||||||
|
accessBlockedShown = true;
|
||||||
|
const d = document.createElement('div');
|
||||||
|
d.id = 'access-blocked';
|
||||||
|
d.setAttribute('role', 'alert');
|
||||||
|
d.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;padding:12px 16px;background:var(--err,#b3564d);color:#fff;font-size:13px;line-height:1.5';
|
||||||
|
const host = location.hostname;
|
||||||
|
d.innerHTML = '<b>Accès refusé par Loki</b> — ' + escHtml(msg) +
|
||||||
|
'<br>Derrière un reverse proxy : ajoute <code>LOKI_TRUSTED_HOSTS=' + escHtml(host) +
|
||||||
|
'</code> aux variables du conteneur puis recrée-le, ou définis une clé de pilotage (<code>docker exec -it loki loki set-web-key</code>).';
|
||||||
|
document.body.appendChild(d);
|
||||||
|
}
|
||||||
// askKeyOnce : UNE seule demande de clé à la fois, partagée par tous les appels.
|
// askKeyOnce : UNE seule demande de clé à la fois, partagée par tous les appels.
|
||||||
//
|
//
|
||||||
// Le chargement de l'UI lance une dizaine d'appels /api/* en parallèle, et le
|
// Le chargement de l'UI lance une dizaine d'appels /api/* en parallèle, et le
|
||||||
|
|||||||
Reference in new issue
Block a user