18
20

More than 3 years have passed since last update.

CLI で Authy と共通の MFA Token を生成する

Last updated at Posted at 2016-11-01

AWS や Google 等で利用可能な MFA の仮想デバイスとしては、複数デバイス間の同期や Chrome Extension を擁する Authy が便利なわけですが、PC 特に CLI を使ってるといちいちデバイス or Chrome Extension を開く必要があり、かなり面倒です。
CLI から token を生成する OATH Toolkit の oathtool を使えば、この面倒さが少しは解消され、スクリプト化による自動処理も可能になります。
ただし、oathtool 自体は毎回 SecretKey が必要になるため、mfacodegen という名の wrapper script を用意して便利に利用できるようにします。

利用イメージは以下の通り。

mfacodegen.png

Authy Desktop から SecretKey を取得する

MFA では

  • TOTP (Time-based One Time Password) RFC 6238
  • HOTP (HMAC-based One Time Password) RFC 4226

が使われていますが、これらの token を生成するには SecretKey が必要となります。

SecretKey は MFA 設定時の QR code 内に仕込まれているのですが、既に Authy を設定済の場合は アプリ上から SecretKey を見ることが出来ません。
ただし Authy は定期的に token を生成するために内部的には当然 SecretKey を保持しています。
Authy Desktop は JS で token を生成しているため、デバッグポートを有効にした状態でアプリを起動し、Developer Tools で内部を覗けばバッチリ取得できます。

Mac の場合は Terminal を開き、以下のコマンドで Authy Desktop を開きます。

open -a "Authy Desktop" --args --remote-debugging-port=5858

Windows の場合は Authy Desktop のショートカットを作成し、右クリック -> プロパティ -> ショートカット -> リンク先 の最後に --remote-debugging-port=5858 を付加して保存。作成したショートカットからアプリを開きましょう。

まず、Authy Chrome Extension を開き、ログインした後のアカウント一覧画面で右クリック、Inspect(検証)を開きます。

Authy Desktop を起動したら、Chrome で http://localhost:5858 を開きます。
デバッグポートに正常に接続できれば以下のような画面が表示されます。

Content shell remote debugging 2021-08-04 16-20-19.png

Twilio Authy をクリックすると、次のように Developer Tool が使えるようになります。

main.html 2021-08-04 16-22-55.png

Developer Tools が開いたら、Console タブを開き、以下のコードを入力して実行します。

appManager.getAuthenticatorApps().forEach(app=>console.log(app.name,app.originalName,app.decryptedSeed))

すると、アカウント名、SecretKey が Console に出力されるはずです。

main.html 2021-08-04 16-27-26.png

得られた SecretKey は、パスワード管理アプリ等を用いて安全に保管しておきましょう。

oathtool で MFA Token を生成してみる

Mac であれば、brew を使えば簡単にインストールできます。

brew install oath-toolkit

以下のコマンドで token が生成できることを確認します。

stty -echo; echo -n 'Enter SecretKey: '; read skey; echo; stty echo; oathtool -b --totp $skey

oathtool -b --totp [SecretKey] だけでも確認できますが、その場合は shell の history に SecretKey が残ってしまうので、後で .bash_history や .zsh_history から削除する必要があります。

wrapper script (mfacodegen) を作成する

このままだと SecretKey の扱いが面倒で非常に使いにくいので、

  • Service ID、SecretKey を書いた key list を用意する
  • key list は openssl で encrypt しておく
  • wrapper script が key list を参照、decrypt
  • wrapper script の引数で渡された Service ID のエントリを元に oathtool で token 生成

のような処理を行います。

暗号化した SecretKey リストファイルを作成する

一行が [ServiceID]\t[SecretKey] となるようなリスト mfalist.txt を適当な場所に生成します。

ServiceID は CLI のオプションとして無難な文字列にしておいてください。

mfalist.png

S/MIME 用の秘密鍵と証明書を生成します。

openssl req -x509 -days 3650 -newkey rsa:2048 -keyout /path/to/mfa.key -out /path/to/mfa.crt -subj '/'

作成した証明書を用いて、openssl コマンドで encrypt します。

openssl smime -encrypt -aes256 -in /path/to/mfalist.txt -out /path/to/mfalist.dat -binary -outform PEM /path/to/mfa.crt

念のため、decrypt 可能かどうか、確認しておきましょう。
以下のコマンドで、標準出力に decrypt 結果が表示されれば成功です。

openssl smime -decrypt -in /path/to/mfalist.dat -inkey /path/to/mfa.key -binary -inform PEM

元の /path/to/mfalist.txt は不要なので削除しておきます。

mfacodegen を設置する

以下のファイルを path の通った適当な場所に設置します。

最初の LIST_PATH および KEY_PATH は適宜修正してください。
また、chmod +x を忘れずに。

mfacodegen
#!/bin/bash
set -e

LIST_PATH=/path/to/mfalist.dat
KEY_PATH=~/path/to/mfa.key

usage() {
  echo "Usage: $0 [-clh] [-s service]" 1>&2
  exit 1
}

while getopts cls:h OPT; do
  case $OPT in
    c)  mode='copy'
        ;;
    l)  cmd='show'
        ;;
    s)  cmd='generate'
        service=$OPTARG
        ;;
    h)  usage
        ;;
    \?) usage
        ;;
  esac
done

[ -z "$cmd" ] && usage

generateToken() {
  list=$(loadList)
  seckey=$(echo "${list}" | awk "\$1==\"${service}\"{print \$2}")
  [ -z "$seckey" ] && { echo "Service ${service} not found." >&2; exit 1; }
  if [ "$(uname)" = 'Darwin' -a "$mode" = 'copy' ]; then
    echo -n $(oathtool -b --totp $seckey) | pbcopy
  else
    oathtool -b --totp $seckey
  fi
}

loadList() {
  if [ -p /dev/stdin ]; then
    stdin=$(cat -)
    openssl smime -decrypt -inkey $KEY_PATH -in $LIST_PATH -binary -inform PEM -passin "pass:$stdin"
  else
    openssl smime -decrypt -inkey $KEY_PATH -in $LIST_PATH -binary -inform PEM
  fi
}

showServiceList() {
  list=$(loadList)
  echo "${list}" | cut -f1 | sort
}

case "$cmd" in
  'show' )
    showServiceList
    ;;
  'generate' )
    generateToken
    ;;
esac

動作確認

-l オプションで、サービスの一覧が表示されます。

mfacodegen -l

-s [ServiceID] オプションで、指定のサービス ID の token を生成します。

mfacodegen -s github

Mac の場合は、-c -s [ServiceID] で token がクリップボードにコピーされます。

mfacodegen -c -s github

これで、CLI で MFA token が取得できるようになりました。

18
20
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
20