Ana ya tiene su remoto registrado. Escribe git push origin main con toda la ilusión y obtiene esto:

Username for 'https://git.ejemplo.es':

Un prompt esperando algo que no sabe muy bien qué es. Y si teclea la contraseña con la que entra en la interfaz web, es muy probable que reciba un rechazo del tipo Support for password authentication was removed.

Este muro detiene a mucha gente en su primer contacto con un repositorio remoto, y es una lástima, porque el problema no tiene nada que ver con Git: es un problema de identidad. Registrar una URL no da permiso para escribir en ella. Alguien tiene que demostrarle al servidor que es quien dice ser.

Esta lección resuelve ese muro de una vez y para siempre. Veremos por qué el clone de un repositorio público no pide nada y el push sí, los dos mecanismos de autenticación que se usan hoy —tokens sobre HTTPS y claves SSH—, cómo generar una clave con ssh-keygen, y cómo hacer que el sistema operativo recuerde las credenciales para no teclearlas cuarenta veces al día. Es la lección que Carla necesita antes de escribir su primera línea, porque entra desde Windows 11 y ahí el gestor de credenciales funciona de forma distinta.

Contenido

  1. Por qué el clone público no pide nada y el push
  2. Autenticación frente a autorización
  3. HTTPS con token personal de acceso
  4. SSH: el par de claves
  5. Generar la clave con ssh-keygen -t ed25519
  6. Subir la pública, probar la conexión y usar el agente
  7. ~/.ssh/config: varias identidades en la misma máquina
  8. HTTPS frente a SSH: tabla comparativa
  9. Gestores de credenciales por sistema operativo
  10. Errores típicos y cómo diagnosticarlos

  1. Por qué el clone público no pide nada y el push

La respuesta es sencilla y despeja mucha confusión: son operaciones con requisitos de permiso distintos.

Operación Qué hace Repositorio público Repositorio privado
git clone, git fetch, git pull Leer objetos y referencias Sin credenciales Credenciales
git push Escribir: modificar referencias del servidor Credenciales siempre Credenciales

Un repositorio público es, por definición, legible por cualquiera. Descargar el código de un proyecto abierto no requiere identificarse, igual que leer una página web no requiere cuenta.

Escribir es otra historia. Cuando envías, le estás pidiendo al servidor que modifique sus referencias: que refs/heads/main pase a apuntar a otro commit. Eso cambia lo que todo el mundo verá al clonar a partir de ese momento. Ningún servidor sensato permite eso a un desconocido.

Y hay un matiz que sorprende a mucha gente: la identidad de Git y la identidad del servidor son cosas distintas.

git config user.name
git config user.email

Eso que configuraste en la lección 01-06 es metadato del commit: texto que se graba dentro del objeto y que aparece en git log. No autentica nada. Cualquiera puede poner cualquier nombre y cualquier correo en sus commits; Git no lo verifica.

La autenticación ocurre en otra capa: en el transporte, cuando tu Git habla con el servidor. Son dos identidades independientes que conviene no mezclar:

user.name / user.email Credenciales de acceso
Dónde vive En la configuración de Git En el gestor de credenciales o en ~/.ssh/
Para qué sirve Firmar la autoría del commit Demostrar quién eres ante el servidor
Lo verifica alguien No Sí, el servidor, en cada conexión
Cuándo interviene Al hacer git commit Al hacer fetch, pull o push

(Existe una forma de vincular ambas cosas de verdad, firmando los commits criptográficamente. Es un tema de seguridad y se trata en la lección 08-05.)

  1. Autenticación frente a autorización

Dos palabras que se confunden a diario y que producen errores muy distintos:

  • Autenticación: quién eres. Demostrar tu identidad ante el servidor.
  • Autorización: qué puedes hacer. Los permisos que esa identidad tiene sobre ese repositorio concreto.

Puedes estar perfectamente autenticado y aun así recibir un rechazo:

remote: Permission to equipo/gestor-tareas.git denied to carla.
fatal: unable to access 'https://git.ejemplo.es/equipo/gestor-tareas.git/': The requested URL returned error: 403

Fíjate en el mensaje: el servidor sabe que eres Carla. La autenticación ha funcionado. Lo que falla es la autorización: a Carla no le han dado permiso de escritura en ese repositorio, o su token no tiene el ámbito necesario.

Distinguir los dos casos ahorra mucho tiempo:

Síntoma Capa que falla Qué revisar
Permission denied (publickey) Autenticación (SSH) La clave: si existe, si está cargada, si está subida al servidor
Authentication failed (HTTPS) Autenticación El token: si es válido, si ha caducado, si está bien guardado
403 Forbidden, Permission to … denied to <usuario> Autorización Los permisos de tu cuenta en el repositorio; los ámbitos del token
Repository not found Ambas, ambiguamente Muchas plataformas devuelven esto para repositorios privados a los que no tienes acceso, para no revelar que existen

Ese último caso es especialmente traicionero: Repository not found no significa necesariamente que hayas escrito mal la URL. Puede significar "existe, pero para ti no".

  1. HTTPS con token personal de acceso

Qué es un PAT y por qué sustituyó a la contraseña

Un token personal de acceso (Personal Access Token, PAT) es una cadena larga generada por la plataforma que funciona como contraseña para un uso concreto. Tiene un aspecto parecido a este (ficticio, evidentemente):

glpat-7fK2mQx9RtVb3Ln8ZwYc

Durante años, la autenticación por HTTPS se hacía con el usuario y la contraseña de la cuenta. GitHub retiró esa posibilidad en agosto de 2021 y el resto de plataformas siguió el mismo camino. Las razones son sólidas:

  • Ámbito limitado. Tu contraseña abre toda la cuenta: repositorios, ajustes, facturación, borrado. Un token puede dar solo lectura de un repositorio concreto.
  • Revocable individualmente. Si un token se filtra, lo eliminas y solo se rompe lo que lo usaba. Cambiar la contraseña te obliga a reconfigurar todo.
  • Compatible con doble factor. Si tu cuenta tiene segundo factor, no hay forma de introducirlo en un prompt de Git. El token resuelve ese callejón sin salida.
  • Caducidad. Un token puede expirar en 30, 60 o 90 días; una contraseña vive hasta que alguien se acuerda de cambiarla.
  • Auditable. La plataforma registra qué token hizo qué y cuándo se usó por última vez.

Cómo se crea y con qué ámbitos

El procedimiento concreto varía por plataforma, pero el esquema es siempre el mismo: ajustes de la cuenta → tokens de acceso → crear → elegir nombre, caducidad y ámbitos.

Los ámbitos son la parte importante. Concede el mínimo imprescindible:

Necesidad Ámbito típico Qué permite
Clonar y hacer fetch de repositorios privados read_repository / repo:read Solo lectura
Hacer push write_repository / repo Lectura y escritura de código
Solo un repositorio concreto Token con ámbito de proyecto Acceso acotado a ese repositorio
Integración continua El mínimo que necesite la tarea Ver módulo 7

Lo que casi nunca necesitas: ámbitos de administración, de gestión de usuarios, de borrado de repositorios o de acceso a los ajustes de la organización. Un token de despliegue que solo tiene que clonar no debería poder borrar nada.

Un consejo operativo: crea un token por máquina, no uno que compartas entre tu portátil, tu ordenador de casa y el servidor de integración continua. Si pierdes el portátil, revocas ese token y el resto sigue funcionando.

Cómo se usa

El token se introduce en el sitio de la contraseña:

git push origin main
Username for 'https://git.ejemplo.es': ana.ferrer
Password for 'https://[email protected]':

En ese segundo prompt va el token, no la contraseña de la cuenta. Como no se ve mientras se escribe, es cómodo pegarlo desde el portapapeles.

Lo que no debes hacer nunca es incrustarlo en la URL:

# MAL: no hagas esto
git remote add origin https://ana:[email protected]/equipo/gestor-tareas.git

Ese token acaba escrito en texto plano en .git/config, aparece en el historial de tu shell, se cuela en capturas de pantalla y sale en cualquier git remote -v que ejecutes delante de alguien. Para eso existen los gestores de credenciales del apartado 9.

  1. SSH: el par de claves

La alternativa a HTTPS es SSH, y se basa en un concepto distinto: en lugar de un secreto compartido, se usa un par de claves asimétricas.

  • La clave privada (~/.ssh/id_ed25519) se queda en tu máquina y no sale de ahí jamás. Es el equivalente a tu llave física.
  • La clave pública (~/.ssh/id_ed25519.pub) se sube al servidor. Es el equivalente a la cerradura: se puede repartir sin riesgo.

La propiedad matemática que lo hace funcionar es que lo que se verifica con la pública solo lo ha podido producir la privada, y la privada no se puede deducir de la pública.

sequenceDiagram
    participant C as Portátil de Carla
    participant S as git.ejemplo.es

    C->>S: Quiero conectarme como usuario "git"
    S->>C: Reto: firma este dato aleatorio
    Note over C: Firma con la clave PRIVADA<br/>(que nunca sale del disco)
    C->>S: Aquí tienes la firma
    Note over S: Verifica con la clave PÚBLICA<br/>que Carla subió antes
    S->>C: Verificada. Acceso concedido

La ventaja sobre el token es evidente: el secreto nunca viaja por la red. Ni siquiera cifrado. Lo único que se transmite es una firma de un dato aleatorio distinto en cada conexión, inútil para un atacante que la capture.

Los tipos de clave

Tipo Estado Comentario
Ed25519 Recomendado Moderno, rápido, claves cortas (68 caracteres), seguridad excelente
RSA 4096 Aceptable Seguro, pero claves largas y más lento; útil en sistemas antiguos que no admiten Ed25519
RSA 2048 Desaconsejado Ya no se considera suficiente
ECDSA Desaconsejado Dudas sobre las curvas estándar; Ed25519 lo supera en todo
DSA Prohibido Obsoleto y eliminado de OpenSSH

Usa Ed25519 salvo que te encuentres con un servidor antiguo que lo rechace, en cuyo caso rsa con 4096 bits.

  1. Generar la clave con ssh-keygen -t ed25519

Carla, desde su Windows 11, abre Git Bash (que instaló en la lección 01-02 y que trae todas las herramientas de OpenSSH). Los comandos son idénticos en Linux, macOS y Git Bash:

ssh-keygen -t ed25519 -C "[email protected]"

Desglose de las opciones:

  • -t ed25519: el tipo de clave. Es el argumento importante.
  • -C "…": un comentario que se añade al final de la clave pública. Sirve para identificarla en la lista del servidor cuando tengas cuatro claves de cuatro máquinas. Puede ser el correo, pero es incluso mejor algo como "carla-portatil-windows", porque lo que quieres saber es de qué máquina es esa clave.

El diálogo:

Generating public/private ed25519 key pair.
Enter file in which to save the key (/c/Users/carla/.ssh/id_ed25519):

Pulsa Intro para aceptar la ruta por defecto. Solo cámbiala si vas a tener varias claves, caso que veremos en el apartado 7.

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

Aquí hay que detenerse. La passphrase cifra la clave privada en el disco. Si alguien te roba el portátil o copia el fichero, sin ella la clave es inservible.

Con passphrase Sin passphrase
Seguridad si te roban el fichero La clave es inútil sin la frase Acceso inmediato a tus repositorios
Comodidad diaria Se teclea una vez por sesión (con ssh-agent) Nunca se teclea
Uso en automatización sin supervisión Complicado Es lo habitual, con la clave muy restringida
Recomendación Ponla siempre en máquinas personales Solo en claves de despliegue de solo lectura

Ponla. Con ssh-agent, el coste real es teclearla una vez al arrancar el ordenador.

El resultado:

Your identification has been saved in /c/Users/carla/.ssh/id_ed25519
Your public key has been saved in /c/Users/carla/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:9xK2mQ7RtVb3Ln8ZwYcF4dJ6hP1sT5uA2eB9gN0kM3o [email protected]

Dos ficheros. Veamos qué contienen:

ls -l ~/.ssh/
-rw-------  1 carla  carla   464 ago  1 09:12 id_ed25519
-rw-r--r--  1 carla  carla   103 ago  1 09:12 id_ed25519.pub

Fíjate en los permisos, que son parte del mecanismo de seguridad:

  • La privada es -rw------- (600): solo tú puedes leerla. Si tuviera permisos más abiertos, OpenSSH se negaría a usarla.
  • La pública es -rw-r--r-- (644): legible por cualquiera, y no pasa nada.
cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK7Rt2mQx9VbLn8ZwYcF4dJ6hP1sT5uA2eB9gN0kM3o [email protected]

Tres partes: el tipo, la clave en sí y el comentario. Esta línea es la que se sube al servidor, y se puede publicar sin ningún riesgo.

Y la privada, con la advertencia obvia:

head -2 ~/.ssh/id_ed25519
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDl...

Este contenido no se enseña, no se copia y no se envía a nadie. Nunca. Ni a un compañero, ni a soporte técnico, ni pegado en un chat. Si alguna vez tienes la duda de cuál de los dos ficheros hay que subir, la regla es infalible: el que acaba en .pub.

  1. Subir la pública, probar la conexión y usar el agente

Subir la clave pública

Se copia el contenido del .pub y se pega en la plataforma (ajustes de la cuenta → claves SSH → nueva clave). Para copiarlo al portapapeles:

# Linux (con xclip instalado)
xclip -selection clipboard < ~/.ssh/id_ed25519.pub

# macOS
pbcopy < ~/.ssh/id_ed25519.pub

# Windows con Git Bash
clip < ~/.ssh/id_ed25519.pub

Un aviso frecuente: al pegar, no debe quedar ningún salto de línea en medio. La clave es una sola línea larga; si el editor la parte, no funcionará.

Probar la conexión con ssh -T

Antes de intentar ningún comando de Git, comprueba que el canal funciona:

La opción -T significa "no quiero terminal interactiva": solo estoy comprobando la autenticación.

La primera vez aparece esto:

The authenticity of host 'git.ejemplo.es (192.0.2.42)' can't be established.
ED25519 key fingerprint is SHA256:p2Q9xL4mR7tVc3Nb8ZwYeF6dJ1sK5uA0eB7gN2kM9o.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

No respondas yes por inercia. Ese mensaje dice que tu ordenador no conoce todavía a ese servidor y te pide que confirmes que es el auténtico. Las plataformas publican sus huellas en su documentación; compárala antes de aceptar. Es la única defensa contra un ataque de intermediario en esa primera conexión.

Al aceptar, la huella se guarda en ~/.ssh/known_hosts y no volverá a preguntar.

Si todo va bien:

Hi carla! You've successfully authenticated, but I do not provide shell access.

Ese mensaje es un éxito completo, aunque diga que no da acceso al shell. Eso es exactamente lo que debe pasar: el usuario git de un servidor de repositorios no sirve para abrir una sesión, solo para hablar Git.

ssh-agent: teclear la passphrase una sola vez

Con passphrase, cada operación pediría la frase. El agente la guarda en memoria durante la sesión y responde por ti.

# Arrancar el agente (si no está ya en marcha)
eval "$(ssh-agent -s)"
Agent pid 5842
# Cargar la clave: pedirá la passphrase una vez
ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/carla/.ssh/id_ed25519:
Identity added: /home/carla/.ssh/id_ed25519 ([email protected])
# Ver qué claves tiene cargadas
ssh-add -l
256 SHA256:9xK2mQ7RtVb3Ln8ZwYcF4dJ6hP1sT5uA2eB9gN0kM3o [email protected] (ED25519)

A partir de aquí, todos los push y pull funcionan sin teclear nada.

Cada sistema tiene su forma de dejarlo automatizado:

# Linux: añadir al final de ~/.bashrc o ~/.zshrc
if [ -z "$SSH_AUTH_SOCK" ]; then
  eval "$(ssh-agent -s)" > /dev/null
  ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi
# macOS: guarda la passphrase en el Llavero y la carga sola
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
# macOS: y para que se cargue en cada sesión, en ~/.ssh/config
Host *
  UseKeychain yes
  AddKeysToAgent yes
  IdentityFile ~/.ssh/id_ed25519

En Windows 11, el servicio ssh-agent viene con el sistema pero arranca desactivado. Desde PowerShell como administrador:

Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent

Y después, ya desde Git Bash:

ssh-add ~/.ssh/id_ed25519

Un detalle para Carla: si usa el ssh de Git Bash y el agente del sistema, pueden no verse entre sí. La solución sencilla es usar el bloque de ~/.bashrc de arriba, que arranca un agente propio de Git Bash.

  1. ~/.ssh/config: varias identidades en la misma máquina

Situación muy real, y precisamente la de Carla: en la lección 01-05 vimos que trabaja en dos contextos, el de ejemplo.es y el de otra empresa, con correos distintos. Si además cada uno exige una cuenta y una clave distintas, hace falta decirle a SSH cuál usar en cada caso.

Eso se resuelve en ~/.ssh/config:

# Cuenta del trabajo
Host git.ejemplo.es
    HostName git.ejemplo.es
    User git
    IdentityFile ~/.ssh/id_ed25519_trabajo
    IdentitiesOnly yes
    AddKeysToAgent yes

# Cuenta de la otra empresa: alias inventado
Host git-otraempresa
    HostName git.otraempresa.es
    User git
    IdentityFile ~/.ssh/id_ed25519_otraempresa
    IdentitiesOnly yes
    AddKeysToAgent yes

Explicación de cada directiva:

Directiva Qué hace
Host El alias que escribes en la URL. Puede ser inventado
HostName El servidor real al que conectarse
User El usuario SSH: en las plataformas de Git es casi siempre git
IdentityFile Qué clave privada usar para este destino
IdentitiesOnly yes Usa solo esa clave, no todas las que tengas cargadas
AddKeysToAgent yes Carga la clave en el agente al primer uso

IdentitiesOnly yes merece un comentario, porque evita un fallo desconcertante. Sin él, SSH ofrece todas tus claves una tras otra hasta que alguna funcione; si tienes cinco, el servidor puede cortar la conexión por exceso de intentos y obtendrás un Too many authentication failures que no parece tener nada que ver con la causa real.

El truco del alias inventado es lo que hace potente este fichero. Fíjate en Host git-otraempresa: ese nombre no existe en ningún DNS. Pero ahora Carla puede escribir:

git clone git@git-otraempresa:proyectos/facturacion.git

Y SSH traduce git-otraempresa a git.otraempresa.es con la clave correcta. El mismo mecanismo sirve para dos cuentas en la misma plataforma, que es el caso clásico de tener una cuenta personal y otra de empresa en el mismo GitHub:

Host github-personal
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_personal
    IdentitiesOnly yes

Host github-empresa
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_empresa
    IdentitiesOnly yes
# Repositorio personal
git clone git@github-personal:carla/mi-blog.git

# Repositorio de la empresa
git clone git@github-empresa:ejemplo-sl/gestor-tareas.git

Mismo servidor real, dos identidades, sin ninguna ambigüedad.

Y para comprobar qué clave se va a usar realmente:

ssh -T -v [email protected] 2>&1 | grep -i "offering\|identity file"
debug1: identity file /home/carla/.ssh/id_ed25519_trabajo type 3
debug1: Offering public key: /home/carla/.ssh/id_ed25519_trabajo ED25519

La opción -v (verbose) es la herramienta de diagnóstico definitiva para cualquier problema de SSH.

Permisos del fichero, que también son estrictos:

chmod 600 ~/.ssh/config
chmod 700 ~/.ssh

  1. HTTPS frente a SSH: tabla comparativa

Las dos opciones son válidas y las dos se usan masivamente. La elección depende del contexto:

Criterio HTTPS + token SSH
Facilidad inicial Alta: pegar un token y listo Media: generar clave, subirla, probar
Cortafuegos corporativos Excelente: usa el puerto 443, abierto en todas partes Problemática: el puerto 22 suele estar cerrado
Uso diario Transparente con gestor de credenciales Transparente con ssh-agent
El secreto viaja por la red Sí (dentro del cifrado TLS) No, nunca
Caducidad Sí: hay que renovar el token cada 30-90 días No caduca
Granularidad de permisos Alta: ámbitos por token Baja: la clave da lo que da tu cuenta
Varias identidades Complicado: el gestor guarda una por servidor Sencillo con ~/.ssh/config
Automatización y CI Preferido: tokens con ámbito mínimo y caducidad Posible con claves de despliegue
Revocación Inmediata desde la web Inmediata borrando la clave
Repositorios públicos de solo lectura Ideal: sin credenciales Innecesario
Detrás de un proxy Se configura fácilmente en Git Requiere configuración adicional de SSH

Recomendación práctica

  • Máquina de trabajo diaria, misma cuenta siempreSSH. Se configura una vez y se olvida: no caduca, no se renueva y no interrumpe.
  • Red corporativa con el puerto 22 cerradoHTTPS + token. (Algunas plataformas ofrecen SSH por el puerto 443 como alternativa.)
  • Integración continua, contenedores, servidoresToken con el ámbito mínimo y caducidad corta, o una clave de despliegue de solo lectura.
  • Clonar un proyecto público para leerloHTTPS sin credenciales.
  • Varias cuentas en la misma máquinaSSH con ~/.ssh/config.

Y un dato tranquilizador: cambiar de una a otra es un set-url, como vimos en la lección anterior:

# De HTTPS a SSH
git remote set-url origin [email protected]:equipo/gestor-tareas.git

# De SSH a HTTPS
git remote set-url origin https://git.ejemplo.es/equipo/gestor-tareas.git

No pierdes nada: los commits son los mismos, las referencias son las mismas y solo cambia el camino.

  1. Gestores de credenciales por sistema operativo

Con HTTPS queda un problema práctico: nadie va a teclear un token de 40 caracteres en cada push. Para eso existe credential.helper, que ya asomó en la lección 01-06 y que ahora podemos entender del todo.

Un credential helper es un programa externo al que Git le pregunta "¿tienes credenciales para este servidor?" antes de mostrar el prompt, y al que le dice "guárdate estas" cuando el acceso funciona.

sequenceDiagram
    participant G as git push
    participant H as credential.helper
    participant A as Almacén del sistema
    participant S as Servidor

    G->>H: ¿Credenciales para git.ejemplo.es?
    H->>A: Consulta (cifrada)
    A-->>H: usuario + token
    H-->>G: Aquí están
    G->>S: Autenticación
    S-->>G: Correcto
    Note over G,A: Si fallan, Git pregunta al usuario<br/>y pide al helper que guarde las nuevas

El de cada sistema

Linux (Ana, Ubuntu) — libsecret

Se integra con el llavero del escritorio (GNOME Keyring, KWallet), que guarda los secretos cifrados y se desbloquea con la sesión:

sudo apt install libsecret-1-0 libsecret-1-dev

# Compilar el ayudante que viene con Git
sudo make --directory=/usr/share/doc/git/contrib/credential/libsecret

git config --global credential.helper \
  /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret

En algunas distribuciones viene ya compilado y basta con instalar el paquete correspondiente.

macOS (Bruno) — osxkeychain

Viene incluido con Git en macOS; una sola línea:

git config --global credential.helper osxkeychain

Las credenciales se guardan en el Llavero, cifradas y visibles en la aplicación Acceso a Llaveros, donde se pueden revisar y borrar.

Windows 11 (Carla) — Git Credential Manager

Es el caso más interesante, y por eso lo dejamos para el final. GCM se instala junto con Git para Windows y queda configurado por defecto:

git config --global credential.helper manager
# Comprobar qué hay configurado
git config --global --get credential.helper
manager

Lo que hace GCM va más allá de guardar una cadena:

  • Guarda las credenciales en el Administrador de credenciales de Windows, cifradas con la cuenta de usuario del sistema.
  • Abre una ventana de navegador para autenticarse contra GitHub, GitLab o Azure DevOps, con soporte para doble factor.
  • Puede generar y renovar tokens por sí mismo, de modo que Carla no tiene que crear ninguno a mano.
  • Es multiplataforma: existe también para Linux y macOS, aunque en esos sistemas los nativos suelen bastar.

Para Carla, esto significa que su primer git clone de un repositorio privado abrirá una ventana del navegador, ella entrará con su cuenta y su segundo factor, y a partir de ahí no volverá a ver un prompt. Es, con diferencia, la experiencia más cómoda de las tres.

Si quiere comprobar qué hay guardado, puede abrirlo desde Git Bash:

# Abre el Administrador de credenciales de Windows
control /name Microsoft.CredentialManager

Las entradas de Git aparecen en la sección "Credenciales genéricas" con el prefijo git:.

Resumen y store, el que no debes usar

Ayudante Sistema Dónde guarda Cifrado Recomendación
manager Windows (y multiplataforma) Administrador de credenciales de Windows La opción en Windows
osxkeychain macOS Llavero del sistema La opción en macOS
libsecret Linux Llavero del escritorio La opción en Linux
cache Cualquiera Memoria RAM, temporalmente No aplica: no toca el disco Aceptable: seguro pero olvidadizo
store Cualquiera ~/.git-credentials, texto plano NO Evítalo

Por qué store guarda en texto plano y cuándo (no) usarlo

Es tentador por lo fácil que es:

git config --global credential.helper store

Y funciona a la primera. El problema se ve al mirar el fichero que crea:

cat ~/.git-credentials
https://ana.ferrer:[email protected]

Tu token, legible, en un fichero de tu carpeta personal. Sin cifrar, sin contraseña maestra, sin caducidad. Los permisos son 600, lo cual protege de otros usuarios de la misma máquina, pero no de:

  • Cualquier programa que se ejecute con tu usuario, incluida una dependencia maliciosa de tu proyecto.
  • Una copia de seguridad de tu carpeta personal que acabe en un disco externo o en la nube.
  • Alguien que arranque el ordenador desde un USB o extraiga el disco.
  • Un fichero de configuración sincronizado por error entre máquinas.

Cuándo no usarlo: en tu portátil, en tu ordenador de sobremesa, en cualquier máquina con acceso a repositorios que importen.

Cuándo podría tener sentido: en un contenedor efímero o una máquina virtual desechable, aislada, con un token de solo lectura y caducidad de horas, donde no hay ningún llavero disponible y el ciclo de vida de la máquina es más corto que el del token. Incluso ahí, casi siempre hay una opción mejor.

La alternativa razonable cuando no hay llavero es cache, que mantiene las credenciales en memoria durante un tiempo limitado:

git config --global credential.helper 'cache --timeout=3600'

Una hora sin volver a teclear, y nada tocando el disco. Al apagar, desaparece.

Todo lo relativo a la gestión de secretos, la firma de commits y el tratamiento de credenciales filtradas en el historial se desarrolla en la lección 08-05. Aquí nos hemos ocupado de lo operativo: poder trabajar de forma cómoda sin dejar secretos tirados.

  1. Errores típicos y cómo diagnosticarlos

Permission denied (publickey)

[email protected]: Permission denied (publickey).
fatal: Could not read from remote repository.

Es el error de SSH más habitual. Significa: "El servidor no ha aceptado ninguna de las claves que le has ofrecido." Las causas, en orden de frecuencia:

# 1. ¿Existe la clave?
ls -l ~/.ssh/
# 2. ¿Está cargada en el agente?
ssh-add -l

Si responde Could not open a connection to your authentication agent, el agente no está en marcha:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

Si responde The agent has no identities, está en marcha pero vacío: falta el ssh-add.

# 3. ¿La clave pública subida al servidor es la misma que tienes?
ssh-keygen -lf ~/.ssh/id_ed25519.pub
256 SHA256:9xK2mQ7RtVb3Ln8ZwYcF4dJ6hP1sT5uA2eB9gN0kM3o [email protected] (ED25519)

Compara esa huella con la que muestra la plataforma en la lista de claves. Si no coincide, subiste otra.

# 4. ¿Los permisos son correctos?
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

OpenSSH ignora en silencio las claves privadas con permisos demasiado abiertos, lo cual produce este error sin ninguna pista.

# 5. El diagnóstico definitivo
ssh -vT [email protected]

En la salida verás qué claves ofrece (Offering public key:) y cómo responde el servidor. Es la forma más rápida de descubrir que estás ofreciendo la clave equivocada.

Host key verification failed

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Significa que la clave del servidor no coincide con la que tenías guardada en ~/.ssh/known_hosts. Hay dos explicaciones posibles:

  1. Legítima: el servidor se ha reinstalado, se ha migrado o la plataforma ha rotado sus claves (ocurre de vez en cuando y siempre se anuncia públicamente).
  2. Ataque de intermediario: alguien se está haciendo pasar por el servidor.

No borres la entrada sin comprobar. El procedimiento correcto:

# 1. Ver qué huella está presentando el servidor ahora
ssh-keyscan git.ejemplo.es 2>/dev/null | ssh-keygen -lf -
256 SHA256:p2Q9xL4mR7tVc3Nb8ZwYeF6dJ1sK5uA0eB7gN2kM9o git.ejemplo.es (ED25519)
# 2. Comparar con la huella oficial publicada por la plataforma.
#    Si y solo si coincide, eliminar la entrada antigua:
ssh-keygen -R git.ejemplo.es

# 3. Reconectar y aceptar la nueva
ssh -T [email protected]

La variante suave del mismo aviso —The authenticity of host … can't be established— es la primera conexión: no hay nada guardado todavía. También ahí conviene comparar la huella antes de aceptar.

Otros errores frecuentes

Mensaje Causa habitual Solución
Support for password authentication was removed Estás usando la contraseña de la cuenta Genera un token y úsalo en su lugar
Authentication failed for 'https://…' Token caducado, revocado o mal guardado Borra la credencial del gestor y vuelve a autenticarte
403 Forbidden / Permission to … denied Autorización, no autenticación Revisa permisos en el repositorio y ámbitos del token
Repository not found Repositorio privado sin acceso, o URL errónea Verifica la URL y tus permisos
Connection timed out (puerto 22) Cortafuegos bloqueando SSH Usa HTTPS, o SSH por el puerto 443 si la plataforma lo ofrece
Too many authentication failures SSH ofrece demasiadas claves IdentitiesOnly yes en ~/.ssh/config
Load key … bad permissions Permisos demasiado abiertos en la privada chmod 600 ~/.ssh/id_ed25519

Y un comando que resuelve el "no sé qué credencial está usando Git":

GIT_TRACE=1 GIT_CURL_VERBOSE=1 git fetch origin 2>&1 | head -30

Muestra el proceso completo, incluido qué ayudante de credenciales se invoca. Úsalo solo para depurar y no compartas su salida sin revisarla, porque puede incluir cabeceras sensibles.

Errores Comunes y Consejos

Error 1: subir la clave privada en lugar de la pública. Ocurre más de lo que parece. La regla es simple: se sube el fichero que termina en .pub. Si has subido la privada por error, bórrala del servidor, elimina el par entero de tu disco y genera uno nuevo: esa clave está comprometida para siempre.

Error 2: incrustar el token en la URL del remoto. Acaba en .git/config, en el historial de la shell y en cualquier captura de pantalla. Usa un gestor de credenciales.

Error 3: crear la clave sin passphrase "para ir rápido". El robo del portátil pasa de ser un problema de hardware a ser un problema de acceso a todos tus repositorios. Con ssh-agent, la passphrase se teclea una vez por sesión.

Error 4: confundir user.email con la identidad de acceso. Cambiar user.email no arregla ningún problema de autenticación: solo cambia el texto que aparece en los commits. Son capas distintas.

Error 5: aceptar la huella del servidor sin mirarla. Ese prompt de la primera conexión es la única oportunidad de detectar una suplantación. Las plataformas publican sus huellas; compararlas cuesta diez segundos.

Error 6: usar el mismo token en cinco máquinas. Cuando haya que revocarlo, se romperán las cinco a la vez y no sabrás cuál se filtró. Un token por máquina y por propósito.

Error 7: usar credential.helper store por defecto. Deja el token en texto plano en tu carpeta personal. Usa el gestor nativo de tu sistema, o cache si no hay ninguno disponible.

Consejo 1: prueba siempre con ssh -T antes de culpar a Git. Si ssh -T git@servidor funciona, el problema no está en SSH. Si no funciona, ssh -vT te dice exactamente en qué paso falla.

Consejo 2: pon un comentario útil en la clave. -C "carla-portatil-windows" es infinitamente más práctico que el correo cuando dentro de un año veas cuatro claves en la lista del servidor y tengas que decidir cuál revocar.

Consejo 3: apunta la caducidad de tus tokens. Un token que expira un lunes por la mañana genera media hora de desconcierto. Ponte un recordatorio unos días antes.

Consejo 4: IdentitiesOnly yes siempre que tengas más de una clave. Evita el Too many authentication failures, que es de los errores más difíciles de relacionar con su causa.

Consejo 5: para automatización, ámbito mínimo y caducidad corta. Un token de integración continua que solo tiene que clonar no necesita permiso de escritura ni de administración.

Ejercicios

Ejercicio 1: generar y auditar un par de claves

Sin subir nada a ningún servidor:

  1. Genera un par Ed25519 en una ruta específica: ~/.ssh/practica_curso, con un comentario descriptivo.
  2. Muestra los permisos de los dos ficheros y explica por qué son distintos.
  3. Muestra la huella de la clave pública.
  4. Cárgala en ssh-agent y verifica que aparece listada.
  5. Comprueba que la huella de la clave privada y la de la pública coinciden. Explica por qué.
  6. Descárgala del agente y elimina los ficheros.

Ejercicio 2: configurar dos identidades

Carla necesita trabajar con dos cuentas: la del equipo (git.ejemplo.es) y la de otra empresa (git.otraempresa.es).

  1. Escribe el ~/.ssh/config completo para las dos, con claves separadas.
  2. Explica qué hace cada directiva.
  3. Escribe la URL de clonado que usaría para cada repositorio.
  4. Indica el comando que le permitiría comprobar qué clave concreta se ofrece a cada servidor.
  5. Explica qué pasaría si omitiera IdentitiesOnly yes y tuviera seis claves cargadas.

Ejercicio 3: diagnóstico

Para cada uno de estos mensajes, indica: (a) si el fallo es de autenticación o de autorización, (b) las dos causas más probables y (c) el comando o comprobación con el que empezarías.

  1. [email protected]: Permission denied (publickey).
  2. remote: Permission to equipo/gestor-tareas.git denied to carla.
  3. remote: Support for password authentication was removed.
  4. WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
  5. ssh: connect to host git.ejemplo.es port 22: Connection timed out

Soluciones

Solución 1:

# 1. Generar con ruta y comentario
ssh-keygen -t ed25519 -f ~/.ssh/practica_curso -C "practica-curso-git"
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/carla/.ssh/practica_curso
Your public key has been saved in /home/carla/.ssh/practica_curso.pub
The key fingerprint is:
SHA256:4tR8mK2nQ7xVb3Lc9ZwYeF6dJ1sP5uA0eB7gN2kM3o practica-curso-git

La opción -f fija la ruta y evita el diálogo de "dónde guardar".

# 2. Permisos
ls -l ~/.ssh/practica_curso*
-rw------- 1 carla carla 464 ago  1 10:22 /home/carla/.ssh/practica_curso
-rw-r--r-- 1 carla carla 103 ago  1 10:22 /home/carla/.ssh/practica_curso.pub

La privada es 600: solo su dueño puede leerla, y OpenSSH se niega a usarla si tuviera permisos más laxos, porque cualquier otro usuario del sistema podría copiarla. La pública es 644 porque está diseñada para repartirse: no revela nada explotable.

# 3. Huella de la pública
ssh-keygen -lf ~/.ssh/practica_curso.pub
256 SHA256:4tR8mK2nQ7xVb3Lc9ZwYeF6dJ1sP5uA0eB7gN2kM3o practica-curso-git (ED25519)
# 4. Cargar y listar
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/practica_curso
ssh-add -l
Agent pid 6120
Enter passphrase for /home/carla/.ssh/practica_curso:
Identity added: /home/carla/.ssh/practica_curso (practica-curso-git)
256 SHA256:4tR8mK2nQ7xVb3Lc9ZwYeF6dJ1sP5uA0eB7gN2kM3o practica-curso-git (ED25519)
# 5. Las dos huellas
ssh-keygen -lf ~/.ssh/practica_curso
ssh-keygen -lf ~/.ssh/practica_curso.pub
256 SHA256:4tR8mK2nQ7xVb3Lc9ZwYeF6dJ1sP5uA0eB7gN2kM3o practica-curso-git (ED25519)
256 SHA256:4tR8mK2nQ7xVb3Lc9ZwYeF6dJ1sP5uA0eB7gN2kM3o practica-curso-git (ED25519)

Idénticas, y no es casualidad. La huella es un hash de la clave pública, y la pública se puede derivar de la privada (lo contrario no). Cuando ssh-keygen -lf recibe una clave privada, extrae la pública y calcula su huella. Por eso la huella sirve para emparejar sin ambigüedad qué privada de tu disco corresponde a qué pública subida al servidor: es exactamente la comprobación del apartado 10.

# 6. Limpieza
ssh-add -d ~/.ssh/practica_curso
rm ~/.ssh/practica_curso ~/.ssh/practica_curso.pub

Solución 2:

# Primero, las dos claves
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_trabajo -C "carla-trabajo-ejemplo"
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_otraempresa -C "carla-otraempresa"

1. El fichero ~/.ssh/config:

# Identidad del equipo de gestor-tareas
Host git.ejemplo.es
    HostName git.ejemplo.es
    User git
    IdentityFile ~/.ssh/id_ed25519_trabajo
    IdentitiesOnly yes
    AddKeysToAgent yes

# Identidad de la otra empresa (alias inventado)
Host git-otraempresa
    HostName git.otraempresa.es
    User git
    IdentityFile ~/.ssh/id_ed25519_otraempresa
    IdentitiesOnly yes
    AddKeysToAgent yes
chmod 600 ~/.ssh/config

2. Qué hace cada directiva:

  • Host: el nombre que Carla escribe en la URL. En el primer bloque coincide con el servidor real; en el segundo es un alias inventado que no existe en ningún DNS.
  • HostName: el servidor real al que hay que conectarse. Es lo que resuelve el alias.
  • User git: el usuario SSH. En las plataformas de repositorios es siempre git; el usuario real se deduce de la clave que presentas.
  • IdentityFile: qué clave privada usar con este destino.
  • IdentitiesOnly yes: usar solo esa clave, sin ofrecer las demás.
  • AddKeysToAgent yes: cargarla en el agente al primer uso, para no repetir la passphrase.

3. Las URL de clonado:

# Repositorio del equipo
git clone [email protected]:equipo/gestor-tareas.git

# Repositorio de la otra empresa: se usa el ALIAS
git clone git@git-otraempresa:proyectos/facturacion.git

4. Comprobar qué clave se ofrece:

ssh -T -v [email protected] 2>&1 | grep "Offering public key"
debug1: Offering public key: /home/carla/.ssh/id_ed25519_trabajo ED25519
ssh -T -v git@git-otraempresa 2>&1 | grep "Offering public key"
debug1: Offering public key: /home/carla/.ssh/id_ed25519_otraempresa ED25519

Cada destino recibe exactamente una clave, la suya.

5. Sin IdentitiesOnly yes: SSH ofrecería todas las claves cargadas en el agente, una por una, en un orden que no controlas. Con seis claves y un servidor cuyo MaxAuthTries está en 6 (el valor habitual), es muy probable que agote los intentos antes de llegar a la correcta:

Received disconnect from 192.0.2.42 port 22:2: Too many authentication failures

Un error especialmente traicionero, porque la clave correcta existe y está cargada: simplemente no le ha dado tiempo a probarla. Además, ofrecer todas tus claves a cualquier servidor con el que hables es una fuga de información innecesaria: le revelas cuántas identidades tienes.

Solución 3:

1. Permission denied (publickey)

  • (a) Autenticación. El servidor no ha aceptado ninguna clave: ni siquiera sabe quién eres.
  • (b) La clave no está cargada en el agente, o la que tienes en el disco no es la que está subida al servidor. Menos frecuente pero muy desconcertante: permisos demasiado abiertos en la privada, que hacen que OpenSSH la ignore en silencio.
  • (c) ssh-add -l para ver qué hay cargado y, si eso no aclara nada, ssh -vT [email protected] para ver qué clave se ofrece y cómo responde el servidor.

2. Permission to equipo/gestor-tareas.git denied to carla

  • (a) Autorización. El mensaje nombra a Carla, así que la autenticación ha funcionado perfectamente.
  • (b) Su cuenta no tiene permiso de escritura en ese repositorio, o —si va por HTTPS con token— el token carece del ámbito de escritura.
  • (c) Revisar los permisos de la cuenta en la configuración del repositorio y los ámbitos del token en la plataforma. No hay ningún comando de Git que arregle esto: es un cambio en el servidor.

3. Support for password authentication was removed

  • (a) Autenticación. Se está enviando un método que el servidor ya no admite.
  • (b) Se ha tecleado la contraseña de la cuenta en lugar de un token; o hay una credencial antigua guardada en el gestor, con la contraseña de antes de la migración.
  • (c) Generar un token con el ámbito necesario y, muy importante, borrar la credencial guardada antes de reintentar, o el gestor volverá a enviar la vieja:
# Eliminar la credencial almacenada para ese servidor
printf "protocol=https\nhost=git.ejemplo.es\n\n" | git credential reject

4. REMOTE HOST IDENTIFICATION HAS CHANGED!

  • (a) Autenticación, pero en el sentido inverso: es el servidor el que no consigue demostrar su identidad ante ti.
  • (b) El servidor se ha reinstalado o la plataforma ha rotado sus claves (lo normal); o alguien está interceptando la conexión (lo grave).
  • (c) Obtener la huella actual y compararla con la publicada oficialmente antes de tocar nada:
ssh-keyscan git.ejemplo.es 2>/dev/null | ssh-keygen -lf -

Solo si coincide con la oficial: ssh-keygen -R git.ejemplo.es y reconectar.

5. connect to host … port 22: Connection timed out

  • (a) Ninguna de las dos: es un problema de red. La conexión no llega a establecerse, así que no hay ni autenticación ni autorización.
  • (b) Un cortafuegos corporativo con el puerto 22 cerrado (lo más habitual con diferencia), o el servidor caído.
  • (c) Comprobar la conectividad y, si es el cortafuegos, cambiar de vía:
# ¿Llega algo al puerto 22?
nc -zv git.ejemplo.es 22

# Solución práctica: pasar a HTTPS
git remote set-url origin https://git.ejemplo.es/equipo/gestor-tareas.git

Algunas plataformas ofrecen además SSH por el puerto 443, que atraviesa esos cortafuegos:

Host git.ejemplo.es
    HostName ssh.git.ejemplo.es
    Port 443
    User git

Conclusión

Con esta lección el equipo puede por fin hablar con el servidor. Lo esencial:

  • Leer un repositorio público no requiere credenciales; escribir siempre sí, porque push modifica las referencias del servidor.
  • user.name y user.email no autentican nada: son metadatos del commit. La identidad de acceso vive en otra capa.
  • Autenticación (quién eres) y autorización (qué puedes hacer) son distintas. Permission denied (publickey) es lo primero; 403 Forbidden es lo segundo. Distinguirlas dirige el diagnóstico.
  • HTTPS se autentica con un token personal de acceso, que sustituyó a la contraseña por ser de ámbito limitado, revocable, caducable, auditable y compatible con doble factor. Concede siempre el ámbito mínimo y usa un token por máquina.
  • SSH se autentica con un par de claves: la privada nunca sale de tu disco, la pública se sube al servidor. Genera con ssh-keygen -t ed25519 -C "descripción", pon passphrase, sube el fichero .pub, prueba con ssh -T y usa ssh-agent para teclearla una sola vez.
  • ~/.ssh/config resuelve el caso de varias identidades, incluso dos cuentas en el mismo servidor, mediante alias inventados. IdentitiesOnly yes evita el Too many authentication failures.
  • HTTPS frente a SSH: HTTPS gana en cortafuegos corporativos y en granularidad de permisos; SSH gana en comodidad diaria, en no caducar y en manejar varias identidades. Cambiar de uno a otro es un git remote set-url.
  • Cada sistema tiene su gestor de credenciales: libsecret en Linux, osxkeychain en macOS y Git Credential Manager en Windows, que además abre el navegador y gestiona los tokens por ti: el camino de Carla.
  • credential.helper store guarda el token en texto plano en ~/.git-credentials. Evítalo en máquinas reales; si no hay llavero, cache con tiempo de espera es mejor opción.
  • Para lo demás —firma de commits, gestión de secretos, credenciales filtradas en el historial— la lección 08-05.

Lo que viene

Ana, Bruno y Carla ya pueden autenticarse. El canal está abierto en los dos sentidos y, por primera vez en el curso, los objetos pueden viajar de un .git/ a otro.

En la lección 04-04: Obteniendo y Extrayendo Cambios empezamos a usarlo en el sentido de la recepción, y atacamos de frente la confusión más extendida de Git: la diferencia entre git fetch y git pull. Veremos por qué fetch no toca jamás tu directorio de trabajo, qué es FETCH_HEAD, cómo inspeccionar lo que ha llegado antes de integrarlo, y los distintos modos de git pull. Y por fin Carla clona gestor-tareas y se pone al día con el trabajo de sus compañeros.

Dominando Git: De Principiante a Avanzado

Módulo 1: Introducción a Git

Módulo 2: Operaciones Básicas de Git

Módulo 3: Ramas y Fusión

Módulo 4: Trabajando con Repositorios Remotos

Módulo 5: Operaciones Avanzadas de Git

Módulo 6: Herramientas y Técnicas de Git

Módulo 7: Estrategias de Colaboración y Flujo de Trabajo

Módulo 8: Mejores Prácticas y Consejos de Git

Módulo 9: Solución de Problemas y Depuración

Módulo 10: Git en el Mundo Real

© Copyright 2026. Todos los derechos reservados