SSHの事情と理屈と設定

SSH関係の過去記事:

前後の事情、具体的な設定、理屈などが書いてない。箇条書きだけで系統的な説明がない。ここで改めて、系統的にまとめる。ssh-keygenは関係するが、ssh, sshdコマンドの設定と使い方は触れない。

[追記]sshコマンドについては SSHコマンドとconfig - (新) 檜山正幸のキマイラ飼育記 メモ編 に書いた。[/追記]

内容:

事情

「ConoHaのサーバーへのログインを、パスワードなしにしたい」という事情で、2022-08に設定をしたときのナニヤカニヤラが上記の3記事に書いてある。

この設定をするとき、(Powershellで)$env:HOME と $env:USERPROFILE が違うことが原因で、~/.ssh/ が2箇所に在るのが困った問題だった。

認証とは

https://e-words.jp/w/%E8%AA%8D%E8%A8%BC.html より:

認証とは、対象の正当性や真正性を確かめること。ITの分野では、相手が名乗った通りの本人であると何らかの手段により確かめる本人確認(相手認証)のことを単に認証ということが多い。データが改竄されていないか確かめたり(メッセージ認証など)、データの属性が真正であることを確認すること(時刻認証など)を指す場合もある。

本人確認のためのパスワード認証、暗証番号認証は認証〈オーセンティケーション〉である。今回の事情は、サーバーへのログインをパスワード認証からSSH公開鍵認証に切り替えた話。もともとがパスワード認証だったので、パスワード認証をベースに認証メカニズムを“ブートストラップ”したと言える。

  • パスワード認証は、What You Know 方式の認証: パスワードを知っている〈人間が記憶している〉
  • 公開鍵認証は、What You Have 方式の認証: 秘密キーを持っている〈デジタルデータとして保管されている〉

公開鍵認証もデジタルデータとは言いながら“ブツ”に基いているので、ブツを盗まれたらアウト。パスワード認証はブツがないが、紙にメモしたらブツになってしまうので危険。

生体認証などは、記憶でもブツでもないが、誤認・誤検知があり得る。一長一短。

  • 記憶〈Know〉 → 忘れるリスク/推測されるリスク/覗き見のリスク
  • ブツ〈Have〉 → 盗まれるリスク/紛失するリスク/壊れるリスク
  • 固有特性〈Are〉 → 誤認のリスク

認証〈authentication | authN〉と認可〈authorization | authZ〉は別だが、その話は割愛。

エージェント(登場人物)

エージェントとは、とあるスコープ内で個体性(一意識別可能性)と役割を持った主体のこと。今回の認証に関わるエージェントは以下。

手元PC                  ConoHaサーバ
 |                      |
ユーザー m-hiyama       ユーザー chimaira

マシン/システムもエージェント、生きている人間としての檜山は一人だが、「ユーザー m-hiyama」と「ユーザー chimaira」は別なエージェント

ソフトウェアのSSHエージェントとはまったく無関係

クレデンシャルとアカウント

エージェント(マシン/システム含む)ごとに、“本人確認”のための情報コンテンツ/情報リソースをクレデンシャルと呼ぶ。

https://e-words.jp/w/%E3%82%AF%E3%83%AC%E3%83%87%E3%83%B3%E3%82%B7%E3%83%A3%E3%83%AB.html より:

クレデンシャルとは、資格、経歴、認定証、信任状などの意味を持つ英単語。情報セキュリティの分野では、認証などに用いられるID、ユーザー名、暗証番号、パスワード、生体パターンなどの識別情報の総称を指す。

アカウントという概念は、識別子とクレデンシャルを含むと考えられる。例えば「ID+パスワード」なら、IDが識別子で、パスワードがクレデンシャル。

  • 識別子で「おまえは誰?」に応える。名乗りに使う。
  • クレデンシャルで「ほんとに本人か?」に応える。確認に使う。

アカウントは、抽象化された行動主体であるエージェントが、特定の行動環境(手元PCやConoHaサーバー)内での識別と認証を担保する情報コンテンツとなる。人間を代理するエージェントとアカウントは1:1対応するとしていいだろう。マシン/システムであるエージェントがアカウントを持つとは限らない。が、IPアドレス/ドメイン名+SSL証明書 は、サーバー(エージェント=行動主体)のインターネット(行動環境)におけるアカウントと言えそうだ。

秘密鍵と鍵ペア

SSH公開鍵方式認証のクレデンシャルは秘密鍵である。パスワードは数文字の文字列だが、秘密鍵は巨大な整数の64進法表記の文字列である。どちらも文字列情報コンテンツである。パスワードは人間の頭に記憶され、秘密鍵はコンピュータのファイルシステムに保存される。

秘密鍵(巨大な整数値) → 公開鍵 → フィンガープリント という一方向関数があり、秘密鍵さえ保持していれば、公開鍵とそのフィンガープリント(適当なハッシュアルゴリズムによるハッシュ値)は計算可能である。

  • 公開鍵の生成例: ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub
  • フィンガープリントの生成例: ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub 引数は秘密鍵ファイル名でも公開鍵ファイル名でもよい。結果は
    3072 SHA256:Ve4gWBKzSNQyz8CmxqAtxOueYdW6pbjmVzp7iqsXqvg no comment (RSA) のような形式。この例では54文字。80文字の2/3程度。

秘密鍵さえ在ればいいが、秘密鍵と公開鍵のペアを鍵ペアと呼び、一式として扱う。秘密鍵は機密として秘匿して、公開鍵(やそのフィンガープリント)は必要に応じて提示・公開する。

デジタルデータとしての鍵ペア

鍵ペアはデジタルデータとして保管されるので、ファイルシステムのファイルとなる。そのため次が問題となる。

  1. ファイルの置き場所〈ディレクトリ〉
  2. ファイルの名前、特に拡張子
  3. ファイルフォーマット
ファイルの置き場所

当該エージェントのSSHディレクトリが決まっている。

  • システム(クライアント側、サーバー側)のユーザーの場合: ~/.ssh/
  • サーバーシステムの場合: /etc/ssh/
  • 手元PCの場合: SSHサーバーアプリケーション(sshd)に依存

2022年夏の困った問題は、手元PCの ~/.ssh/ ディレクトリが一意に決まらなかったこと。実際は、$env:USERPROFILE のディレクトリがSSHディレクトリと認識されていた。

SSHディレクトリ内にある設定ファイル(~/.ssh/config, /etc/ssh/ssh_config, /etc/ssh/sshd_config)により、鍵ペア(実際には秘密鍵のみ必須)の置き場所/ファイル名は参照できる。その意味では、鍵ペアの置き場所は自由とも言える。

参考:

[chimaira]$ ls ~/.ssh/
authorized_keys
[chimaira]$
authorized_keys
[root]# ls -1 /etc/ssh/
moduli
ssh_config
ssh_config.d/
sshd_config
ssh_host_ecdsa_key
ssh_host_ecdsa_key.pub
ssh_host_ed25519_key
ssh_host_ed25519_key.pub
ssh_host_rsa_key
ssh_host_rsa_key.pub
[root]#
ファイルの名前

習慣に過ぎないが、次のネーミングコンベンションがあるようだ。

  • ☓☓☓を任意の名前として、
  • ☓☓☓_暗号化方式_key (拡張子なし)が秘密鍵
  • ☓☓☓_暗号化方式_key.pub が公開鍵
  • 「☓☓☓_暗号化方式」が同じで、末尾が 「_key」か「_key_pub」である2ファイルが鍵ペアを構成する。
  • しかし、「_key」がない場合もある。
  • 暗号化方式は、rsa, ecdsa, ed25519 など。
  • 暗号化方式自体の一般名称は「RSA」「DSA」「ECDSA」「EdDSA」など。
  • RSAではダメ、DSAやECDSAは非推奨。結局EdDSA(ed25519)一択のようだ。他の暗号化方式はフォールバックとして使用する。

注意: RSA(rsa)に比べると、ECDSA(ecdsa)、EdDSA(ed25519)の公開鍵は短い。以下のよう。このデータが known_hosts や authorized_keys に入る。行の先頭部分にメタデータとして暗号化方式名が入る。

[root]# cat ssh_host_rsa_key.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDFP99c6CNSa8ljkraOn+IP6bHPWApptp84rHEEJmmZwdOc5v0vq8iJfW02UCTSUoe0QRARbTMf5cOX8Nvg9noWhaMLfwxYUt2pAYwbmgBZSwg8ODWY9IS+RTyFI7DRMIjeL+ohMma+QFKXLmIwbCgDHIny1G5L/3HWDRjhfBop1qGpYMUW3P6qHxfV9P3dcLhb1zG7OVUKBjg0sGkWwzVz0cQa7UV7fZ4qrDe0wN2gvk2Aw+q/avFPvJFSlpBn9QlYRSFvvUp1fbJTmmF/usJnLvY/vPlwqgGNJ96GrVoYIfajRG2OUsegGbYKx8/ijH2ojraPNvlXo7R64Glg7JxbYuHJsWo+6pK3p5v8tzR/P4x1KFmaCXwkuJFfkOPz0TPdRuBE31479g8ePVwdwJsFkFB4cBflwI+QN84dlBTfd3zoc68mGH+pD3GXSjNcyoPh1Ly9PdNOTQQKWSEtafoANye2JODgyJgyIaM9YOQbRQYlqpe29tcN5gLrIZzQ0Y8=

[root@chimaira ssh]# cat ssh_host_ecdsa_key.pub
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEW6gLaBxUNqzDwh6osDXSd4YJ4xyiR7ZiPd1vxpa1ReWIRG0FyYgB9TGZJi2Kh/NpgfVxk0B3eH6Ohe90PMais=

[root]# cat ssh_host_ed25519_key.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJqkVApuePWIyaD+XGJy9qb2nEu1Yvw6AXuaUgFDewJy

[root]#

昔使っていた PuTTY〈パティ〉 では、PuTTYgenコマンドで鍵ペアを生成して、

  • ☓☓☓を任意の名前として、
  • ☓☓☓.key が秘密鍵
  • ☓☓☓.ppk が公開鍵(ppk は PuTTY Public Key)

もはや PuTTY を使うことはないだろう。

ところで、private も public の p ではじまるので、一文字に省略では要注意。ppk が PuTTY Public Key か PuTTY Private Key かの区別は付かない。なんか事故になりそうで怖い。

ファイルフォーマット

秘密鍵はPEM〈Privacy Enhanced Mail〉フォーマットのようだ。詳細はともかく次の形。ヘッダ部は省略可能、いきなりボディのデータを書いてもよい。

-----BEGIN ☓☓☓ PRIVETE KEY-----
〈ヘッダ部〉

〈ボディ部〉
-----END BEGIN ☓☓☓ PRIVETE KEY-----

ボディ部は、Base64エンコードされたバイナリデータ(巨大整数の表現)。

公開キーはいくつかのフォーマットがあるみたいだが、だいたい、データ本体はBase64データで、前後に少量のメタデータを置いた一行形式。

セットアップあるいはイニシエーションあるいはブートストラップ

ユーザー(人間)は、サーバーに対してパスワード認証のアカウント(いわゆる「ID+パスワード」)を持っているとする。このパスワード認証アカウントを土台にして、SSH公開鍵認証アカウントにブートストラップする。ブートストラップが完了すると、SSH公開鍵認証アカウントが使用可能となり、引き続きパスワード認証アカウントも使える。

必要とする情報リソース
  1. クライアント側エージェント(人間の代理物)としての鍵ペア: 手元PCのファイルシステム内のファイル達
  2. サーバー(マシン/システム)の鍵ペア: システム管理者が設定済みとする。
  3. サーバー側エージェント(サーバーのアカウント)としての許可情報: 公開鍵の一覧 ~/.ssh/authorized_keys

前提として:

  • クライアント側エージェントの秘密鍵は秘匿され続ける。
  • クライアント側エージェントの公開鍵はインターネットを流れる。
  • サーバー(マシン/システム)の秘密鍵は秘匿され続ける。
  • サーバー(マシン/システム)の公開鍵はインターネットを流れる。
  • サーバー(マシン/システム)のフィンガープリントはインターネットを流れる。
手順
  1. クライアント側エージェントの鍵ペアを生成する。☓☓☓_暗号化方式_key と ☓☓☓_暗号化方式_key.pub の2つのファイルが生成される(とする)。
  2. クライアント側 ~/.ssh/config に、ホストエントリーを追加する。ひとつのホストエントリーが、当該ホストへの接続確立のための情報を提供する。ドキュメンテーションの意味でも、configエントリーに明示的に書くのが望ましい。
  3. 公開キーファイルの内容を、サーバー側アカウントの ~/.ssh/authorized_keys ファイルに追加する。これにより、公開キーで識別されるクライアント側エージェントのアクセスが許可される。ファイル名は、authenticated_keys のはずだが致し方ない。
  4. ローカルにある(秘匿情報ではない)公開キーファイル内容を、サーバー側に送り込むときに認証が必要になる。「認証のセットアップのために認証が必要」という循環状況になるが、ここではパスワード認証を使う。
  5. 実際に使った方法は、cat ~\.ssh\id_rsa.pub | ssh User-Name@daphnia.org sh -c "cat - >> ~/.ssh/authorized_keys" 、キーボードからのパスワード入力が必要。
  6. インターネットサービスの場合は、ブラウザから使えるUIが準備されているだろう。
  7. ssh で当該ホストに接続する。ssh 〈User-Name〉@〈Host-Name〉 、ここで、Host-Name はconfigのホストエントリーのタイトルとして指定された名前。ホストエントリーのHostNameフィールド値ではない!
  8. はじめての接続の場合、リモートホストが公開鍵のフィンガープリント(ハッシュ値)を送ってくる。
  9. ユーザー(人間)が、そのフィンガープリントが目的のリモートホストの公開鍵のフィンガープリントであることを、何らかの方法で確認する。
  10. 確認が済んだら、そのリモートホストを信用する。

実際には、フィンガープリントによる確認が難しい。インターネットサービスでは公開情報がある。githubでは次のページで公開されている。暗号化方式とハッシュ値計算方式ごとにフィンガープリントができる。

GitHub の公開鍵のフィンガープリントは次のとおりです。

SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8 (RSA)
SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM (ECDSA)
SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU (Ed25519)

bitbucketは:

Bitbucket のサーバーの公開キーのフィンガープリントは、以下のとおりです。

SHA256 形式

2048 SHA256:zzXQOXSRBEiUtuE8AikJYKwbHaxvSc0ojez9YXaGp1A bitbucket.org (RSA)
1024 SHA256:RezPkAnH1sowiJM0NQXH90IohWdzHc3fAisEp7L3O3o bitbucket.org (DSA)

MD5 形式

97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40 (RSA)
35:ee:d7:b8:ef:d7:79:e2:c6:43:9e:ab:40:6f:50:74 (DSA)

SSHホストのフィンガープリントの取得には、ssh-keyscan コマンドが使える。

known_hosts

クライアント側の ~/.ssh/known_hosts には、信頼しているホストのホスト鍵(接続先ホストの公開鍵)が1行に1ホストずつ書かれている。1行の形式は鍵ペアの公開鍵と同じフォーマットである。

初回接続時に(成功すれば)known_hostsの行〈エントリー〉が追加される。削除するには、

  • ssh-keygen -R ホスト名またはIPアドレス

まとめ

クライアント側情報リソース:

  • 鍵ペアの秘密鍵 ~/.ssh/id_ed25519 (一例)
  • 鍵ペアの公開鍵 ~/.ssh/id_ed25519.pub (一例)
  • 信頼しているホスト達のホスト鍵 ~/.ssh/known_hosts
  • 設定ファイル ~/.ssh/config

サーバー側情報リソース:

  • ホストの秘密鍵 /etc/ssh/ssh_host_ed25519_key (一例)
  • ホストの公開鍵 /etc/ssh/ssh_host_ed25519_key.pub (一例)
  • 接続を受け入れるエージェント達の公開鍵 ~/.ssh/authorized_keys

コマンド:

  • 鍵ペアの生成 ssh-keygen -t ed25519 -f host-or-service (対話的、暗号化方式に ed25519を指定、-f はファイル名、パスフレーズは無しでいい)
  • 生成時に指定したファイル名は、鍵ペアの名前、あるいは鍵のタイトルとして使われる。が、名前に過剰な情報を込めないこと。推測の可能性が高くなる。
  • 公開鍵の生成 ssh-keygen -yf 〈秘密鍵ファイル〉 > 〈公開鍵ファイル〉
  • フィンガープリント表示 ssh-keygen -lf 〈公開鍵ファイルまたは秘密鍵ファイル〉
  • known_hostsエントリー削除 ssh-keygen -R 〈ホスト名またはIPアドレス〉
  • フィンガープリントの取得 ssh-keyscan 〈ホスト名〉

注意: 暗号化方式のデフォルトは rsa。dsa, ecdsa, ed25519 から指定できるから ed25519 が推奨。-b オプションは rsa のビット数指定だが、rsa 使わなければ関係ない。秘密鍵の盗難・漏洩時にはパスフレーズがあれば安全性が増すが、パスフレーズは入れないほうがトラブルを防げる。

注意: 鍵のタイトルとホスト/サービスのタイトルは概念的には違う対象物の名前だが、同名にしたほうがむしろ管理が楽かも。ホスト/サービスのタイトルは、configエントリーのタイトルに使う。ただし、configエントリーのタイトルとHostNameフィールド値は違う概念。指定にどちらを使うかは要注意。名前が何を指し、どこでどう使うかはいつも悩みと混乱のタネ。

檜山の特殊事情

~/.ssh/ が二箇所ある。

  • C:\Users\m-hiyama\.ssh
  • C:\Users\m-hiyama\Work\.ssh

現在の標準のSSH関連コマンドは C:\Users\m-hiyama\.ssh を見る。かつてのツールや、ディレクトリのデフォルトが変更可能なツールは C:\Users\m-hiyama\Work\.ssh を見る。現状は、C:\Users\m-hiyama\.ssh を中心に考えている。

参考

bitbucketにおけるSSHのセットアップ〈イニシエーション | ブートストラップ〉は、次が詳しい。

プログラム/サービスがエージェントとなってアクセスする場合のセットアップは:

追記:GitのリモートURL

httpsプロトコルの場合は、リモートgitリポジトリへのアクセスURLは次のようになる。この場合パスワード認証になる。

https://m_hiyama@bitbucket.org/m_hiyama/hogehoge.git

ここで、先頭の m_hiyama はユーザー名、後ろの m_hiyama/ はパスの一部。sshプロトコルの場合は:

ssh://m_hiyamar@bitbucket.org/m_hiyama/hogehoge.git

プロトコル名以外はhttpsプロトコルと同じフォーマットになる。しかし、SCP構文だと:

// ↓これは違う
m_hiyamat@bitbucket.org:m_hiyama/hogehoge.git

いや、違った。SSHの別なURLは:

git@bitbucket.org:m_hiyama/hogehoge.git

クローンする場合なら:

git clone https://m_hiyama@bitbucket.org/m_hiyama/hogehoge.git
git clone git@bitbucket.org:m_hiyama/hogehoge.git

SCP形式のような方言・略記は常に混乱のもとになる。統一的な構文を持つ ssh: プロトコルを使うのが吉。