SSH CA認証まとめ
SSHでの接続を、CA認証でやってみる。
備忘録として残しておくの。。。
概要
Open-SSH-5.4より、証明書を利用してログイン認証を行う、SSH CA認証が実装されました。
従来、サーバへのログインでは、公開鍵認証が一般的な認証方法ではありましたが、このCA認証の処理を追ってみて、どんないいことがあるのかとかに関してまとめておくのです。
SSH認証ではユーザ認証、サーバ認証とがあります。ユーザ認証では接続先サーバが接続元ユーザの認証を行い、サーバ認証では逆にユーザが接続先サーバが正しいかの認証を行います。
ここでは主にユーザ認証に焦点を絞って見ていくのです。(サーバ認証では、ユーザ認証でのユーザとサーバの立場を逆にして読み替える感じになります。また時間があったらまとめたい。)
SSHって?
おさらいとして、そもそものSSH(Secure Shell)ですが、2者間での通信時、やりとりされるデータを暗号化して認証を行うプロトコルとなります。
商用SSHからライセンス絡みのしがらみを取り除いき派生したOpenSSHが一般的によく利用されていますが、このOpenSSHではSSH1, SSH2の2種類のプロトコルが定義されています。各プロトコルによって暗号化方法は異なり、互換性はありません。
つまり、接続元、接続先ともに同じプロトコルでやり取りしあう必要があります。
SSH1(プロトコル1)
ここで登場する暗号鍵として、公開鍵暗号と共通鍵暗号の2つがあります。それぞれの特徴は以下のとおり。
共通鍵暗号方式 | ・生成されるキーペアは両方同じもの ・共通鍵で暗号化したものは、ペアの共通鍵でしか復号できない ・どちらの鍵も基本的に通信者間以外に晒してはダメなもの ・処理コストが低い |
公開鍵暗号方式 | ・生成されるキーペアは公開鍵と秘密鍵で、この2鍵は別物 ・公開鍵、または秘密鍵で暗号化したものはペア対の鍵でしか復号できない ・基本的に公開鍵は晒しても良いもの、秘密鍵は晒してはダメなもの ・処理コストが大きい |
処理の容易さから、なるべく共通鍵でデータの暗号化をしたいわけですが、そのためには共通鍵を2者間で共有する必要があり、その共有の際にネットワーク上に共通鍵が晒されてしまいます。
それはまずいので、共通鍵暗号化転送するために公開鍵暗号方式を利用し、公開鍵で共通鍵を暗号化してもらってから転送し、秘密鍵で復号します。
ということで、まとめると、SSHは公開鍵暗号方式、共通鍵暗号方式それぞれのメリットを活かしたプロトコルとなります。
SSL通信とかでもこれと似たような感じで暗号化通信を実現しています。
SSH認証は、このSSHでの認証となるので、以降で説明するデータのやり取りは基本的に暗号化されています。
また、公開鍵認証、CA認証でも、公開鍵暗号方式が用いられています。
SSH2(プロトコル2)
SSH1では共通鍵の2者間でのシェアのために公開鍵暗号を利用しましたが、SSH2ではDiffie-Hellman方式によって2者間での共通鍵シェアを実現します。
詳細はDiffie-Hellman鍵共有とかを見れば雰囲気わかるかと思いますので割愛しますが、接続元、接続先各々で公開鍵ペア(公開鍵+秘密鍵)を生成し、
互いに公開鍵を交換し、自身の秘密鍵と交換した公開鍵から、2者間で共通の共通鍵を生成します。
この共通鍵を用いて暗号化通信を実現します。
SSH1よりもより安全性の高いアルゴリズムをサポートしていたりと、現在ではSSH2の利用が推奨されています。
公開鍵認証 VS CA認証
公開鍵認証とCA認証について、比較してみてみます。
公開鍵認証
まず公開鍵認証ですが、処理フローとしては以下のようになります。
接続元のユーザは公開鍵暗号の鍵ペア(公開鍵+秘密鍵)を生成し、公開鍵を接続先サーバへ事前に配布しておきます。
その後、ユーザのログイン要求時、接続先サーバは、事前にユーザからもらっていた公開鍵で乱数を暗号化し、それをユーザへ返します。
ユーザは秘密鍵で復号し、サーバの生成した乱数を取得し、ハッシュ化してサーバへ返送し、サーバ側で検証されます。
暗号化した乱数を復号できるのは秘密鍵を持った接続元のユーザのみなので、それによって接続元ユーザの正しさが保証されます。
以上が公開鍵認証の流れとなります。
まとめると、ユーザは秘密鍵を、サーバはその秘密鍵に対応する公開鍵をそれぞれ認証承認のために必要とします。シンプルでなのです。
CA認証
次に本題の、CA認証の処理フローを見てみます。流れは以下の図のようになります。
公開鍵認証と違い、新たにCA(認証局)が登場します。CAは信頼される存在であり、サーバはこのCAを介して接続元ユーザが正しくアクセス権のある存在であることを検証します。
事前の準備として、公開鍵認証同様、接続元ユーザ側で公開鍵暗号の鍵ペア(ユーザ公開鍵+ユーザ秘密鍵)を生成します。また、CAでも、署名用に公開鍵暗号の鍵ペア(CA公開鍵+CA秘密鍵)を生成しておきます。
まず、ユーザは自身のユーザ公開鍵をCAに署名してもらいます。CAでは、このユーザ公開鍵を、CA秘密鍵を用いて署名します。ここでの署名ではオプションが指定でき、有効期限やら接続元IP、ユーザの制限やら色々設定できます。詳細は次のセクションで説明します。
んで、CAは署名に用いた秘密鍵の対のCA公開鍵を、接続先のサーバへ配布、登録しておきます。
そして、ユーザは接続先サーバへ接続時、CAに署名してもらった公開鍵(証明書)をまず渡します。
接続先サーバでは、CA公開鍵を用いてその署名済みユーザ公開鍵を検証します。(CA公開鍵で復号できるか+署名内容的にアクセス可能か)
更に、公開鍵認証同様、ユーザが、「署名済み公開鍵に対応するユーザ秘密鍵」をちゃんと所持しているかも検証されます。
以上が処理の流れの概要となります。。。
まとめると、ユーザはユーザ秘密鍵と署名済みユーザ公開鍵(証明書)を、サーバはCA公開鍵をそれぞれ認証承認のために必要とします。
署名時のオプション
ここでは、CAでのユーザ公開鍵の署名時のオプションについて具体的に見てみます。署名は、下記コマンドによって実行されます。
$ ssh-keygen -s <CA秘密鍵> -I <Specify> -n <principals> -z <シリアル番号> -V <有効期限> -O <オプション> <ユーザ公開鍵>
-s | ・署名に用いるCA秘密鍵を指定する |
-I | ・公開鍵に署名する際のSpecify(詳細)を指定する ・証明書を使った認証時、サーバ側のログに記録される。 |
-n(-Z) | ・principalsには接続元のユーザ名、またはホスト名、任意の識別子が入る ・ここで指定された識別子でのみ証明書は有効となる ・カンマ区切りで複数指定可能 |
-z <シリアル番号> | ・証明書に埋めこむシリアル番号を指定する ・これは、同一の CA から発行された複数の証明書を区別するのに使用。 ・デフォルト0 |
-V <有効期限> | ・証明書の有効期限を指定する ・指定しない場合、無期限に有効となる |
-O <オプション> | ・接続元IPの制限やForceCommandなど、その他細かい制限を指定できる ・複数指定可能 |
基本的に上の表のとおりですが、SSH CA認証において、有効期限、プリンシパル(principal)は重要なキーとなる特徴ですので、もう少し詳細を見てみます。(あとついでにオプションも)
有効期限[-V]
証明書が有効な開始日時、終了日時を指定します。開始日時を指定しない場合は、現在から終了日時までが有効期限となります。
指定記法は以下の通り。
- 時刻指定 YYYYMMDDHHMMSS : (例) 20150320111111:20160320111111
- 2015年3月20日11時11分11秒から2016年3月20日11時11分11秒まで有効
- 日付指定 YYYYMMDD : (例) 20150320:20160320
- 2015年3月20日0時0分0秒から2016年3月20日0時0分0秒まで有効
- 相対日時指定 : (例) -1d:+1d
- 昨日から明日まで有効
また、これらの記法を組み合わせることも可能です。
- (例) -1d:20160101
こんな感じで、かなり自由に設定できます。
注意点としては、この有効期限が検証されるのは、あくまでSSHリクエスト時のみとなります。なのでCA認証での期限内でのログイン後、証明書の期限が切れてもいきなりセッションがバツンと切れたりはせず、持続します。
もしリアルタイムに期限を効かせたい場合はそれ用の監視デーモンを作成するとか、Uber社がOSSで提供しているPAMモジュール(pam-ussh)のようなCA認証を各所に仕込む(sudoなど)とかになるかと思います。(どうせならOpenSSHでそこもサポートしてくれればなぁとか思ったり。。)
まあ漏洩時のリスクを最小限に抑えるならなるべく期限は短めにして、必要な時に都度発行するのが望ましい気はしますがね。
principal[-n]
CA認証での証明書には、署名時にprincipalとして任意の識別子を設定することができます。ログイン先サーバ側では、あらかじめ許可するprincipal一覧を設定しておくことで、ログインリクエスト時に提供される証明書内のprincipalがそのprincipal一覧に含まれているか検証されます。
principal一覧が未設定の場合はデフォルトでログイン要求してきたユーザ名が検証に充てられること(証明書のprincipalにユーザ名があるか)に注意です。
このprincipalをユーザ名に対応づけたり、または複数のユーザを束ねたグループ(Role)に紐づけたりして使うのがよくある使い方のようです。
通常ユーザがCAに証明書を発行してもらう際、何かしら認証が必要かと思いますが、そこでの認証ロジックだったり、ログイン先サーバでのprincipal一覧の管理方法等に応じて適宜どうするといい感じか考えると良さそうですね。
オプション[-O]
このオプションでは、ForceCommandやエージェントフォワード禁止・許可など、本来サーバ側のsshd_configで設定するようなことを証明書でできちゃいます。
CA側でこれらの制御ができるのはいろいろおもしろいことができそう。
詳細はssh-keygen manページをご参照くださいです。
ちなみにこの証明書での制御とログイン先サーバでの設定による制御
CA認証メリット・デメリット
ここまでいろいろ見てきましたが、結局のところ何がどううれしくなるのかについて観てみます。
CASE1 運用サーバが増えた・・・
公開鍵認証:追加されたサーバに対し、運用メンバ全員の公開鍵を登録する必要がある
CA認証:追加されたサーバに対し、CA公開鍵を登録するのみでOK
CASE2 運用メンバーが増えた・・・
公開鍵認証:各運用サーバに対し、増えたメンバーの公開鍵をそれぞれ登録する必要がある
CA認証:増えたメンバーに対し、証明書を発行するだけでOK
CASE3 鍵漏洩のリスクを最小限にしたい
公開鍵認証:有効期限の制御は不可
CA認証:上述の通り設定可能。また、CA側に制御を寄せることができる
懸念点
ここまでいろいろなケースを観てみると、公開鍵認証よりCA認証のほうがいいじゃんってなりそうですが、以下の問題点も考えられます。。
- CAの管理コストがかかる
- CA秘密鍵の漏洩リスク
- CAが障害点になりうる
まあ個人レベルの環境で特に特殊なアクセス制御など必要ないとすると、CA認証のメリットもそれほど享受されず、めんどくさいからもう公開鍵認証でいいやってなりそうな気がします。。
CA認証を実際に試してみる
通常、CA認証では接続元、接続先、認証局の3コンポーネント必要ですが、3つ用意するのもなかなかしんどいのでここでは全部1ホスト内で試してみます。
事前準備
まず、接続元(client)、認証局(CA)のワークスペースをディレクトリに切り、それぞれキーペアを作成します。
# ワークスペース作成 $ mkdir -p catest/{client,ca} # client用のキーペア作成 $ ssh-kaygen -f catest/client/client.key # CA用のキーペア作成 $ ssh-kaygen -f catest/ca/ca.key
CAでの署名処理
clientの公開鍵を、CAの秘密鍵で署名してみます。
# 有効期限: 1日、principal: test-userで署名(test-userは適宜ログイン先サーバに存在するユーザ名に置き換えて) $ ssh-keygen -s ./catest/ca/ca.key -I "ca test" -n "test-user" -z 0 -V +1d ./catest/client/client.key.pub
すると、client公開鍵のあるディレクトリ配下に証明書が生成されます。実際に証明書の中を確認してみます。
$ ssh-keygen -L -f catest/client/client.key-cert.pub client.key-cert.pub: Type: ssh-rsa-cert-v01@openssh.com user certificate Public key: RSA-CERT 8f:39:b9:b5:50:0f:d3:0b:94:69:79:15:f6:a3:c6:1d Signing CA: RSA 53:58:74:1b:98:7d:96:42:b0:eb:65:33:d7:02:bd:06 Key ID: "ca test" Serial: 0 Valid: from 201X-01-03T03:14:00 to 201X-01-04T03:15:58 Principals: test-user Critical Options: (none) Extensions: permit-X11-forwarding permit-agent-forwarding permit-port-forwarding permit-pty permit-user-rc
ちゃんと意図した値が設定されてそうですね。
接続先サーバでの設定
今回は接続先サーバも同じサーバ内で賄うので、そのまま設定していきます。
設定はsshd_configに追記します。通常/etc/ssh/配下にあるはずです。
$ vim /etc/ssh/sshd_config ... # 以下追記 TrustedUserCAKeys /etc/ssh/ca.key.pub AuthorizedPrincipalsFile /etc/ssh/authorizedprincipals ...
TrustedUserCAKeys に証明書の検証に用いるCA公開鍵を、AuthorizedPrincipalsFile に許可するprincipal一覧が書かれたファイルをそれぞれ指定します。
上記の設定通り、ファイルを設置していきます。
# CA公開鍵を設置 $ sudo cp catest/ca/ca.key.pub /etc/ssh/ # principal許可リストを設置(開業区切りで複数指定可能) $ sudo sh -c "echo test-user > /etc/ssh/authorizedprincipals"
最後に、sshdを再起動して設定を反映させます。ssh周りの設定はやらかすとログインできなくなったりなどもあるので反映前にチェックします。
# syntax check $ sudo sshd -t <何も出ないなら問題なし> # 再起動 $ sudo service sshd restart Stopping sshd: [ OK ] Starting sshd: [ OK ]
clientからログイン
それでは、諸々準備は整ったので最後にclientからSSHして確認してみます。
エージェントに既存の公開鍵が登録されているとわかりづらいので、削除してデバッグモードで試します。
# 既存のエージェント一式削除 $ ssh-add -D # ssh リクエスト # 利用する秘密鍵を指定することで、同一ディレクトリ内の「<秘密鍵>-cert.pub」が自動的に証明書として読み込まれます。 $ ssh -i ./catest/client/client.key test-user@kontany.net -vvv ... debug1: Authentications that can continue: publickey,keyboard-interactive debug1: Offering RSA-CERT public key: rsa w/o comment debug3: send_pubkey_test debug2: we sent a publickey packet, wait for reply debug1: Server accepts key: pkalg ssh-rsa-cert-v01@openssh.com blen 1090 debug2: input_userauth_pk_ok: fp 8f:39:b9:b5:50:0f:d3:0b:94:69:79:15:f6:a3:c6:1d debug3: sign_and_send_pubkey: RSA-CERT 8f:39:b9:b5:50:0f:d3:0b:94:69:79:15:f6:a3:c6:1d debug1: Authentication succeeded (publickey). ...
debugログから、RSA-CERTのような文字列が確認できれば証明書による認証が行われていることになります。
まとめ
CA認証についてみていきました。鍵有効期限の制御やprincipalによる制御ができたり、サーバやユーザ追加などの環境変化にもスマートに対応できたりと、いろいろうまみがあります。このうまみは、特に比較的大規模環境の時真価を発揮しそうな感じですね。
しかし、個人環境レベルだと、CA管理コストやCA秘密鍵漏洩リスク、複雑さなどから、正直公開鍵認証で十分な気がします。
公開鍵認証、CA認証どっちがいいか、つまりは扱う環境の規模や、どうアクセス制限をかけたいか次第ってことかなと。
参考
GoogleAdsense
関連記事
-
-
SSH CA認証 (ホスト認証編)
ホスト認証 以前の投稿でSSH CA認証まとめとしてSSH CA認証のユーザ認証についてみて
-
-
MySQLでの条件付きSUM,COUNT
以前つっかかったMySQLでの条件付きSUM,条件付きCOUNTについて備忘録。。。 通常、条
-
-
Vimプラグイン「NERDTree」設定・コマンド備忘録
Vimのプラグインである「NERDTree」について、設定・コマンドを備忘録としてまとめておきます。
-
-
【Androidエラー】Conversion to Dalvik format failed with error 1
Androidにおいて、アプリケーションを作成し終え、いざパッケージをエクスポートっ!!というところ
-
-
genymotionのすすめ
Android開発においてしばしば挙がる不満として、エミュレータのもっさり感があります。 この
-
-
パスワードクラックツール JOHN THE RIPPER 使い方まとめ
パスワードクラッキングツールである「JOHN THE RIPPER」の使い方を備忘録としてまとめてお
-
-
PHPのextensionが読み込まれない問題…
大したあれではないが、ちょっと詰まったのでメモ。。。 事のぼったんは久々にWordPressを
-
-
【Node.js】 Cookieの取り扱いまとめ
最近流行りのNode.jsですが、Cookieに関して、いくつかお決まりの取り扱い方があり、
-
-
【Android】APKのデコンパイルまとめ
AndroidのプロジェクトをパッケージングしたAPKファイルをデコンパイル(Javaのソースコード
-
-
【Android】難読化ツールProguard設定まとめ
Android開発環境の1つであるEclipseでは、標準でAndroidプロジェクトの難読化ツール
GoogleAdsense
- PREV
- 【Node.js】 Cookieの取り扱いまとめ
- NEXT
- SSH CA認証 (ホスト認証編)
Comment
こんにちは。
気づいたことがあるのですが、
共通鍵は公開鍵暗号方式で転送するのでしょうか?
DH方式による生成ではないでしょうか?
それからサーバー側で、公開鍵で暗号化されたものをハッシュ化しているように見えるのですが、暗号化前にハッシュ化ではないでしょうか?
唐突に失礼いたしました。
>mywatertank
ご指摘ありがとうございます。
>共通鍵は公開鍵暗号方式で転送するのでしょうか?
>DH方式による生成ではないでしょうか?
プロトコル1方式しか記載してませんでした。ご指摘通り、プロトコル2ではDH方式による生成となりますね。修正しました。
>それからサーバー側で、公開鍵で暗号化されたものをハッシュ化しているように見えるのですが、暗号化前にハッシュ化ではないでしょうか?
ご指摘通りです。図の作り間違えです。時間のあるときに修正しときますね。
チャレンジレスポンス認証なので、サーバ側で生成した乱数のハッシュ検証が行われているのですね。。