サーバログインのスタンダードであるOpenSSH
に関して、U2F/FIDO2認証
のサポートがコミットされました。
ということで早速どんな感じなのか試してみたのでまとめてみますよ。
はじめに
OpenSSHでのU2F/FIDO2認証サポートがコミットされたとの報告がコミュニティで上がりました。
U2F/FIDO2自体については以前も簡単に扱ったのでそちらを参照ってことでここでは割愛しますが、 セキュリティキーを利用した認証ログインの仕組みです。
WebブラウザベースでのU2F/FIDO2(Webauthn)認証とは結構雰囲気が異なっていたので注意です。 どちらかというと使い勝手的にはPKCS#11(スマートカード)に近い印象でした。
ここではどんな感じか試したのでまとめてみます。
先に結論を言っておくと、一応セキュリティキーを利用して登録・検証通して再現できはしましたが、 ところどころ条件によってはうまく行かないところもあり、まだ安定はしてない印象でした。
検証したのは2019年12月末で、ここでの検証ではその段階での最新のOpenSSH
,
ならびに依存ライブラリ(libfido2
)を利用しています。
※ 挙動は今後リファクタリングによってこれからどんどん変わっていく(安定していく)ものと思われますのでその点留意ください。1
構成概要
OpenSSHでのFIDOサポートでは、FIDO2もU2F(CTAP1)を包括していることから、 U2F/FIDO2どちらでも汎用的に利用できるように、基本的にはU2Fの機能のみで実現できます。
詳細な仕様についてはOpenSSHのPROTOCOL.u2f を参照するとおもしろいかもしれません。
鍵の登録から認証までのフローのざっくりとした概略は以下のようになります。
WebブラウザベースでのU2F/FIDO2フローとは異なり、登場人物はログイン元(SRC)
とログイン先(Dest)
のみ、FIDOサーバ
は登場しません。
順にフローを追ってみます。
- 登録フロー
セキュリティキー(device-secret)
を用いてキーペア(key-handle + pubkey)
を生成します。- 生成したキーペアのうち、
公開鍵(pubkey)
をログイン先(Dest)
に何らかの方法でexportします。
- 認証フロー
- キーペアのうち、
key-handle
からセキュリティキー(device-secret)
を用いて秘密鍵(privkey)
を導出します。 - 導出した
秘密鍵(privkey)
を用いて公開鍵認証を行います。
※ ここではセキュリティキーから生成されたキーペアのpriv部分をkey-handle
と書いておきます。
勘違いしやすいですがYubikeyなどのセキュリティキー自体には認証時に利用するprivkey
はストアしません。
詳細は以下がわかりやすいです。
特に難しいことはないと思いますが、Webブラウザベースフローではkey-handle
の管理はFIDOサーバが担っていましたが、
それが存在しないため、その管理はログインユーザ(SRC)側に委ねられるようです。。
つまり、セキュリティキーとは別にkey-handle
もSRCのホストにコピーして回らなければならいぽい。。
(この辺りの不都合さは後述するFIDO2でのresident key
の仕組みでカバーすると言うことのようです。)
概要を確認したところで、それでは実際に試してみます。
検証環境
USBインタフェースに挿したセキュリティキーを介した認証となるため、リモートのサーバからではセキュリティキーとU2F(CTAP)通信ができません。
なのでLinux(今回はUbuntu 18.04.3 LTS
)Localマシン環境で行います。詳細は以下となります。
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
また、セキュリティキーとして今回はYubico社がリリースしている以下を用いました。
- Security Key By Yubico (FIDO2対応のもの)
- YubiKey 4 (FIDO2非対応・U2F対応のもの)
SSHでのFIDO認証を検証してみる
準備
まず検証にあたって必要なソフトフェアをインストールします。
middleware (libfido2
) インストール
OpenSSHでは、U2F/FIDO2サポートにあたってセキュリティキーとの通信プロトコル部は自身に盛り込まず、外部のミドルウェアライブラリに委譲する仕様としたようです。(PKCS#11と同様)
ということでその外部のミドルウェアライブラリとしてYubicoが公開している Yubico/libfido2をまずインストールします。
ビルド・インストール手順は公式のとおりですが、依存など含めて一応記載しておきますよ。
# install dependencies
$ sudo apt install cmake libcbor-dev libssl-dev libudev-dev
# install libfido2
$ git clone https://github.com/Yubico/libfido2.git && cd libfido2
$ mkdir build && cd build
$ cmake ..
$ cd .. && make -C build
$ sudo make -C build install
# confirm
$ ls /usr/local/lib/ | grep fido
libfido2.a
libfido2.so
libfido2.so.1
libfido2.so.1.3.0
libsk-libfido2.so
OpenSSH Latest インストール
U2F/FIDO2サポートした最新の master OpenSSH をインストールします。
これもビルド・インストール手順は公式のとおりですが、依存・オプションなど含めて一応記載しておきますよ。
# install dependencies
$ sudo apt install autoconf gcc g++ make
$ git clone https://github.com/openssh/openssh-portable && cd openssh-portable
$ autoreconf
# 既存のopensshとの競合を避けるため、`--prefix`でインストール先を適当なところに変更します
# 上でインストールした`libfido2`をincludeするため`--with-security-key-builtin`オプションを付与
$ ./configure --prefix=$HOME/.local --with-security-key-builtin
# それっぽいリンクのチェックが行われるのを確認
checking if /usr/bin/pkg-config knows about libfido2... yes
checking for fido_init in -lfido2... yes
OpenSSH has been configured with the following options:
User binaries: /home/nicopun/.local/bin
System binaries: /home/nicopun/.local/sbin
Configuration files: /home/nicopun/.local/etc
Askpass program: /home/nicopun/.local/libexec/ssh-askpass
Manual pages: /home/nicopun/.local/share/man/manX
PID file: /var/run
Privilege separation chroot path: /var/empty
sshd default user PATH: /usr/bin:/bin:/usr/sbin:/sbin:/home/nicopun/.local/bin
Manpage format: doc
...
Translate v4 in v6 hack: yes
BSD Auth support: no
Random number source: OpenSSL internal ONLY
Privsep sandbox style: seccomp_filter
PKCS#11 support: yes
U2F/FIDO support: built-in
# 上記構成でインストール
$ make && sudo make install
# 確認
$ cd $HOME/.local/bin/ && ls
scp sftp ssh ssh-add ssh-agent ssh-keygen ssh-keyscan
--with-security-key-builtin
オプションを付与したため、./configure
結果でU2F/FIDO support: built-in
となっているのを確認します。
鍵生成・認証
もろもろ下準備は完了したので試してみます。以下でのSSH系のコマンドは上記でインストールした$HOME/.local/
配下のものを利用します。
キーペアの生成
U2F/FIDO用のキータイプとしてecdsa-sk
,ed25519-sk
が追加されています。
今回はecdsa-sk
を指定してキーペアを生成してみます。
(※ FIDO/U2F機能を利用するにはセキュリティキーが少なくともECDSA-P256
をサポートしている必要があります。ED25519
はオプション。今回の検証で用いたキーはそもそもED25519
をサポートしてなさそうなため利用できませんでした。)
USBにPIN設定済みのSecurity Key By Yubico(FIDO2対応のもの)
を挿入して以下を実行していきます。
$ ./ssh-keygen -t ecdsa-sk
Generating public/private ecdsa-sk key pair.
You may need to touch your security key to authorize key generation.
Enter PIN for security key:
Enter file in which to save the key (/home/nicopun/.ssh/id_ecdsa_sk):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/nicopun/.ssh/id_ecdsa_sk.
Your public key has been saved in /home/nicopun/.ssh/id_ecdsa_sk.pub.
The key fingerprint is:
SHA256:ACNdkdpt/2YFO52YHjFlJAKCBfT9k4tt0sjGK2VHiq4 nicopun@nicopun-laptop
The key's randomart image is:
+-[ECDSA-SK 256]--+
| .o===o... ... |
| .o+.o . .o |
| oo.. o |
| . ..o...+ |
| oS++ O . |
| .o+=oo* + |
| . oB.=o + |
| o. + = |
| E. .. o |
+----[SHA256]-----+
$ cd $HOME/.ssh
$ cat id_ecdsa_sk
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAfwAAACJzay1lY2
RzYS1zaGEyLW5pc3RwMjU2QG9wZW5zc2guY29tAAAACG5pc3RwMjU2AAAAQQSQ1QYr3P3m
VOdZYNwurDEuLjetAq0n2dBUWeZujvv+Vps05YbuLNiGzU8K4HQvqTZsZXcHyNsItSVsbA
goJzgvAAAABHNzaDoAAADwy9G+esvRvnoAAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBv
cGVuc3NoLmNvbQAAAAhuaXN0cDI1NgAAAEEEkNUGK9z95lTnWWDcLqwxLi43rQKtJ9nQVF
nmbo77/labNOWG7izYhs1PCuB0L6k2bGV3B8jbCLUlbGwIKCc4LwAAAARzc2g6AQAAAEBs
hiZwb2AhzwaH1C8OrQDKnniGnrAFuPmWFbbLEAi2MoCz3hv9i5dGU4OcNqhnV2aBZn7QiL
/2kPAOmJ84/VAUAAAAAAAAABZrb250YW5pQGtvbnRhbmktbGFwdG9wAQIDBAUG
-----END OPENSSH PRIVATE KEY-----
$ cat id_ecdsa_sk.pub
sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBJDVBivc/eZU51lg3C6sMS4uN60CrSfZ0FRZ5m6O+/5WmzTlhu4s2IbNTwrgdC+pNmxldwfI2wi1JWxsCCgnOC8AAAAEc3NoOg== nicopun@nicopun-laptop
以下の出力メッセージのあと、PIN
の入力とセキュリティキーへのタップ(UP)
が要求され、一通り操作を終えるとキーペアが生成されました。
You may need to touch your security key to authorize key generation.
Enter PIN for security key:
これらのキーペアを用いて認証を行います。
(ちなみに、PIN
が設定されていないU2Fキー
の場合はPIN
は要求されませんでした。)
SSHD (dest) サーバ起動
今回はsource, destともにlocalhostで検証を行います。(localhost -> localhost)
まず、上で生成したキーペアのうち、pub
をauthorized_keys
に追加しておきます。
$ cat id_ecdsa_sk.pub > $HOME/.ssh/authorized_keys
クライアントでの認証を行う前に、SSHDサーバ側も上記のキータイプ(*-sk)を解釈できるようにするため最新にします。 既存のSSHDプロセスを先程インストールした最新のものに挿げ替えます。
# 既存プロセスの停止
$ sudo systemctl stop sshd
# latestのSSHDを起動
$ sudo $HOME/.local/sbin/sshd -D
認証メソッドとしてはpublickey
扱いなので、とくにsshd_config
の変更は不要なはずです。
認証(ログイン)
最後にログインしてみます。
今回生成したprivkey
(厳密にはkeyhandle
)はデフォルト名(id_ecdsa_sk
)で自動参照してくれるため、そのままssh
してみます。
$ ssh -A localhost
Confirm user presence for key ECDSA-SK SHA256:UHdGvOx7YajtnMtrOsN5V9lFZqBeteub9jMJlIWFncE
Last login: Sun Dec 28 22:42:25 2019 from 127.0.0.1
セキュリティキーへのタップ(UP
)が要求され、タップすると無事認証されました。
また、ssh
の際、USBに対象のセキュリティキーが挿入されていない場合速攻でfailします。
加えて、複数セキュリティキーを挿入していた場合、どちらか1つしか反応しませんでした。
(※これはlibfido2
の仕様上仕方なしらしい。)
ちなみにこのid_ecdsa_sk
ですが、これ自体では意味を持たないため、
通常の公開鍵認証での秘密鍵ほどセンシティブなものではないことに留意です。
機能詳細
上記で一応通して検証はできましたが、もう少し関連する機能詳細について見てみますよ。
UP(セキュリティキーへのタップ)を制御
デフォルトセキュリティキーでの認証時、UPを満たすためセキュリティキーへのタップが要求されますが、これはキーペア生成時にオプションで指定していてかつDestサーバ側で設定することで制御可能なようです。
ssh-keygen
でのUP
不要指定
-O no-touch-required
sshd_config
による制御
PubkeyAuthOptions (none / touch-required)
PubkeyAuthOptions
によってsshd
レベルでUP
を制御可能です。デフォルトtouch-required
なので、
もしセキュリティキーでの認証にUPが不要ならnone
指定します。
authorized_keys
による制御
各セキュリティキーごとにより細かく制御したい場合、authorized_keys
レベルでの制御も可能です。
対象の公開鍵のprefixに以下オプションを指定してあげます。
no-touch-required
ざっとみた感じUV
(User Verificaiton)はなさそうです。
(U2Fの時代はそんなフラグなかったもんね。)
Provider(middleware)の制御
今回の検証ではbuilt-in
でlibfido2
を指定したため、特になにも指定しませんでしたが、これもオプションで色々制御できます。
ssh-agent
でのWhite-listの設定
通常、ssh-agent
で利用するshared-library
はwhitelist制御
されています。デフォルトのwhitelist
は/usr/lib/*, /usr/local/lib/*
なので普通は特に指定なしで問題ない事が多いですが、もし別のpathにインストールされている場合は別途whitelist追加してあげる必要があります。
-P provider_whitelist
ssh-add
でのprovider指定
ssh-add
でagentに追加するタイミングでprovider
の指定ができます。これはオプション
か環境変数
で可能。
- オプション指定の場合
-S provider
- 環境変数指定の場合
SSH_SK_PROVIDER
ssh-keygen
でのprovider指定
これも上記と同じような感じでオプション
か環境変数
で可能です。
- オプション指定の場合
-w provider
- 環境変数指定の場合
SSH_SK_PROVIDER
resident key
によるキーペアの導出
さて、U2F/FIDO認証にはセキュリティキーに加えてkey-handle
も必要なわけですが、
それだと各ログイン元であるSRCにkey-handle
を何らかの方法で逐一転送してやらないといけないくなります。
それだとせっかくのセキュリティキーのポータブルな旨味が目減りします。
そこでセキュリティキーのみでなんとかなるように、FIDO2でのresident key
を活用してキーペアを導出するためのseed
をセキュリティキーへストア・利用する機構があるようです。
これにより、セキュリティキーのみで任意の環境からのSSHが可能となります。
(※ セキュリティキーのみで認証が完結できるため、セキュリティキー盗難時のリスクが大きくなる気もしますが、
residentkey
へのアクセスはPIN
認証が必須であり対策がとられています。)
手順としてはまずssh-keygen
でのキーペア生成時にオプションでresidentkey
利用を指定してやります。
ssh-keygen -Oresident -t ecdsa-sk
これでセキュリティキーへストアされ、利用する際はssh-keygen
でのexport, ssh-add
でのエージェント追加などします。
ssh-keygen -K
ssh-add -O
これならまあまあ使い勝手も良いのではないでしょうか。
まあ自分の環境ではこれは再現しなかったんですけどね。。。。
おわりに
今回、SSHでのU2F認証を試してみました。
もちろんこのサポートのアプローチでなくともSSHにはPAM
やらForceCommand
やら
を駆使することで別のやり方もできそうです。
個人的にはSRCのクライアントのsshクライアント更新はまだいいとして、 Destのsshdも最新に更新しないとけないのはなかなかハードル高い気がしてます。(PIVとかだとそんな必要なかったのに)
ただ、キーペアの生成から認証まで一貫してFIDOのプロトコルで面倒見れて手軽な印象だったので、 そのあたりは美味しいのかもしれません。
所々オプションがうまくいかなかったりWSLやMacでも試しましたが、挙動がいまいちだったので もう少し安定しないと使うのは厳しいそうな印象でした。
と言うことでもう少し様子見しまふ。という〆
-
2020-02-14、
OpenSSH 8.2
にて正式にFIDO/U2Fサポートがリリースされました。 詳細はOpenSSH-8.2リリースノートをご参照ください。 ↩︎