SSH anahtarıyla giriş yapamıyorsanız ve ssh bağlantısı ya da git push aşağıdaki satırla sona eriyorsa, sunucu yalnızca açık anahtarla kimlik doğrulamaya (public key authentication) izin veriyor ve istemcinin sunduğu anahtarların hiçbirini bu kullanıcı için kabul etmedi demektir:
Kod:
[email protected]: Permission denied (publickey).
Mesaj nedeni söylemez. Anahtar hiç gönderilmemiş ya da yanlış kullanıcı adıyla denenmiş olabilir; sshd, authorized_keys dosyasını izinleri yüzünden yok saymış ya da sshd_config kullanıcıyı daha anahtara bakmadan engellemiş olabilir. Bu rehberde sunucularımızda izlediğimiz sırayı anlatıyoruz: önce istemcinin ne gönderdiğini, sonra sunucunun neden reddettiğini görüyor, nedeni düzeltiyor ve anahtarla giriş çalıştıktan sonra parolalı girişi güvenle kapatıyoruz. Seçenek adlarını ve varsayılan değerleri güncel OpenSSH man sayfalarıyla (OpenSSH 10.6, Ekim 2026) karşılaştırdık; Debian/Ubuntu ile RHEL/AlmaLinux/Rocky arasındaki farkları ayrıca belirtiyoruz.
Mesaj tam olarak ne söylüyor?
Parantez içindeki liste, sunucunun o anda kabul ettiği kimlik doğrulama yöntemlerini gösterir. Yalnızca publickey yazıyorsa parolalı giriş kapalıdır; parola denemenin anlamı yoktur. Red Hat tabanlı sistemlerde sık görülen "Permission denied (publickey,gssapi-keyex,gssapi-with-mic)" biçimi, sunucuda Kerberos (GSSAPI) yöntemlerinin de açık olduğunu gösterir; tanı aynıdır. Pratikte üç olasılık vardır:- İstemci doğru anahtarı hiç sunmadı (yanlış dosya, boş ajan) ya da yanlış kullanıcıyla veya yanlış sunucuya bağlanıyor.
- Anahtar sunuldu, ama o kullanıcının authorized_keys dosyasında eşleşen satır yok ya da sunucu dosyayı okumayı reddetti (izinler, SELinux, AuthorizedKeysFile ayarı).
- Anahtar ve dosya doğru, ama kullanıcı ya da imza algoritması sunucu yapılandırması tarafından engelleniyor (AllowUsers, PermitRootLogin, eski ssh-rsa imzaları).
1. Adım: İstemcinin ne gönderdiğine bakın (ssh -v)
Ayrıntılı çıktıyla bağlanın; daha fazla ayrıntı gerekirse -vvv kullanın:
Bash:
ssh -v [email protected]
Çıktının kimlik doğrulama kısmı genellikle şöyledir (kısaltılmış; eski sürümlerde Will attempt key satırları biraz daha yukarıda görünür):
Kod:
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Will attempt key: /home/ali/.ssh/id_ed25519 ED25519 SHA256:K7sN/Ex2OkglGdKq/+K8ldwxF2iuxENKHJ0QnbdAZNg
debug1: Offering public key: /home/ali/.ssh/id_ed25519 ED25519 SHA256:K7sN/Ex2OkglGdKq/+K8ldwxF2iuxENKHJ0QnbdAZNg
debug1: Authentications that can continue: publickey
debug1: No more authentication methods to try.
[email protected]: Permission denied (publickey).
Satırları şöyle okuyun:
- Will attempt key: İstemcinin denemeyi planladığı anahtarlar. Beklediğiniz dosya listede yoksa sorun istemcidedir.
- Offering public key ve hemen ardından yine Authentications that can continue: Anahtar gönderildi, sunucu bu kullanıcı için kabul etmedi. Nedeni sunucu günlüğündedir.
- Server accepts key: Sunucu anahtarı authorized_keys dosyasında buldu. Hata bundan sonra geliyorsa sorun büyük olasılıkla imzalama aşamasındadır: ajan, parola ifadesi (passphrase), donanım anahtarı ya da algoritma. Root kullanıcısıyla bağlanıyorsanız PermitRootLogin ayarına da bakın.
- Trying private key: İstemci bu dosya için açık anahtar bulamadı (yanında .pub dosyası yok, ajanda da değil) ve özel anahtarı doğrudan okumayı deniyor. Varsayılan dosya adlarında bu satır, dosya hiç yokken de görünür; istemci başka bir mesaj vermeden sonraki anahtara geçer. Buna karşılık -i ya da IdentityFile ile belirttiğiniz dosya yoksa ssh bunu "no such identity" uyarısıyla bildirir.
- send_pubkey_test: no mutual signature algorithm veya Skipping ... key ... not supported by server: İmza algoritması uyuşmuyor; aşağıdaki ssh-rsa bölümüne bakın.
Bash:
ssh -G [email protected] | grep -iE '^(user|hostname|port|identityfile|identitiesonly) '
2. Adım: Sunucunun neden reddettiğine bakın
Sunucuya konsoldan ya da açık kalan başka bir oturumdan erişebiliyorsanız günlüğü izleyin ve istemciden yeniden deneyin:
Bash:
sudo journalctl -u ssh -f # Debian/Ubuntu (servis adı ssh)
sudo journalctl -u sshd -f # RHEL/AlmaLinux/Rocky (servis adı sshd)
sudo tail -f /var/log/auth.log # Debian/Ubuntu, rsyslog kuruluysa
sudo tail -f /var/log/secure # RHEL ailesi
Debian 12'den beri rsyslog varsayılan olarak kurulmadığından /var/log/auth.log olmayabilir; journalctl her durumda çalışır. OpenSSH 9.8'den itibaren bazı satırlar sshd-session, 10.0'dan itibaren de sshd-auth adıyla görünebilir; journalctl -u bunların hepsini kapsar. Aradığınız satırlar:
- Invalid user ubuntu from 203.0.113.10 port 51234: Böyle bir kullanıcı yok, kullanıcı adı yanlış.
- User deploy from 203.0.113.10 not allowed because not listed in AllowUsers (ya da DenyUsers, AllowGroups): sshd_config kullanıcıyı engelliyor.
- ROOT LOGIN REFUSED FROM 203.0.113.10 port 51234: Kimlik doğrulaması başarılı, ama PermitRootLogin root kullanıcısının bu yöntemle girmesine izin vermiyor.
- Authentication refused: bad ownership or modes for directory /home/deploy/.ssh: İzin ya da sahiplik sorunu (StrictModes).
- Could not open user 'deploy' authorized keys '/home/deploy/.ssh/authorized_keys': Permission denied: sshd dosyayı kullanıcının kimliğiyle okuyamıyor. Nedeni çoğu zaman sudo ile oluşturulmuş, root'a ait 600 izinli bir dosya ya da yanlış bir SELinux etiketidir.
- userauth_pubkey: signature algorithm ssh-rsa not in PubkeyAcceptedAlgorithms: İstemci SHA-1 kullanan eski ssh-rsa imzası gönderiyor (eski sürümlerde "key type ssh-rsa not in ..." biçimindedir).
- Connection closed by authenticating user deploy 203.0.113.10 port 51234 [preauth]: İstemci, anahtarları reddedilince vazgeçti. Varsayılan INFO düzeyinde sshd çoğu zaman nedeni yazmaz; en olası neden anahtarın authorized_keys dosyasında olmamasıdır.
Bash:
sudo ssh-keygen -lf /home/deploy/.ssh/authorized_keys # sunucuda: dosyadaki tüm anahtarlar
ssh-keygen -lf ~/.ssh/id_ed25519.pub # istemcide: sizin anahtarınız
VERBOSE düzeyi kalıcı olarak açık kalabilir. DEBUG düzeyleri ise man sayfasının da uyardığı gibi kullanıcıların gizliliğini ihlal eder; iş bitince INFO ya da VERBOSE düzeyine dönün. Denemeler sırasında çok sayıda başarısız giriş yaparsanız OpenSSH 9.8 ve sonrasındaki PerSourcePenalties özelliği IP adresinizi bir süre (varsayılan ayarlarla en fazla 10 dakika) reddedebilir; bunu yeni bir arıza sanmayın.
İstemci tarafındaki nedenler
Yanlış kullanıcı adı
Anahtar belirli bir kullanıcının authorized_keys dosyasına eklenir; başka bir kullanıcıyla bağlanırsanız eşleşme olmaz. Bulut imajlarında root ile SSH girişi genellikle kapalıdır ve anahtar varsayılan kullanıcıya yüklenir. AWS'de Ubuntu için ubuntu, Amazon Linux ve RHEL için ec2-user, Debian için admin kullanılır. cloud-init'in genel ayarlarında varsayılan kullanıcı çoğu zaman dağıtımın adıdır (almalinux, rocky, fedora gibi). Birçok VPS sağlayıcısı ise anahtarı doğrudan root'a ekler. Emin değilseniz imajın belgesine bakın. Root ile bağlandığınızda kimlik doğrulama başarılı olup bağlantı "Please login as the user "ubuntu" rather than the user "root"." mesajıyla kapanıyorsa root girişini cloud-init kapatmıştır; mesajdaki kullanıcıyla bağlanın.Yanlış anahtar ya da anahtarın hiç yüklenmemesi
İstemci varsayılan olarak ~/.ssh/id_ed25519, ~/.ssh/id_ecdsa, ~/.ssh/id_rsa gibi standart dosyaları ve ssh-agent'taki anahtarları dener. Anahtarınızın adı farklıysa (bulut panelinden indirilen anahtar.pem ya da proje_deploy gibi) ssh onu kendiliğinden bulmaz; -i ya da IdentityFile ile belirtmeniz gerekir.
Bash:
ssh-add -l # ajandaki anahtarların parmak izleri
ssh-add ~/.ssh/proje_deploy # anahtarı ajana ekle
ssh-keygen -y -f ~/.ssh/proje_deploy # özel anahtardan açık anahtarı üret, sunucudakiyle karşılaştır
"The agent has no identities." ajanın boş olduğunu, "Could not open a connection to your authentication agent." çalışan bir ajan bulunmadığını söyler; eval "$(ssh-agent -s)" ile başlatabilirsiniz. Cron, CI ya da sudo ile başlatılan işler farklı bir ortamda çalışır: oturumunuzdaki ajana erişemez, çoğu zaman da ~/.ssh dizininde başka anahtarlar bulunan farklı bir kullanıcı olarak çalıştırılır (örneğin sudo, root'un anahtarlarını kullanır). Elle denediğinizde çalışan bağlantı otomatik işte "Permission denied" veriyorsa neden çoğunlukla budur.
Too many authentication failures
Ajanda çok sayıda anahtar varsa istemci hepsini sırayla sunar. Sunucu reddedilen her anahtarı başarısız bir deneme sayar ve MaxAuthTries sınırına (varsayılan 6) ulaşınca bağlantıyı "Received disconnect from 203.0.113.10 port 22:2: Too many authentication failures" iletisiyle keser; doğru anahtarın sırası çoğu zaman hiç gelmez. Çözüm, o sunucu için yalnızca belirlediğiniz anahtarı kullanmaktır (IdentitiesOnly yes). OpenSSH 10.6 bunu sunucu tarafında yumuşattı: ilk altı anahtar sorgusu (PubkeyAuthOptions max-pk-ok) artık MaxAuthTries hakkından düşmüyor. Dağıtımların çoğu hâlâ daha eski sürümleri kullandığından istemci ayarı yine gereklidir.Her seferinde komut satırı seçeneği yazmak yerine ~/.ssh/config kullanmak hem işi kısaltır hem de bu hataların çoğunu önler:
Kod:
Host web1
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519_web
IdentitiesOnly yes
AddKeysToAgent yes
Host eski-switch
HostName 192.0.2.50
User admin
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
Artık ssh web1 yazmanız yeterli. ssh_config'de her seçenek için ilk bulunan değer geçerlidir (birden fazla IdentityFile satırı ise listeye eklenir); bu yüzden özel Host blokları dosyanın başına, genel Host * ayarları sona yazılır. Dosya başkalarınca yazılabilir durumdaysa ssh "Bad owner or permissions on /home/ali/.ssh/config" hatasıyla durur; chmod 600 ~/.ssh/config yeterlidir.
Özel anahtar izinleri, parola ifadesi ve PuTTY .ppk
Özel anahtar dosyasında grup ya da diğer kullanıcılar için herhangi bir izin varsa ssh "WARNING: UNPROTECTED PRIVATE KEY FILE!" ve "Permissions 0644 for '/home/ali/.ssh/id_ed25519' are too open." uyarısını verip anahtarı yok sayar; chmod 600 (ya da 400) yeterlidir. Windows'ta aynı denetim ACL üzerinden yapılır (Windows bölümüne bakın).PuTTY'nin .ppk dosyasını OpenSSH okuyamaz ve "Load key ...: invalid format" yazar. PuTTYgen'de anahtarı açıp Conversions > Export OpenSSH key ile dönüştürün ya da Linux'ta komut satırındaki puttygen aracını kullanın (Debian ve Ubuntu'da putty-tools paketinde):
Bash:
puttygen sunucu.ppk -O private-openssh -o ~/.ssh/id_sunucu
puttygen sunucu.ppk -O public-openssh -o ~/.ssh/id_sunucu.pub
chmod 600 ~/.ssh/id_sunucu
PuTTYgen'in "Save public key" düğmesiyle kaydedilen ve "---- BEGIN SSH2 PUBLIC KEY ----" ile başlayan dosya authorized_keys içinde çalışmaz. PuTTYgen penceresindeki "Public key for pasting into OpenSSH authorized_keys file" kutusundaki tek satırı kullanın ya da dosyayı ssh-keygen -i -f anahtar.pub ile çevirin. "Enter passphrase for key" istemi sunucu parolası değildir, anahtarı koruyan parola ifadesidir; yanlış girilirse anahtar hiç kullanılmaz.
Sunucu tarafındaki nedenler
authorized_keys içeriği
Dosyadaki her satır tek bir açık anahtardır: varsa seçenekler, ardından anahtar türü (ssh-ed25519, ecdsa-sha2-nistp256, ssh-rsa), base64 kodlu anahtar ve isteğe bağlı bir açıklama. Sık yapılan hatalar:- Anahtarın terminalden kopyalanırken birkaç satıra bölünmesi. Anahtar tek ve kesintisiz bir satır olmalıdır.
- Yeni anahtarın, son satırı satır sonuyla bitmeyen bir dosyaya eklenip önceki anahtara yapışması. ssh-copy-id bunu denetler; elle eklerken dosyayı açıp bakın.
- Özel anahtarın yapıştırılması. "BEGIN OPENSSH PRIVATE KEY" ile başlayan içerik sunucuya asla girmez; buraya yalnızca .pub dosyasının içeriği yazılır.
- Satır başındaki seçeneklerde yazım hatası: sshd hatalı seçenekli satırı atlar. from="198.51.100.0/24" gibi bir kısıt varsa başka bir adresten bağlandığınızda günlükte "Authentication tried for deploy with correct key but not from a permitted host" görürsünüz.
- Dosyanın Windows'ta UTF-16 olarak kaydedilmesi. Windows PowerShell 5.1'deki > yönlendirmesi UTF-16 yazar ve sshd bu dosyayı okuyamaz.
Sahiplik ve izinler: authorized_keys izinleri neden önemli?
StrictModes yes (varsayılan) iken sshd, authorized_keys dosyasını ve dosyadan ev dizinine kadar (ev dizini dahil) yoldaki her dizini denetler: hepsi kullanıcıya ya da root'a ait olmalı, grup ya da diğer kullanıcılar tarafından yazılabilir olmamalıdır. Aksi hâlde dosyayı yok sayar, çünkü başkasının yazabildiği bir authorized_keys dosyası, o kişinin kendi anahtarını eklemesine izin verir. Standart değerler: ev dizininde yazma izni yalnızca sahibinde, ~/.ssh için 700, authorized_keys için 600 ve hepsi kullanıcıya ait.
Bash:
# Kullanıcının kendisi olarak
chmod go-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
# root olarak başka bir kullanıcı (deploy) için
sudo chown -R deploy: /home/deploy/.ssh
sudo chmod go-w /home/deploy
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys
namei -l /home/deploy/.ssh/authorized_keys # yoldaki her bileşenin sahibi ve izni
İki yanlış "çözümden" uzak durun: chmod -R 777 ve StrictModes no. İlki dosyaları herkesin yazabileceği hâle getirir ve sshd onları yine reddeder; ikincisi reddi kaldırır ama güvenlik açığını yerinde bırakır. Ev dizini başka bir kullanıcıya aitse (örneğin bir dizini kopyalayıp chown yapmayı unuttuysanız) sorun yine "bad ownership" olarak görünür.
SELinux bağlamı (RHEL, AlmaLinux, Rocky)
İzinler doğru olduğu hâlde RHEL ailesinde anahtar reddediliyorsa SELinux etiketine bakın. ~/.ssh ve içindekiler ssh_home_t türünde olmalıdır; elle oluşturulan, mv ile taşınan ya da yedekten geri yüklenen dosyalar yanlış etiket taşıyabilir:
Bash:
ls -ldZ /home/deploy/.ssh /home/deploy/.ssh/authorized_keys
sudo ausearch -m AVC -ts recent # son SELinux retleri
sudo restorecon -Rv /home/deploy/.ssh
Ev dizinleri /home dışındaysa (örneğin /srv/home) restorecon doğru etiketi bilemez. Önce sudo semanage fcontext -a -e /home /srv/home ile eşdeğerlik tanımlayın, sonra sudo restorecon -Rv /srv/home çalıştırın. SELinux'u kapatmak çözüm değildir.
sshd_config, drop-in dosyaları ve Match blokları
Kontrol edilecek seçenekler: PubkeyAuthentication (varsayılan yes), AuthorizedKeysFile, AllowUsers/AllowGroups/DenyUsers/DenyGroups, PermitRootLogin (varsayılan prohibit-password: root parola ile giremez, anahtarla girebilir) ve Match blokları. Güncel Debian, Ubuntu ve RHEL 9 ailesinde sshd_config dosyasının başında Include /etc/ssh/sshd_config.d/*.conf satırı bulunur. sshd her seçenek için ilk okuduğu değeri kullandığından bu dizindeki dosyalar, ana dosyada daha aşağıda yazan satırları geçersiz kılar. Bulut imajlarındaki 50-cloud-init.conf ya da RHEL 9 ailesinin kurulumunda "Allow root SSH login with password" işaretlenince oluşan 01-permitrootlogin.conf bu yüzden sürpriz yaratır. Tahmin etmek yerine sshd'nin gerçekte kullandığı değeri sorun:
Bash:
sudo sshd -T -C user=deploy,host=laptop,addr=198.51.100.24 | grep -iE '^(pubkeyauthentication|authorizedkeysfile|permitrootlogin|allowusers|allowgroups|denyusers|strictmodes|pubkeyacceptedalgorithms) '
sudo grep -RniE '^\s*(PubkeyAuthentication|AuthorizedKeysFile|PermitRootLogin|AllowUsers|AllowGroups|Match)' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
sudo sshd -t && sudo systemctl reload ssh # Debian/Ubuntu
sudo sshd -t && sudo systemctl reload sshd # RHEL/AlmaLinux/Rocky
-C ile kullanıcı ve adres verildiğinde çıktı, Match bloklarının o bağlantıya uygulanmış hâlini gösterir. OpenSSH 10.4 ve sonrası seçenek adlarını büyük-küçük harf karışık (PubkeyAuthentication), eski sürümler yalnızca küçük harfle yazdırdığı için grep'te -i kullanın. sshd -t sözdizimini denetler; hata veriyorsa yeniden yüklemeyin. Yapılandırmayı değiştirirken mevcut oturumu kapatmayın ve değişikliği ikinci bir terminalden yeni bağlantıyla deneyin; açık oturumlar reload ile kopmaz.
Algoritma uyumsuzluğu: ssh-rsa ve "no mutual signature supported"
OpenSSH 8.8 (Eylül 2021), SHA-1 kullanan RSA imzalarını, yani ssh-rsa imza algoritmasını varsayılan olarak kapattı. Bu, RSA anahtarlarının kullanılamaz hâle geldiği anlamına gelmez: OpenSSH 7.2'den beri RSA anahtarları rsa-sha2-256 ve rsa-sha2-512 imzalarıyla kullanılabiliyor ve iki taraf da destekliyorsa bu otomatik olur. Sorun, taraflardan biri çok eski olduğunda çıkar: yıllardır güncellenmemiş sunucular, ağ cihazları, gömülü sistemler ya da eski SSH kütüphanesi kullanan araçlar. Belirtiler:- Yeni istemci, eski sunucu: -v çıktısında "send_pubkey_test: no mutual signature algorithm" ya da doğrudan "sign_and_send_pubkey: no mutual signature supported".
- Eski istemci, yeni sunucu: sunucu günlüğünde "userauth_pubkey: signature algorithm ssh-rsa not in PubkeyAcceptedAlgorithms".
- DSA (id_dsa) anahtarları: 2015'te varsayılan olarak kapatılan DSA desteği, OpenSSH 10.0 (Nisan 2025) ile tamamen kaldırıldı.
Bash:
ssh-keygen -t ed25519 -a 100 -C "ali@dizustu-2026"
# Ed25519 desteklemeyen eski bir sistem için: ssh-keygen -t rsa -b 4096 -C "ali@dizustu-2026"
-a, özel anahtar parola ifadesiyle şifrelenirken anahtar türetme işlevinin (KDF) kaç tur çalışacağını belirler; yüksek değer, çalınan bir anahtar dosyasının parola ifadesini kaba kuvvetle (brute force) bulmayı yavaşlatır. -C açıklamadır ve authorized_keys dosyasında anahtarın kime ait olduğunu anlamanızı sağlar. Mutlaka bir parola ifadesi belirleyin; ssh-agent sayesinde onu her bağlantıda yeniden yazmanız gerekmez. Donanım güvenlik anahtarınız varsa ed25519-sk (FIDO2, iki tarafta da OpenSSH 8.2 ve sonrası) daha güçlü bir seçenektir.
ssh-rsa'yı bütün sunucular için yeniden açmayın. Güncellenemeyen tek bir cihaz için yukarıdaki eski-switch bloğundaki gibi yalnızca o Host'a geçici bir istisna tanımlayın. Eski yapılandırmalarda aynı seçenek PubkeyAcceptedKeyTypes adıyla geçer; OpenSSH 8.5'ten beri bu ad takma ad olarak çalışmaya devam ediyor. RHEL 9 ailesinde sistem genelindeki kripto politikası (crypto policy) da SHA-1 imzalarını kısıtlar; update-crypto-policies --set DEFAULT:SHA1 bunu geri açar ama tüm sistemin güvenliğini düşürür. Eski tarafı güncellemek her zaman daha doğru çözümdür.
Anahtarı sunucuya doğru eklemek (ssh-copy-id ve paneller)
Parolayla ya da başka bir anahtarla hâlâ girebiliyorsanız en temiz yol ssh-copy-id'dir. Gerekirse ~/.ssh dizinini ve authorized_keys dosyasını kısıtlı izinlerle oluşturur, eksik satır sonunu tamamlar, zaten yüklü anahtarları atlar ve SELinux olan sistemlerde restorecon çalıştırır:
Bash:
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]
Konsoldan ya da root oturumundan elle eklemek için:
Bash:
sudo mkdir -p /home/deploy/.ssh
echo 'ssh-ed25519 AAAAC3Nza... ali@dizustu-2026' | sudo tee -a /home/deploy/.ssh/authorized_keys > /dev/null
sudo chown -R deploy: /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh && sudo chmod 600 /home/deploy/.ssh/authorized_keys
sudo restorecon -Rv /home/deploy/.ssh # yalnızca SELinux kullanan sistemlerde
Kontrol panelleri de aynı dosyayı yönetir. cPanel'de Security > SSH Access > Manage SSH Keys altında Import Key ile açık anahtarı ekleyin, ardından Manage > Authorize ile yetkilendirin; cPanel belgeleri, yeni bir anahtarın kullanılmadan önce yetkilendirilmesi gerektiğini belirtir. DirectAdmin'de bölüm genellikle Advanced Features > SSH Keys altındadır ve yalnızca hesapta SSH erişimi açıksa görünür. Menü adları dile, temaya ve sürüme göre değişebilir; barındırma firması kabuk (shell) erişimini kapattıysa anahtar eklemek de işe yaramaz. Özel anahtarınızı hiçbir panele yüklemeniz gerekmez.
Parolalı giriş kapalıyken anahtar çalışmıyorsa sunucuya SSH dışında bir yoldan girmeniz gerekir: bulut sağlayıcısının tarayıcı ya da seri konsolu, VPS panelindeki VNC konsolu veya kurtarma modu (rescue mode). Kurtarma modunda sistem diskini bağlayıp (mount) authorized_keys dosyasını ve izinleri düzeltebilirsiniz. SELinux kullanan sistemlerde bu yolla oluşturulan dosyalar etiketsiz kalabilir; yeniden başlatmadan önce bağladığınız kök dosya sisteminde boş bir .autorelabel dosyası oluşturun (örneğin touch /mnt/.autorelabel), etiketler bir sonraki açılışta yeniden atanır. Kurtarma adımlarını Linux'ta root şifresi nasıl değiştirilir: çalışan sistem ve kurtarma modu konusunda anlattık.
GitHub ve GitLab'da Permission denied (publickey)
[email protected]: Permission denied (publickey) hatası aynı mantıkla çözülür, birkaç farkla:- Bağlantıyı test edin: ssh -T [email protected]. Başarılıysa "Hi KULLANICI! You've successfully authenticated, but GitHub does not provide shell access." yazar; komutun 1 çıkış koduyla bitmesi normaldir. GitLab'da ssh -T [email protected], başarılıysa kullanıcı adınızı içeren bir "Welcome to GitLab" mesajı döndürür.
- GitHub.com'da kullanıcı her zaman git'tir; ssh [email protected] çalışmaz.
- Git komutlarını sudo ile çalıştırmayın: sudo git pull, sizin anahtarlarınızı değil root'unkileri kullanır.
- Anahtar doğru hesapta mı? ssh-add -l çıktısındaki parmak izini GitHub'da Settings > SSH and GPG keys listesiyle karşılaştırın. Belirli bir anahtarın nereye bağlı olduğunu ssh -T -o IdentitiesOnly=yes -i ~/.ssh/anahtar [email protected] gösterir; yanıtta kullanıcı adı yerine hesap/depo biçiminde bir ad görürseniz anahtar bir dağıtım anahtarıdır (deploy key).
- Deploy key ile kullanıcı anahtarını karıştırmayın. Deploy key tek bir depoya bağlıdır, varsayılan olarak salt okunurdur (eklerken yazma izni verilebilir) ve aynı anahtar ikinci bir depoya eklenemez ("Key already in use" hatası). Push sırasında alınan "Permission to ... denied to ..." iletisinin sonunda başka bir kullanıcı adı varsa anahtar, depoya erişimi olmayan bir hesaba; hesap/depo biçiminde bir ad varsa başka bir deponun deploy key'ine aittir.
- Uzak adres HTTPS ise (git remote -v çıktısında https://github.com/...) SSH anahtarı hiç kullanılmaz; git remote set-url origin [email protected]:hesap/depo.git ile SSH'ye geçin.
- GitHub, DSA anahtarlarını 15 Mart 2022'den beri kabul etmiyor; 2 Kasım 2021'den sonra eklenen RSA anahtarları yalnızca SHA-2 imzalarıyla çalışıyor. Eski bir istemcide RSA anahtarınız bu yüzden reddediliyorsa Ed25519 anahtarına geçin; istemci onu da desteklemiyorsa güncellenmesi gerekir.
Kod:
Host github-firma
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_firma
IdentitiesOnly yes
Depoyu git remote set-url origin git@github-firma:firma/proje.git ile bu takma ada bağlayın ve ssh -T git@github-firma ile test edin. Bulunduğunuz ağ 22 numaralı portu engelliyorsa GitHub'ın belgelediği ssh.github.com adresini 443 portuyla kullanın: önce ssh -T -p 443 [email protected] ile deneyin; çalışıyorsa ~/.ssh/config dosyasına Host github.com, Hostname ssh.github.com, Port 443 ve User git satırlarını ekleyin. Bu yol GitHub Enterprise Server'da ve veri yerleşimli GitHub Enterprise Cloud (GHE.com) hesaplarında desteklenmiyor. GitLab.com'daki karşılığı altssh.gitlab.com adresi ve 443 portudur.
Windows'ta OpenSSH
Windows 10 sürüm 1809 ve Windows Server 2019'dan beri sistemle gelen OpenSSH istemcisi Linux'takiyle aynı şekilde çalışır; anahtarlar C:\Users\KULLANICI\.ssh altında durur. Farklar:- ssh-agent hizmeti varsayılan olarak devre dışıdır; yönetici olarak açılmış bir PowerShell penceresinde etkinleştirip başlatın, anahtarı ise normal oturumdan ekleyin (aşağıdaki komutlar).
- Git for Windows kendi ssh.exe dosyasını kullanırsa Windows ajanını göremez ve parola ifadesini tekrar tekrar sorar; core.sshCommand ile sistem istemcisine yönlendirin.
- Özel anahtar izinleri chmod ile değil ACL ile denetlenir. Dosyayı başka bir bilgisayardan kopyaladıysanız devralınan izinleri kaldırıp yalnızca kendinize okuma izni verin.
- ssh-copy-id genellikle bulunmaz; Linux sunucuya anahtarı aşağıdaki boru (pipe) komutuyla ekleyebilirsiniz.
Kod:
Get-Service ssh-agent | Set-Service -StartupType Automatic # yönetici PowerShell
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"
icacls $env:USERPROFILE\.ssh\id_ed25519 /inheritance:r /grant:r "$($env:USERNAME):(R)"
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh [email protected] "umask 077; mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys"
Sunucu Windows Server ise dikkat: Administrators grubundaki hesaplar için anahtar kullanıcı profilinde değil, C:\ProgramData\ssh\administrators_authorized_keys dosyasında aranır ve bu dosyanın erişim listesinde (ACL) yalnızca Administrators ile SYSTEM bulunmalıdır. Microsoft'un verdiği komut:
Kod:
icacls.exe "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"
Türkçe Windows'ta grubun adı Yöneticiler olduğu için bu komut hata verebilir. Microsoft'un belgesi, grup adı yerine başına yıldız konmuş bir SID kullanılabileceğini gösteriyor:
Kod:
icacls.exe "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "*S-1-5-32-544:F" /grant "SYSTEM:F"
Windows sunucuda sshd günlükleri varsayılan olarak Olay Görüntüleyicisi'nde (Event Viewer) OpenSSH altındaki Operational ve Admin kanallarına yazılır.
Anahtarla giriş çalıştıktan sonra: parolalı girişi güvenle kapatmak
Anahtarla giriş çalıştığına göre parolalı girişi kapatabilirsiniz; bunu şu sırayla yapın:- Anahtarla girişi sudo yetkili kullanıcıyla ve yeni bir terminalden doğrulayın; mevcut oturumu açık tutun.
- Acil durum erişimini (break-glass) hazırlayın: bulut ya da VPS konsoluna girişi önceden deneyin (bulut imajlarında varsayılan kullanıcının çoğu zaman parolası yoktur; konsoldan girebilmek için önce bir parola belirleyin) ve ikinci bir yönetici anahtarını çevrim dışı saklayın.
- Ayarları, diğer dosyalardan önce okunacak bir drop-in dosyasına yazın.
Kod:
# /etc/ssh/sshd_config.d/10-anahtar-zorunlu.conf
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
KbdInteractiveAuthentication satırı önemlidir: UsePAM yes iken PAM, klavye etkileşimli yöntemle parola sormayı sürdürebilir. Sunucuya sudo yetkili bir kullanıcıyla giriyorsanız PermitRootLogin no daha da iyidir. Dosya adı 10 ile başladığından 50-cloud-init.conf gibi dosyalardan önce okunur ve geçerli olur; RHEL ailesinde 01-permitrootlogin.conf varsa o daha önce okunacağı için onu da düzenleyin. sshd_config dosyanızda Include satırı yoksa (eski sürümler) ayarları doğrudan sshd_config'e yazın. Ardından sudo sshd -t çalıştırın, sudo sshd -T | grep -iE '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin) ' ile etkin değerleri doğrulayın ve servisi yeniden yükleyin. Yeni bir terminalden ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password [email protected] denediğinizde "Permission denied (publickey)" (Red Hat ailesinde gssapi yöntemleriyle birlikte) görmeniz, bu kez istenen sonuçtur: parolalı giriş gerçekten kapanmıştır.
Bundan sonraki adımlar: OpenSSH 9.8 ve sonrasındaki PerSourcePenalties, başarısız deneme yapan adresleri zaten bir süre reddeder; bunu fail2ban ya da kontrol panelinizin kaba kuvvet (brute force) korumasıyla tamamlayın. Ayrılan çalışanların anahtarlarını kaldırmak için authorized_keys dosyalarını ssh-keygen -lf ile düzenli gözden geçirin ve her anahtarı -C açıklamasıyla bir kişiye bağlayın. AuthorizedKeysFile değerinde authorized_keys2 de geçiyorsa o dosyayı da kontrol edin: OpenSSH'nin derleme varsayılanı bu dosyayı da okur ve Debian'ın sshd_config dosyası bu varsayılanı değiştirmez. Daha geniş bir asgari güvenlik listesi için 2026'da sunucu sıkılaştırma: canlı Linux sunucular için asgari güvenlik konusuna bakabilirsiniz. Bu işi bir ekiple yürütmek isterseniz AIOR'un siber güvenlik hizmetleri arasında SSH güvenliği kurulumu ve Linux sunucu sertleştirme de bulunuyor.
Belirti tablosu
| Gördüğünüz | Olası neden | Ne yapmalı |
|---|---|---|
| -v çıktısında Offering public key var, Server accepts key yok | Anahtar o kullanıcının authorized_keys dosyasında değil ya da dosya okunmuyor | Kullanıcıyı ve parmak izini kontrol edin, sunucu günlüğüne bakın |
| Offering satırı yok, yalnızca Trying private key | İstemci beklediğiniz anahtarı bulamıyor | -i ya da IdentityFile, ssh-add |
| Too many authentication failures | Ajanda çok fazla anahtar | IdentitiesOnly yes ve tek IdentityFile |
| UNPROTECTED PRIVATE KEY FILE! | Özel anahtarı başkaları okuyabiliyor | chmod 600 (Windows'ta icacls) |
| Load key ...: invalid format | PuTTY .ppk ya da bozuk dosya | PuTTYgen ile OpenSSH biçimine çevirin |
| no mutual signature supported / algorithm | ssh-rsa (SHA-1) uyumsuzluğu | Ed25519 anahtar; gerekirse tek Host için istisna |
| Günlükte bad ownership or modes | Ev dizini, ~/.ssh ya da dosya izinleri | chmod go-w, 700, 600, chown |
| Günlükte Could not open user ... authorized keys | Dosya root'a ait ya da SELinux etiketi yanlış | chown -R KULLANICI:, restorecon -Rv |
| Günlükte not allowed because ... ya da ROOT LOGIN REFUSED | AllowUsers, DenyUsers, PermitRootLogin | sshd -T ile etkin değeri bulup düzeltin |
| Günlükte Invalid user | Yanlış kullanıcı adı | İmajın varsayılan kullanıcısıyla bağlanın |
| Please login as the user "ubuntu" rather than the user "root" | cloud-init root girişini kapatmış | Mesajdaki kullanıcıyla bağlanın |
Sık sorulan sorular
Anahtarı ekledim ama sunucu hâlâ parola soruyor. Neden?Parolalı giriş açıkken anahtar reddedilirse istemci sessizce parolaya geçer; yani bu da aynı hatadır. ssh -v ile anahtarın sunulup sunulmadığına, sunucu günlüğünde de neden reddedildiğine bakın. "Enter passphrase for key" istemini sunucu parolasıyla karıştırmayın; o, anahtarın kendi parola ifadesidir.
chmod 777 ya da StrictModes no sorunu çözer mi?
Hayır. Herkesin yazabildiği dizin ve dosyaları sshd zaten reddeder; StrictModes no ise başkasının authorized_keys dosyanıza anahtar eklemesine kapı açar. Doğru değerler 700 ve 600'dür.
Eski RSA anahtarımı silmem gerekiyor mu?
Gerekmez. RSA anahtarları güncel OpenSSH'de rsa-sha2-256/512 imzalarıyla çalışır; sorun yalnızca taraflardan biri SHA-1 dışında imza bilmediğinde çıkar. Yeni anahtarlar için Ed25519 seçin; RSA kullanacaksanız ssh-keygen'in varsayılanı olan 3072 bit ya da daha fazlası yeterlidir.
Aynı anahtarı birden fazla sunucuda kullanmak güvenli mi?
Açık anahtarın birçok sunucuda bulunması sorun değildir; risk özel anahtardadır. Kişisel anahtarınızı parola ifadesiyle koruyun, CI/CD ve otomasyon için ayrı ve yetkisi kısıtlı anahtarlar kullanın. Biri sızarsa yalnızca onu iptal edersiniz.
Sunucuya hiç erişemiyorum. Ne yapmalıyım?
Sağlayıcının web ya da seri konsolundan girin. Konsol yoksa kurtarma modunda sistem diskini bağlayın, authorized_keys dosyasını ve izinleri düzeltin (SELinux kullanan sistemlerde .autorelabel dosyasını unutmayın) ve normal açılışa dönün. Kurtarma modunda sshd_config dosyasına da dokunduysanız değişikliği iki kez kontrol edin; sözdizimi hatası varsa sshd hiç başlamaz.
Sonuç ve sonraki adımlar
"Permission denied (publickey)" tek bir arıza değil, birkaç farklı nedenin ortak çıktısıdır. Sırayı koruyun: ssh -v ile istemcinin hangi kullanıcıyla hangi anahtarı sunduğuna, sunucu günlüğüne ve sshd -T ile etkin yapılandırmaya bakın; izinleri 700/600 olarak düzeltin, gerekiyorsa SELinux etiketlerini geri yükleyin ve eski ssh-rsa ya da DSA anahtarlarını Ed25519 ile değiştirin. Anahtarla giriş çalıştıktan sonra parolalı girişi bir drop-in dosyasıyla kapatın, acil durum erişimini test edin ve anahtar listelerini düzenli olarak temizleyin.If SSH key authentication is not working and ssh or git push stops with the line below, the server only accepts public key authentication and none of the keys your client offered was accepted for that account:
Kod:
[email protected]: Permission denied (publickey).
The message never says why. The right key may not have been offered at all, it may have been offered for the wrong user, sshd may have ignored authorized_keys because of its permissions, or sshd_config may block the account before any key is checked. This guide follows the order we use on our own servers: see what the client sends, read why the server refused it, fix the cause, and only then switch off password logins safely. Option names and defaults were checked against the current OpenSSH manual pages (OpenSSH 10.6, October 2026), and we point out where Debian/Ubuntu and RHEL/AlmaLinux/Rocky differ.
What the error actually means
The list in parentheses is the set of authentication methods the server still accepts. If it says only publickey, password login is disabled and typing a password will not help. On Red Hat family servers you will often see "Permission denied (publickey,gssapi-keyex,gssapi-with-mic)"; that only means Kerberos (GSSAPI) methods are enabled as well, and the diagnosis is the same. In practice there are three possibilities:- The client never offered the right key (wrong file, empty agent), or it is logging in as the wrong user or to the wrong host.
- The key was offered, but the account's authorized_keys has no matching line, or sshd refused to read the file (permissions, SELinux, the AuthorizedKeysFile setting).
- Key and file are fine, but the server configuration blocks the user or the signature algorithm (AllowUsers, PermitRootLogin, legacy ssh-rsa signatures).
Step 1: see what the client offers (ssh -v)
Connect with verbose output, and add more v's (-vvv) if you need extra detail:
Bash:
ssh -v [email protected]
The authentication part of the output usually looks like this (trimmed; older releases print the Will attempt key lines a little earlier):
Kod:
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Will attempt key: /home/alex/.ssh/id_ed25519 ED25519 SHA256:K7sN/Ex2OkglGdKq/+K8ldwxF2iuxENKHJ0QnbdAZNg
debug1: Offering public key: /home/alex/.ssh/id_ed25519 ED25519 SHA256:K7sN/Ex2OkglGdKq/+K8ldwxF2iuxENKHJ0QnbdAZNg
debug1: Authentications that can continue: publickey
debug1: No more authentication methods to try.
[email protected]: Permission denied (publickey).
How to read it:
- Will attempt key: the keys the client plans to try. If the file you expect is missing here, the problem is on the client.
- Offering public key followed straight away by another Authentications that can continue: the key was sent and the server did not accept it for this user. The reason is in the server log.
- Server accepts key: the server found the key in authorized_keys. If the login still fails after this line, the problem is most likely the signing step: the agent, the passphrase, a hardware key or the algorithm. When logging in as root, check PermitRootLogin too.
- Trying private key: the client found no public key for this file (no .pub file next to it, nothing in the agent) and tries to load the private key itself. For the default file names this line also appears when the file does not exist at all; the client then moves on to the next key without another message. A file you named with -i or IdentityFile that does not exist is reported as "no such identity" instead.
- send_pubkey_test: no mutual signature algorithm or Skipping ... key ... not supported by server: the two sides share no usable signature algorithm; see the ssh-rsa section below.
Bash:
ssh -G [email protected] | grep -iE '^(user|hostname|port|identityfile|identitiesonly) '
Step 2: read why the server refused
If you can still reach the server through a console or another open session, follow the log while you retry from the client:
Bash:
sudo journalctl -u ssh -f # Debian/Ubuntu (service name ssh)
sudo journalctl -u sshd -f # RHEL/AlmaLinux/Rocky (service name sshd)
sudo tail -f /var/log/auth.log # Debian/Ubuntu with rsyslog installed
sudo tail -f /var/log/secure # RHEL family
Since Debian 12, rsyslog is not installed by default, so /var/log/auth.log may not exist; journalctl always works. From OpenSSH 9.8 some lines are tagged sshd-session, and from 10.0 some are tagged sshd-auth; journalctl -u catches all of them. The lines that matter:
- Invalid user ubuntu from 203.0.113.10 port 51234: the account does not exist, so the user name is wrong.
- User deploy from 203.0.113.10 not allowed because not listed in AllowUsers (or DenyUsers, AllowGroups): sshd_config is blocking the account.
- ROOT LOGIN REFUSED FROM 203.0.113.10 port 51234: authentication succeeded, but PermitRootLogin does not let root log in this way.
- Authentication refused: bad ownership or modes for directory /home/deploy/.ssh: a permission or ownership problem (StrictModes).
- Could not open user 'deploy' authorized keys '/home/deploy/.ssh/authorized_keys': Permission denied: sshd reads the file as the user and fails. The usual cause is a root-owned file with mode 600 created through sudo, or a wrong SELinux label.
- userauth_pubkey: signature algorithm ssh-rsa not in PubkeyAcceptedAlgorithms: the client is sending a legacy SHA-1 ssh-rsa signature (older servers word it "key type ssh-rsa not in ...").
- Connection closed by authenticating user deploy 203.0.113.10 port 51234 [preauth]: the client gave up after its keys were refused. At the default INFO level sshd usually logs no reason; the most likely one is that the key is not in authorized_keys.
Bash:
sudo ssh-keygen -lf /home/deploy/.ssh/authorized_keys # on the server: every key in the file
ssh-keygen -lf ~/.ssh/id_ed25519.pub # on the client: your key
VERBOSE is fine to leave on. The DEBUG levels, as the manual page warns, violate users' privacy, so go back to INFO or VERBOSE when you are done. One more trap while testing: on OpenSSH 9.8 and later, PerSourcePenalties may refuse new connections from your address for a while (10 minutes at most with the defaults) after repeated failures. Do not mistake that for a new fault.
Client-side causes
Wrong user name
A key is authorized for one account only; log in as anyone else and nothing matches. Cloud images usually disable SSH login as root and install your key for a default user instead. On AWS that is ubuntu for Ubuntu, ec2-user for Amazon Linux and RHEL, and admin for Debian. cloud-init's generic defaults usually name the user after the distribution (almalinux, rocky, fedora and so on), while many VPS providers put the key straight into root's account. When in doubt, check the image documentation. If authentication as root succeeds but the session closes with "Please login as the user "ubuntu" rather than the user "root".", cloud-init has disabled root; use the user it names.Wrong key, or the key is never loaded
By default the client tries standard files such as ~/.ssh/id_ed25519, ~/.ssh/id_ecdsa and ~/.ssh/id_rsa plus whatever ssh-agent holds. A key with any other name (a provider-issued key.pem, project_deploy) is never picked up on its own; point to it with -i or IdentityFile.
Bash:
ssh-add -l # fingerprints of the keys in the agent
ssh-add ~/.ssh/project_deploy # load a key into the agent
ssh-keygen -y -f ~/.ssh/project_deploy # derive the public key from the private key, compare with the server
"The agent has no identities." means the agent is empty; "Could not open a connection to your authentication agent." means none is running (start one with eval "$(ssh-agent -s)"). Jobs started from cron, CI pipelines or sudo run in a different environment: they cannot reach your session's agent, and they often run as a different user whose ~/.ssh holds different keys (sudo, for example, uses root's). When a command works by hand but fails with "Permission denied" inside automation, this is usually why.
Too many authentication failures
With many keys in the agent, the client offers them one after another. The server counts every rejected key as a failed attempt and, once MaxAuthTries (default 6) is reached, disconnects with "Received disconnect from 203.0.113.10 port 22:2: Too many authentication failures", often before the right key gets its turn. The fix is to tell the client to use only the key you configured for that host (IdentitiesOnly yes). OpenSSH 10.6 relaxed this on the server side: the first six key queries (PubkeyAuthOptions max-pk-ok) no longer count against MaxAuthTries. Most distributions still ship older releases, so the client-side setting remains the reliable fix.A ~/.ssh/config file saves typing and prevents most of these mistakes:
Kod:
Host web1
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519_web
IdentitiesOnly yes
AddKeysToAgent yes
Host legacy-switch
HostName 192.0.2.50
User admin
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
Now ssh web1 is enough. In ssh_config the first value found for each option wins (IdentityFile is different: every IdentityFile line adds another key to the list), so specific Host blocks go at the top of the file and general Host * defaults at the bottom. If the file is writable by other users, ssh stops with "Bad owner or permissions on /home/alex/.ssh/config"; chmod 600 ~/.ssh/config fixes it.
Private key permissions, passphrases and PuTTY .ppk files
If the private key file grants any permission to the group or to other users, ssh prints "WARNING: UNPROTECTED PRIVATE KEY FILE!" and "Permissions 0644 for '/home/alex/.ssh/id_ed25519' are too open." and ignores the key. chmod 600 (or 400) fixes it. On Windows the same check is done through ACLs (see the Windows section).OpenSSH cannot read PuTTY's .ppk files and reports "Load key ...: invalid format". Open the key in PuTTYgen and use Conversions > Export OpenSSH key, or use the puttygen command-line tool on Linux (in the putty-tools package on Debian and Ubuntu):
Bash:
puttygen server.ppk -O private-openssh -o ~/.ssh/id_server
puttygen server.ppk -O public-openssh -o ~/.ssh/id_server.pub
chmod 600 ~/.ssh/id_server
A related trap: the file saved with PuTTYgen's "Save public key" button starts with "---- BEGIN SSH2 PUBLIC KEY ----" and will not work in authorized_keys. Copy the single line from the "Public key for pasting into OpenSSH authorized_keys file" box instead, or convert the file with ssh-keygen -i -f key.pub. Finally, "Enter passphrase for key" asks for the passphrase that protects your private key, not the server password; get it wrong and the key is never used.
Server-side causes
What goes into authorized_keys
Each line holds exactly one public key: any options first, then the key type (ssh-ed25519, ecdsa-sha2-nistp256, ssh-rsa), the base64-encoded key and an optional comment. Common mistakes:- A key wrapped over several lines when copied from a terminal. It must be one unbroken line.
- A key appended to a file whose last line has no trailing newline, so it gets glued to the previous key. ssh-copy-id checks for this; when adding keys by hand, open the file and look.
- A private key pasted by mistake. Anything starting with "BEGIN OPENSSH PRIVATE KEY" never belongs on a server; only the contents of the .pub file go here.
- A typo in the options in front of the key, which makes sshd skip the line. If a line carries a restriction such as from="198.51.100.0/24", connecting from another address logs "Authentication tried for deploy with correct key but not from a permitted host".
- A file saved as UTF-16 on Windows. Windows PowerShell 5.1 writes UTF-16 with the > redirection, and sshd cannot read that.
Ownership and permissions: why authorized_keys permissions matter
With StrictModes yes (the default), sshd checks authorized_keys and every directory on the path up to and including the home directory: each must be owned by the user or by root, and none may be writable by the group or by others. Otherwise the file is ignored, because an authorized_keys that someone else can write lets that person add their own key. The standard values: home directory writable only by its owner, ~/.ssh at 700, authorized_keys at 600, all owned by the user.
Bash:
# As the user
chmod go-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
# As root, for another user (deploy)
sudo chown -R deploy: /home/deploy/.ssh
sudo chmod go-w /home/deploy
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys
namei -l /home/deploy/.ssh/authorized_keys # owner and mode of every path component
Two popular "fixes" to avoid: chmod -R 777 and StrictModes no. The first makes the files writable by everyone, so sshd refuses them anyway; the second silences the check and leaves the actual hole open. A home directory owned by another account (a copied directory without a chown, for example) shows up as the same "bad ownership" error.
SELinux contexts on RHEL, AlmaLinux and Rocky
If the permissions are right and a RHEL-family server still refuses the key, check the SELinux label. ~/.ssh and its contents should be of type ssh_home_t; files created by hand, moved with mv or restored from a backup can carry the wrong label:
Bash:
ls -ldZ /home/deploy/.ssh /home/deploy/.ssh/authorized_keys
sudo ausearch -m AVC -ts recent # recent SELinux denials
sudo restorecon -Rv /home/deploy/.ssh
If home directories live outside /home (say /srv/home), restorecon does not know the right label. Define an equivalence first with sudo semanage fcontext -a -e /home /srv/home, then run sudo restorecon -Rv /srv/home. Turning SELinux off is not the fix.
sshd_config, drop-in files and Match blocks
The options to check are PubkeyAuthentication (default yes), AuthorizedKeysFile, AllowUsers/AllowGroups/DenyUsers/DenyGroups, PermitRootLogin (default prohibit-password: root may log in with a key but not with a password) and any Match blocks. Current Debian, Ubuntu and RHEL 9 family systems have an Include /etc/ssh/sshd_config.d/*.conf line near the top of sshd_config. Because sshd uses the first value it reads for each option, files in that directory override the lines further down the main file. That is why 50-cloud-init.conf on cloud images, or the 01-permitrootlogin.conf that the RHEL 9 family installer writes when you tick "Allow root SSH login with password", so often cause surprises. Rather than guessing, ask sshd what it actually uses:
Bash:
sudo sshd -T -C user=deploy,host=laptop,addr=198.51.100.24 | grep -iE '^(pubkeyauthentication|authorizedkeysfile|permitrootlogin|allowusers|allowgroups|denyusers|strictmodes|pubkeyacceptedalgorithms) '
sudo grep -RniE '^\s*(PubkeyAuthentication|AuthorizedKeysFile|PermitRootLogin|AllowUsers|AllowGroups|Match)' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
sudo sshd -t && sudo systemctl reload ssh # Debian/Ubuntu
sudo sshd -t && sudo systemctl reload sshd # RHEL/AlmaLinux/Rocky
The user and address passed with -C make sshd apply the matching Match blocks before printing. OpenSSH 10.4 and later print option names in mixed case (PubkeyAuthentication), older releases in lower case only, hence grep -i. sshd -t checks the syntax; if it reports an error, do not reload. Keep your current session open while you change sshd and test from a second terminal with a fresh connection; a reload does not drop sessions that are already open.
Algorithm mismatch: ssh-rsa and "no mutual signature supported"
OpenSSH 8.8 (September 2021) disabled RSA signatures that use SHA-1, the ssh-rsa signature algorithm, by default. That does not make RSA keys useless: since OpenSSH 7.2, RSA keys can sign with rsa-sha2-256 and rsa-sha2-512, and this happens automatically when both sides support it. Trouble starts when one side is very old: servers nobody has updated in years, network gear, embedded systems, or tools built on an outdated SSH library. Symptoms:- New client, old server: "send_pubkey_test: no mutual signature algorithm" in the -v output, or "sign_and_send_pubkey: no mutual signature supported" printed directly.
- Old client, new server: "userauth_pubkey: signature algorithm ssh-rsa not in PubkeyAcceptedAlgorithms" in the server log.
- DSA keys (id_dsa): disabled by default in 2015 and removed completely in OpenSSH 10.0 (April 2025).
Bash:
ssh-keygen -t ed25519 -a 100 -C "alex@laptop-2026"
# Only for an old system without Ed25519 support: ssh-keygen -t rsa -b 4096 -C "alex@laptop-2026"
-a sets how many rounds the key derivation function (KDF) runs when your passphrase encrypts the private key; more rounds slow down anyone brute-forcing the passphrase of a stolen key file. -C is a comment that tells you whose key a line in authorized_keys is. Set a passphrase and let ssh-agent remember it. If you own a hardware security key, ed25519-sk (FIDO2, OpenSSH 8.2 or later on both ends) is stronger still.
Do not re-enable ssh-rsa for every host. For a single device that cannot be upgraded, add a temporary exception for that host only, like the legacy-switch block above. Older configurations use the previous name of the option, PubkeyAcceptedKeyTypes, which has worked as an alias since OpenSSH 8.5. On the RHEL 9 family the system-wide crypto policy restricts SHA-1 signatures as well; update-crypto-policies --set DEFAULT:SHA1 relaxes it, but it weakens the whole system. Upgrading the old side is always the better fix.
Adding a key the right way (ssh-copy-id and control panels)
If you can still log in with a password or another key, ssh-copy-id is the cleanest route. It creates ~/.ssh and authorized_keys with restrictive permissions when needed, adds a missing trailing newline, skips keys that are already installed and runs restorecon on SELinux systems:
Bash:
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]
To add a key by hand from a console or a root session:
Bash:
sudo mkdir -p /home/deploy/.ssh
echo 'ssh-ed25519 AAAAC3Nza... alex@laptop-2026' | sudo tee -a /home/deploy/.ssh/authorized_keys > /dev/null
sudo chown -R deploy: /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh && sudo chmod 600 /home/deploy/.ssh/authorized_keys
sudo restorecon -Rv /home/deploy/.ssh # SELinux systems only
Control panels manage the same file. In cPanel, open Security > SSH Access > Manage SSH Keys, use Import Key for the public key, then Manage > Authorize; cPanel's documentation states that a key must be authorized before it can be used. In DirectAdmin the section is usually under Advanced Features > SSH Keys and only appears when the account has SSH access. Menu names vary with language, theme and version, and if your host has disabled shell access, adding a key changes nothing. You never need to upload your private key to a panel.
If password login is off and the key does not work, you need a way in that does not depend on SSH: your cloud provider's browser or serial console, the VNC console in the VPS panel, or rescue mode. From rescue mode you can mount the system disk and fix authorized_keys and its permissions. On SELinux systems, files created this way can end up without a label, so before rebooting create an empty .autorelabel file at the root of the mounted file system (for example touch /mnt/.autorelabel); the labels are restored during the next boot. We walk through the rescue steps in How to Change the Root Password in Linux: Live System and Rescue Mode.
[email protected]: Permission denied (publickey) on GitHub and GitLab
The same logic applies to Git hosting, with a few specifics:- Test the connection with ssh -T [email protected]. Success looks like "Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access.", and the command exiting with code 1 is normal. For GitLab, ssh -T [email protected] answers with a "Welcome to GitLab" message that includes your user name.
- On GitHub.com the user is always git; ssh [email protected] will fail.
- Do not run Git with sudo: sudo git pull uses root's keys, not yours.
- Is the key on the right account? Compare the fingerprint from ssh-add -l with the list under Settings > SSH and GPG keys. To see where one particular key is attached, run ssh -T -o IdentitiesOnly=yes -i ~/.ssh/key [email protected]; if the greeting shows owner/repo instead of a user name, the key is a deploy key.
- Keep deploy keys and user keys apart. A deploy key belongs to a single repository, is read-only unless you grant write access when adding it, and the same key cannot be added to a second repository ("Key already in use"). If a push fails with "Permission to ... denied to ..." and the message ends in another user name, the key belongs to an account without access; if it ends in owner/repo, it is another repository's deploy key.
- If the remote is HTTPS (git remote -v shows https://github.com/...), SSH keys are not used at all. Switch with git remote set-url origin [email protected]:OWNER/REPO.git.
- GitHub has not accepted DSA keys since 15 March 2022, and RSA keys added after 2 November 2021 work only with SHA-2 signatures. If an old client's RSA key is refused for this reason, switch to an Ed25519 key; a client that cannot handle Ed25519 either needs an upgrade.
Kod:
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
Point the repository at the alias with git remote set-url origin git@github-work:company/project.git and test it with ssh -T git@github-work. If your network blocks port 22, GitHub documents port 443 on ssh.github.com: when ssh -T -p 443 [email protected] works, add a Host github.com block with Hostname ssh.github.com, Port 443 and User git to ~/.ssh/config. GitHub Enterprise Server and GitHub Enterprise Cloud with data residency (GHE.com) do not support this route. On GitLab.com the equivalent is altssh.gitlab.com on port 443.
OpenSSH on Windows
The OpenSSH client that has shipped with Windows since Windows 10 version 1809 and Windows Server 2019 behaves like the Linux one, with keys under C:\Users\USERNAME\.ssh. The differences:- The ssh-agent service is disabled by default. Enable and start it from an elevated PowerShell, then add your key from a normal session (commands below).
- Git for Windows may use its own bundled ssh.exe, which cannot talk to the Windows agent and keeps asking for your passphrase; point Git at the system client with core.sshCommand.
- Private key protection is checked through ACLs, not chmod. If you copied the key from another machine, remove inherited permissions and grant read access to yourself only.
- ssh-copy-id is usually not available; for a Linux server, the pipe command below does the same job.
Kod:
Get-Service ssh-agent | Set-Service -StartupType Automatic # elevated PowerShell
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"
icacls $env:USERPROFILE\.ssh\id_ed25519 /inheritance:r /grant:r "$($env:USERNAME):(R)"
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh [email protected] "umask 077; mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys"
When the server runs Windows Server, there is one more trap: for accounts in the Administrators group, sshd does not read the key from the user profile but from C:\ProgramData\ssh\administrators_authorized_keys, and that file's ACL must contain only Administrators and SYSTEM. Microsoft's documented command:
Kod:
icacls.exe "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"
On non-English editions the group name is localized (Yöneticiler on Turkish Windows) and the command above can fail. Microsoft's documentation shows the SID, prefixed with an asterisk, in place of the name:
Kod:
icacls.exe "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "*S-1-5-32-544:F" /grant "SYSTEM:F"
By default, sshd on Windows logs to Event Viewer, in the Operational and Admin channels under OpenSSH.
Once key login works: turning off passwords safely
With key login confirmed, you can turn off password login. Do it in this order:- Confirm key login with your sudo-capable user from a new terminal, and keep the existing session open.
- Prepare a break-glass path: try the cloud or VPS console beforehand (on cloud images the default user often has no password, so set one if you want to log in at the console) and keep a second admin key offline.
- Put the settings in a drop-in file that is read before the others.
Kod:
# /etc/ssh/sshd_config.d/10-key-only.conf
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
The KbdInteractiveAuthentication line matters: with UsePAM yes, PAM can keep asking for a password through keyboard-interactive authentication. If you log in through a sudo user, PermitRootLogin no is better still. A name starting with 10 is read before files such as 50-cloud-init.conf, so its values win; on the RHEL family a 01-permitrootlogin.conf is read even earlier, so edit that one too. If your sshd_config has no Include line (older releases), put the settings in sshd_config itself. Then run sudo sshd -t, confirm the effective values with sudo sshd -T | grep -iE '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin) ' and reload the service. From a new terminal, ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password [email protected] should now end in "Permission denied (publickey)" (with the gssapi methods added on Red Hat family servers). This time that is the result you want: password login really is off.
Next steps: on OpenSSH 9.8 and later, PerSourcePenalties already refuses addresses that keep failing for a while; add fail2ban or your control panel's brute-force protection on top. Review authorized_keys files regularly with ssh-keygen -lf to remove keys of people who have left, and tie every key to a person with its -C comment. If AuthorizedKeysFile still lists authorized_keys2, check that file too: OpenSSH's compiled-in default reads it, and Debian's sshd_config leaves that default in place. For a wider baseline, see Server hardening in 2026: minimum viable security for production Linux. If you would rather have a team handle it, AIOR's cyber security services include SSH security setup and Linux server hardening.
Symptom table
| What you see | Likely cause | What to do |
|---|---|---|
| -v shows Offering public key but never Server accepts key | Key not in that account's authorized_keys, or the file is not read | Check the user and the fingerprint, read the server log |
| No Offering lines, only Trying private key | The client cannot find the key you expect | -i or IdentityFile, ssh-add |
| Too many authentication failures | Too many keys in the agent | IdentitiesOnly yes with one IdentityFile |
| UNPROTECTED PRIVATE KEY FILE! | Private key readable by others | chmod 600 (icacls on Windows) |
| Load key ...: invalid format | PuTTY .ppk or a damaged file | Convert to OpenSSH format with PuTTYgen |
| no mutual signature supported / algorithm | ssh-rsa (SHA-1) mismatch | Ed25519 key; per-host exception only if unavoidable |
| Log: bad ownership or modes | Home, ~/.ssh or file permissions | chmod go-w, 700, 600, chown |
| Log: Could not open user ... authorized keys | Root-owned file or wrong SELinux label | chown -R USER:, restorecon -Rv |
| Log: not allowed because ... or ROOT LOGIN REFUSED | AllowUsers, DenyUsers, PermitRootLogin | Find the effective value with sshd -T and fix it |
| Log: Invalid user | Wrong user name | Use the image's default user |
| Please login as the user "ubuntu" rather than the user "root" | cloud-init disabled root login | Log in as the user it names |
FAQ
I added my key, but the server still asks for a password. Why?When password login is enabled, a rejected key makes the client fall back to the password prompt quietly, so this is the same failure in disguise. Use ssh -v to see whether the key was offered and the server log to see why it was refused. Do not confuse "Enter passphrase for key" with the server password: that prompt is for your key's own passphrase.
Will chmod 777 or StrictModes no fix it?
No. sshd refuses world-writable directories and files anyway, and StrictModes no lets anyone who can write to your authorized_keys add their own key. The right values are 700 and 600.
Do I have to throw away my old RSA key?
No. Current OpenSSH uses RSA keys with rsa-sha2-256/512 signatures; the problem only appears when one side knows nothing but SHA-1. Choose Ed25519 for new keys, and if you need RSA, ssh-keygen's default of 3072 bits or more is fine.
Is it safe to use the same key on several servers?
Having the public key on many servers is not the risk; the private key is. Protect your personal key with a passphrase and use separate, restricted keys for CI/CD and automation, so that a leak means revoking one key rather than all of them.
I am locked out completely. What now?
Use the provider's web or serial console. If there is none, boot into rescue mode, mount the system disk, fix authorized_keys and its permissions (on SELinux systems, add the .autorelabel file) and boot normally. If you also edited sshd_config from rescue mode, double-check the change: with a syntax error sshd will not start at all.