SSHログイン時に公開鍵認証とGoogle OAuthで多要素認証する

こんばんは。みなさんは在宅勤務が大好きですか?僕は大好きです。

好きすぎるがゆえにこういったことも起きました。

そうなると、いかに安全に踏み台サーバを構築するかということになるのですが、セキュリティにおいて多層防御の考え方は非常に大事です。一般的な踏み台サーバであればおそらくFromIPでの制御と、公開鍵認証での運用が多いのではないでしょうか。ペパボでは僕が開発運用しているSTNSを利用して、公開鍵認証でのログインを必須にしているかつ、IPでの制御も行っています。

さて、まだ本番では一切何もしていないのですが、このIPでの制御をやめるとなった場合に、多層防御の層が一枚薄くなってしまいます。それをどうにかしてカバーしようと思うと、何かしら考える必要があります。

ということで考えたのがこちらです。

体験としては下記のようになります。

% ssh bastion-server
# 公開鍵認証が通ったら下記のメッセージが出る
Go to the following link in your browser then type the authorization code: 
# このURLをブラウザで開く
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=xxxxxxxx-xxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=uxxxxx
# ブラウザで認証後表示されたコードをペーストする
Please type code:xxxxxxxxxxxxxxx

sshでサーバにログインする際に、まず公開鍵認証が行われ、正解するとたーみなるに、このURLを開いてくれというメッセージが出ます。

https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=xxxxxxxx-xxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=uxxxxx

上記の部分ですね。これをブラウザに貼り付けて、Googleの認証を超えると、あるコードが払い出されるので、それをターミナルに貼り付けると無事ログインできます。また、ログイン後はトークンが有効期限のうちは再度oAuthする必要はありません。

インストール方法

OAuth 2.0 の設定 を参考にGoogleの認証情報を作成し、下記のようにoAuthクライアントを払い出してください。

払い出しができたら、secretファイルをダウンロードし、踏み台サーバの /etc/google-web-oauth/client_secret.json に配置します。Permissionは600としてください。

コマンドを releases からダウンロードして、踏み台サーバの /usr/binに置いてください。また google-web-oauth だけをsudo実行できる設定にして下さい。これがない場合、一般ユーザーから、client_secret.jsonが見えずにコマンドが失敗します。

sudoを許容している以上、このコマンドが開くファイルの中身を全く見れないわけではないのでその点については理解の上、活用してください。

%example ALL=(ALL) NOPASSWD: /usr/bin/google-web-oauth

そして、最後に /etc/sshd/sshd_config に下記の設定を入れます。

ForceCommand sudo /usr/bin/google-web-oauth && eval ${SSH_ORIGINAL_COMMAND:-/bin/bash}

ForceCommandについては こちら が詳しいです。

ForceCommand (強制コマンド実行)
クライアントが指定したコマンドを無視し、ForceCommandによって指定されたコマンドと、存在する場合は~/.ssh/rcを強制的に実行します。このコマンドはそのユーザのログインシェルの -c オプションを使って実行されます。この項目はシェル、コマンド実行、あるいはサブシステムの実行に適用されます。この設定項目はMatchブロックと組み合わせるともっとも有用です。クライアントが本来指定したコマンドラインはSSH_ORIGINAL_COMMAND 環境変数に格納されます。コマンドとして”internal-sftp”を指定すると、強制的に内蔵の sftp サーバを使います。これはChrootDirectoryが指定されているときに、外部のファイルなしで動作できます。デフォルトは”none”です。

要はユーザー権限で、 /usr/bin/google-web-oauth を実行し、正常に認証が通れば、ユーザーが指定したコマンドを実行、もしくは指定がない場合は /bin/bash を起動するということをやっています。認証が通らなかった場合はそこで終了します。もちろんscpについてもこれまで通り利用することができます。

最後に

もともとはpamを利用してあれこれしようとか、キーボード対話的認証 を利用してあれこれやろうと思って追ったのですが、そちらを選択した場合、sshdのpamの使い方がちょい僕の用途と合わないところがあって、四苦八苦して、ドキュメントを隅々まで読んだら ForceCommand を見つけた感じでした。もう少し脆弱な点がないかなど調べてみて、問題なさそうだったら僕も本番にもっていこうかなと思います。

このエントリは続編があります。