CPI の共用サーバーに SSH で入ろうとしたところ、鍵を指定しているのにパスワードを聞かれ続けて入れませんでした。コントロールパネルで鍵を発行し直しても同じです。
原因は、ダウンロードした秘密鍵ファイルの 改行コードが CRLF で、パーミッションが 644 だったことでした。ssh が鍵を読めずに無視し、パスワード認証に移っていました。
症状
$ ssh myserver
Enter passphrase for key '~/.ssh/server_key':
(user@xxxx.secure.ne.jp) Password:
(user@xxxx.secure.ne.jp) Password:
(user@xxxx.secure.ne.jp) Password:
Connection closed by xxx.xxx.xxx.xxx port xxxxx
鍵のパスフレーズのあとにパスワードを聞かれ、何を入れても通りません。
鍵ファイルを確認する
ssh-keygen -l で鍵を読ませると、2つのメッセージが出ました。
$ ssh-keygen -l -f ~/.ssh/server_key
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '~/.ssh/server_key' are too open.
This private key will be ignored.
~/.ssh/server_key is not a key file.
Permissions 0644 ... are too open:ほかのユーザーも読める状態なので、ssh が鍵を使いません。is not a key file:パーミッションを直しても、このメッセージは消えませんでした。
中身を調べると、改行コードが CRLF(Windows 形式)でした。
file ~/.ssh/server_key # PEM RSA private key
grep -c $'\r' ~/.ssh/server_key # 0 以外なら CRLF が混ざっている
PEM 形式の鍵は、行末に \r が付いていると OpenSSH が読めません。
直し方
元のファイルを残してから、CR を取り除いてパーミッションを 600 にします。
cd ~/.ssh
cp -p server_key server_key.crlf.bak
tr -d '\r' < server_key.crlf.bak > server_key
chmod 600 server_key server_key.crlf.bak
ssh-keygen -l -f server_key # 2048 SHA256:... (RSA) と出れば OK
これで鍵が読めるようになり、パスワードを聞かれずに入れるようになりました。
コントロールパネルで鍵を発行し直しても、ダウンロードしたファイルはまた CRLF になっていました。発行し直したときは、毎回この変換が必要です。
接続設定の例
~/.ssh/config に書いておくと、ssh myserver だけで入れます。ホスト名・ポート・ユーザー名は、コントロールパネルに表示されているものに置き換えてください(ポートは 22 ではありませんでした)。
Host myserver
HostName xxxx.secure.ne.jp
Port xxxxx
User xxxxxxxx
IdentityFile ~/.ssh/server_key
IdentitiesOnly yes
UseKeychain yes
AddKeysToAgent yes
ServerAliveInterval 60
UseKeychain は macOS の ssh だけの設定です。ほかの OS ではエラーになるので外してください。
ユーザー名が分からなかったとき
コントロールパネルが重く、SSH のユーザー名がすぐには確認できませんでした。手元にあった本番の設定ファイルの、ホームディレクトリのパス(/usr/home/<アカウント>)に出てくるアカウント名がユーザー名でした。FTP のアカウント名とは別でした。
遠回りしたこと
最初は、「鍵が古い RSA 形式(BEGIN RSA PRIVATE KEY)なので、SHA-1 署名(ssh-rsa)が最近の OpenSSH で無効になっているのでは」と考え、PubkeyAcceptedAlgorithms +ssh-rsa を足しました。しかし、ssh -v で接続先を見ると OpenSSH 9.1 で、新しい署名方式(rsa-sha2)に対応していたので、関係ありませんでした。
パスワードを聞かれるときは、サーバー側を疑う前に、まず手元の鍵ファイルが読めているかを ssh-keygen -l -f で確かめるのが近道です。
おまけ:入ってみて分かったサーバーの様子
- シェルは csh で、
md5やwhoamiも入っていない、かなり制限された環境でした。 phpコマンドは無く、/usr/local/bin/php-8.3のようにバージョン付きの名前で入っています。mysqldumpはあるので、DB のバックアップは SSH 越しに手元へ直接流せました。パスワードはサーバー上でwp-config.phpから読んで環境変数で渡すと、画面にもコマンドの引数にも出ません。
# サーバー上で実行するスクリプトを標準入力で渡す(サーバーにファイルを作らない)
ssh myserver /bin/sh -s -- html/cms/wp-config.php <DB名> < remote_dump.sh \
| gzip > backup-$(date +%Y%m%d).sql.gz
# remote_dump.sh(サーバー上で動く)
set -eu
conf=$1; db=$2
val() { sed -n "s/^[[:space:]]*define([[:space:]]*'$1'[[:space:]]*,[[:space:]]*'\([^']*\)'.*/\1/p" "$conf" | head -1; }
[ "$(val DB_NAME)" = "$db" ] || { echo "DB名が一致しません" >&2; exit 1; }
MYSQL_PWD=$(val DB_PASSWORD); export MYSQL_PWD
exec mysqldump -h "$(val DB_HOST)" -u "$(val DB_USER)" \
--single-transaction --quick --no-tablespaces --default-character-set=utf8mb4 "$db"