<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Monitoring on inetshell</title><link>https://inet.sh/es/tags/monitoring/</link><description>Recent content in Monitoring on inetshell</description><generator>Hugo</generator><language>es</language><lastBuildDate>Sat, 26 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/es/tags/monitoring/index.xml" rel="self" type="application/rss+xml"/><item><title>Backup consistente de Prometheus: snapshot con la admin API (enableAdminAPI en el operador), copiarlo, restaurarlo y borrar series</title><link>https://inet.sh/es/posts/monitoring/prometheus-snapshot-admin-api-backup/</link><pubDate>Sat, 26 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/es/posts/monitoring/prometheus-snapshot-admin-api-backup/</guid><description>Copiar /prometheus con Prometheus corriendo da un backup inconsistente. La TSDB tiene una API de administración, apagada por defecto, que crea un snapshot atómico con hardlinks en snapshots/&amp;lt;fecha&amp;gt;-&amp;lt;id&amp;gt;: cómo activarla (--web.enable-admin-api, o enableAdminAPI: true en el CRD del operador), el POST a /api/v1/admin/tsdb/snapshot, cómo sacar el snapshot de un pod sin que kubectl cp tarde una hora, restaurarlo en otra instancia, y los otros dos endpoints: delete_series y clean_tombstones para liberar espacio.</description><content:encoded><![CDATA[<h2 id="en-corto">En corto</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Prometheus Operator / kube-prometheus-stack</span>
</span></span><span class="line"><span class="cl">kubectl -n monitoring patch prometheus kube-prometheus-stack-prometheus --type merge <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -p <span class="s1">&#39;{&#34;spec&#34;:{&#34;enableAdminAPI&#34;:true}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl -n monitoring port-forward svc/prometheus-operated <span class="m">9090</span> <span class="p">&amp;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># crear el snapshot (la ruta es /api/v1, no /api/v2)</span>
</span></span><span class="line"><span class="cl">curl -s -XPOST http://localhost:9090/api/v1/admin/tsdb/snapshot
</span></span><span class="line"><span class="cl"><span class="c1"># {&#34;status&#34;:&#34;success&#34;,&#34;data&#34;:{&#34;name&#34;:&#34;20260926T101500Z-3f2b1c9a7d0e4b11&#34;}}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># sacarlo del pod con tar por stdout, mucho más rápido que kubectl cp</span>
</span></span><span class="line"><span class="cl"><span class="nv">SNAP</span><span class="o">=</span>20260926T101500Z-3f2b1c9a7d0e4b11
</span></span><span class="line"><span class="cl">kubectl -n monitoring <span class="nb">exec</span> prometheus-kube-prometheus-stack-prometheus-0 -c prometheus -- <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  tar cf - -C /prometheus/snapshots <span class="nv">$SNAP</span> <span class="p">|</span> tar xf - -C ./backup/
</span></span></code></pre></td></tr></table>
</div>
</div><p>Fuera de Kubernetes es lo mismo con el flag <code>--web.enable-admin-api</code> en la línea de comandos de Prometheus y el snapshot en <code>&lt;storage.tsdb.path&gt;/snapshots/</code>.</p>
<h2 id="por-qué-no-vale-copiar-el-directorio">Por qué no vale copiar el directorio</h2>
<p>La TSDB escribe en el <em>head</em> (WAL más el bloque en memoria) mientras compacta bloques de dos horas en bloques más grandes y borra los viejos. Un <code>cp -r</code> o un <code>rsync</code> de <code>/prometheus</code> en marcha pilla bloques a medio compactar, un WAL que ya no cuadra con los checkpoints, y con suerte arranca; con menos suerte, <code>opening storage failed: … corrupted</code>.</p>
<p>El snapshot lo hace Prometheus desde dentro: crea <code>snapshots/&lt;fecha&gt;-&lt;id&gt;/</code> con <strong>hardlinks</strong> a los bloques ya compactados (ocupa casi nada y tarda segundos) y vuelca el <em>head</em> como un bloque más, así que la copia es consistente hasta el instante de la petición. Con <code>?skip_head=true</code> se salta el head y solo incluye lo compactado, que basta si haces snapshots a diario y no te importan las últimas dos horas.</p>
<h2 id="restaurar">Restaurar</h2>
<p>Un snapshot es un directorio de bloques (<code>01HXYZ…/</code>, cada uno con <code>chunks/</code>, <code>index</code>, <code>meta.json</code>, <code>tombstones</code>). Restaurar es <strong>ponerlos en la raíz del directorio de datos</strong> de la instancia destino:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># instancia nueva (StatefulSet o binario) parada, o al menos con el data dir vacío</span>
</span></span><span class="line"><span class="cl">kubectl -n newprom <span class="nb">exec</span> prometheus-new-0 -c prometheus -- rm -rf /prometheus/wal /prometheus/01*
</span></span><span class="line"><span class="cl">tar cf - -C ./backup/<span class="nv">$SNAP</span> . <span class="p">|</span> kubectl -n newprom <span class="nb">exec</span> -i prometheus-new-0 -c prometheus -- tar xf - -C /prometheus/
</span></span><span class="line"><span class="cl">kubectl -n newprom delete pod prometheus-new-0     <span class="c1"># reinicia para que lea los bloques</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Prometheus carga los bloques al arrancar y los va compactando con los nuevos. Los bloques con <strong>etiquetas externas</strong> distintas (otro <code>cluster</code>, otro <code>replica</code>) se mezclan sin problema; lo que no puedes es restaurar bloques que se solapen en tiempo con datos que ya existen y esperar que se fusionen: se quedan duplicados hasta la siguiente compactación, y las consultas ven ambas series.</p>
<p><code>promtool tsdb list /prometheus</code> y <code>promtool tsdb analyze /prometheus &lt;bloque&gt;</code> dicen qué rango cubre cada bloque y qué series pesan más.</p>
<h2 id="borrar-series-y-liberar-espacio">Borrar series y liberar espacio</h2>
<p>La misma API sirve para quitar métricas que no debían estar (un exporter con cardinalidad desbocada, datos de una prueba):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">curl -s -XPOST -g <span class="s1">&#39;http://localhost:9090/api/v1/admin/tsdb/delete_series?match[]={job=&#34;noisy-exporter&#34;}&amp;start=2026-09-01T00:00:00Z&amp;end=2026-09-20T00:00:00Z&#39;</span>
</span></span><span class="line"><span class="cl">curl -s -XPOST http://localhost:9090/api/v1/admin/tsdb/clean_tombstones
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>delete_series</code> solo marca (tombstones); el espacio no se libera hasta <code>clean_tombstones</code> o la siguiente compactación. Los dos devuelven <code>204</code> sin cuerpo.</p>
<h2 id="trampas">Trampas</h2>
<ul>
<li><strong>La admin API no tiene autenticación</strong>: cualquiera que llegue al 9090 puede borrar toda la base. Actívala, haz el snapshot por <code>port-forward</code>, y vuelve a ponerla en <code>false</code>. Nunca detrás de un Ingress sin auth.</li>
<li><strong><code>kubectl cp</code> con miles de ficheros</strong> tarda una eternidad y a veces se corta; el <code>tar</code> por stdout de arriba es un solo flujo. Para snapshots de decenas de GB, monta un PVC de backup o usa un sidecar con <code>mc</code>/<code>rclone</code> hacia S3.</li>
<li><strong>Borra los snapshots viejos</strong> de <code>/prometheus/snapshots/</code>: son hardlinks, pero mantienen vivos bloques que la retención ya habría eliminado, y el disco crece sin que la retención lo explique.</li>
<li><strong>Thanos o Mimir</strong> con <code>sidecar</code> ya suben cada bloque a S3: ahí el snapshot solo sirve para el head. El backup son los buckets.</li>
<li><strong>kube-prometheus-stack</strong>: <code>prometheus.prometheusSpec.enableAdminAPI: true</code> en los values si quieres que el patch sobreviva al siguiente <code>helm upgrade</code>.</li>
</ul>
<p>Referencias: <a href="https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-admin-apis">TSDB Admin APIs</a> en la documentación de Prometheus.</p>
<h2 id="ver-también">Ver también</h2>
<ul>
<li><a href="/es/posts/monitoring/grafana-sso-disableautologin-roles-provisioning/">Grafana con SSO</a> — la otra mitad del stack.</li>
<li><a href="/es/posts/zfs/hold-proteger-snapshot/">ZFS: proteger snapshots con hold</a> — si el data dir de Prometheus vive en ZFS, los snapshots del sistema de archivos son otra opción consistente.</li>
</ul>
]]></content:encoded></item><item><title>Grafana con SSO: entrar con el usuario local (?disableAutoLogin), roles que se pisan en cada login (skip_org_role_sync) y datasources aprovisionados con secretos</title><link>https://inet.sh/es/posts/monitoring/grafana-sso-disableautologin-roles-provisioning/</link><pubDate>Sat, 26 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/es/posts/monitoring/grafana-sso-disableautologin-roles-provisioning/</guid><description>Tres cosas que acaban mordiendo en cualquier Grafana con OAuth: auto_login te manda al proveedor antes de ver el formulario y te deja fuera si el SSO se rompe (la salida es /login?disableAutoLogin=true), los roles que asignas a mano vuelven a su valor en el siguiente login salvo que uses skip_org_role_sync o mapees bien role_attribute_path, y los datasources aprovisionados por YAML que no se pueden editar en la UI, guardan secretos en secureJsonData y solo desaparecen con deleteDatasources o prune.</description><content:encoded><![CDATA[<h2 id="en-corto">En corto</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://grafana.example.com/login?disableAutoLogin=true
</span></span></code></pre></td></tr></table>
</div>
</div><p>Eso te lleva al formulario de usuario y contraseña aunque <code>auto_login = true</code> te esté redirigiendo al proveedor OAuth. Si además no recuerdas la contraseña del admin local:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># contenedor / pod</span>
</span></span><span class="line"><span class="cl">grafana cli --homepath /usr/share/grafana admin reset-admin-password <span class="s1">&#39;nueva&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># paquete</span>
</span></span><span class="line"><span class="cl">grafana-cli admin reset-admin-password <span class="s1">&#39;nueva&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Y si el formulario está desactivado (<code>[auth] disable_login_form = true</code>), primero <code>GF_AUTH_DISABLE_LOGIN_FORM=false</code> en el entorno y reinicia.</p>
<h2 id="roles-que-se-deshacen-solos">Roles que se deshacen solos</h2>
<p>Le das <em>Editor</em> a alguien en <em>Administration → Users</em>, y al día siguiente vuelve a ser <em>Viewer</em>. No es un bug: en cada login OAuth Grafana <strong>sincroniza el rol de la organización</strong> con lo que diga el proveedor (o con <code>auto_assign_org_role</code> si el proveedor no dice nada) y pisa lo que pusiste a mano.</p>
<p>Dos formas de vivir con ello:</p>
<p><strong>1. Que el proveedor mande de verdad.</strong> Mapea grupos o claims a roles con JMESPath y haz que sea estricto, para que un usuario sin grupo no entre como Viewer por defecto:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[auth.github]</span>
</span></span><span class="line"><span class="cl"><span class="na">enabled</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">allow_sign_up</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">auto_login</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">client_id</span> <span class="o">=</span> <span class="s">$__file{/etc/secrets/github-client-id}</span>
</span></span><span class="line"><span class="cl"><span class="na">client_secret</span> <span class="o">=</span> <span class="s">$__file{/etc/secrets/github-client-secret}</span>
</span></span><span class="line"><span class="cl"><span class="na">scopes</span> <span class="o">=</span> <span class="s">user:email,read:org</span>
</span></span><span class="line"><span class="cl"><span class="na">allowed_organizations</span> <span class="o">=</span> <span class="s">my-org</span>
</span></span><span class="line"><span class="cl"><span class="na">role_attribute_strict</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">allow_assign_grafana_admin</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">role_attribute_path</span> <span class="o">=</span> <span class="s">contains(groups[*], &#39;@my-org/grafana-admin&#39;) &amp;&amp; &#39;GrafanaAdmin&#39; || contains(groups[*], &#39;@my-org/grafana-editor&#39;) &amp;&amp; &#39;Editor&#39; || contains(groups[*], &#39;@my-org/grafana-viewer&#39;) &amp;&amp; &#39;Viewer&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>2. Que Grafana no toque los roles.</strong> Si prefieres gestionarlos en la UI:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[auth.github]</span>
</span></span><span class="line"><span class="cl"><span class="na">skip_org_role_sync</span> <span class="o">=</span> <span class="s">true</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>skip_org_role_sync</code> va <strong>por proveedor</strong> (<code>auth.github</code>, <code>auth.google</code>, <code>auth.generic_oauth</code>, <code>auth.azuread</code>, <code>auth.okta</code>, <code>auth.gitlab</code>, SAML). El antiguo interruptor global <code>[auth] oauth_skip_org_role_update_sync</code> está obsoleto; si todavía lo tienes en el <code>.ini</code>, cámbialo por el de cada proveedor.</p>
<p>Con Google, <code>hosted_domain</code> y <code>allowed_domains</code> limitan quién puede entrar; sin <code>role_attribute_path</code> todos entran con <code>auto_assign_org_role</code> (Viewer por defecto).</p>
<h2 id="datasources-aprovisionados">Datasources aprovisionados</h2>
<p>Los datasources definidos en <code>provisioning/datasources/*.yaml</code> (en el chart de Helm, <code>datasources.datasources.yaml</code>) tienen tres propiedades que sorprenden:</p>
<ul>
<li><strong>No se editan en la UI</strong> si llevan <code>editable: false</code>: el formulario aparece bloqueado. Es lo correcto en producción; el cambio va en el YAML.</li>
<li><strong>Los secretos van en <code>secureJsonData</code></strong>, no en <code>jsonData</code>: Grafana los cifra con <code>secret_key</code> antes de guardarlos en su base de datos (<code>/var/lib/grafana/grafana.db</code>, SQLite por defecto) y no los devuelve por la API. Tokens, contraseñas y certificados de cliente van ahí; en <code>jsonData</code> quedan en claro.</li>
<li><strong>No desaparecen al quitarlos del YAML.</strong> Grafana solo inserta y actualiza; para borrar hay que decirlo:</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">prune: true                  # versiones recientes</span><span class="p">:</span><span class="w"> </span><span class="l">borra lo aprovisionado que ya no esté en este archivo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">deleteDatasources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Sentry-old</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">orgId</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">datasources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">access</span><span class="p">:</span><span class="w"> </span><span class="l">proxy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">orgId</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">http://prometheus-server.monitoring.svc.cluster.local</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">isDefault</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">editable</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Sentry</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">grafana-sentry-datasource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">access</span><span class="p">:</span><span class="w"> </span><span class="l">proxy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">orgId</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">editable</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">jsonData</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">https://sentry.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">orgSlug</span><span class="p">:</span><span class="w"> </span><span class="l">my-org</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">secureJsonData</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">authToken</span><span class="p">:</span><span class="w"> </span><span class="l">${SENTRY_TOKEN}     </span><span class="w"> </span><span class="c"># variable de entorno del proceso de Grafana</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Las variables de entorno se expanden con <code>$VAR</code> o <code>${VAR}</code>; un <code>$</code> literal en un secreto se escribe <code>$$</code>.</p>
<h2 id="trampas">Trampas</h2>
<ul>
<li><strong><code>auto_login</code> con varios proveedores</strong> se ignora: solo funciona con uno configurado para auto-login.</li>
<li><strong><code>allow_sign_up = false</code></strong> deja fuera a cualquier usuario nuevo del proveedor, incluido el que acabas de dar de alta en el grupo; el error en el log es <em>&ldquo;user signup disabled&rdquo;</em>.</li>
<li><strong><code>role_attribute_strict = true</code> sin grupo mapeado</strong> rechaza el login con <em>&ldquo;Role attribute not found&rdquo;</em>; es lo que quieres, pero avisa a la gente.</li>
<li><strong>El admin local sigue existiendo</strong> con SSO: cámbiale la contraseña y guárdala en el gestor de secretos, es tu puerta de emergencia junto con <code>disableAutoLogin</code>.</li>
<li><strong><code>secret_key</code> por defecto</strong> (<code>SW2YcwTIb9zpOOhoPsMm</code>) cifra los <code>secureJsonData</code> de cualquier Grafana igual; ponle el tuyo (<code>[security] secret_key</code>) antes de aprovisionar nada, porque cambiarlo después invalida lo cifrado.</li>
</ul>
<p>Referencias: <a href="https://grafana.com/docs/grafana/latest/setup-grafana/configure-access/configure-authentication/">Configure authentication</a> y <a href="https://grafana.com/docs/grafana/latest/administration/provisioning/">Provisioning</a> en la documentación de Grafana.</p>
<h2 id="ver-también">Ver también</h2>
<ul>
<li><a href="/es/posts/proxmox/keycloak-openid-connect/">Proxmox con Keycloak (OpenID)</a> — el mismo patrón de SSO, con Keycloak como proveedor.</li>
</ul>
]]></content:encoded></item></channel></rss>