LoginSignup
28
15

More than 3 years have passed since last update.

Cisco AnyConnect を使った VPN への接続を自動化する (Mac 版)

Last updated at Posted at 2020-10-02

これは何?

macOS でこれの入力を自動化するやつ。

スクリーンショット 2020-10-02 12.32.00 1.png

Cisco AnyConnect クライアントは VPN 接続で Multi-Factor Authentication (MFA) をサポートしているが, これを毎回接続時に実行するのは死ぬほど面倒くさい。というわけで自動化を試みてみた。

当初は AppleScript を使って GUI 操作の自動化を試みていたが,このアプリケーションのクセが非常に強かったので断念。 CLI からの自動化を試みて,後から GUI 版を起動するという解決策を採る。

準備手順

Cisco AnyConnect のインストール

通常,企業から支給された PC にはインストールされていると思います。未インストールの場合は各自ダウンロードしてきてインストールしてください。

GUI アプリケーションには CLI コマンドも同梱されているので, これを vpn コマンドで呼び出せるようにパスを通すかエイリアスを張るかしておいてください。自分の環境では以下の場所にインストールされていました。

/opt/cisco/anyconnect/bin/vpn

oathtool コマンドのインストール

brew install oath-toolkit

MFA の TOTP 発行のために必要です。

キーチェーンへの登録

macOS の 「キーチェーンアクセス」にあらかじめ以下の秘密情報を登録しておいてください。

  • パスワード
  • MFA のトークン発行元となるシークレットの BASE 32 エンコード値

スクリーンショット 2020-10-02 12.39.47.png

  • 「アカウント名」はここでは使用しないので何でもいいです。
  • もしシークレットが QR コードで提供されている場合は, zbarimg および php を使って読み取ってください。
# インストール
brew install zbar

# パースして出力
zbarimg -q 画像ファイルへのパス | php -r 'parse_str(parse_url(fgets(STDIN), PHP_URL_QUERY), $_); echo $_["secret"];'

スクリプトの整備

以下のコマンドを,普段お使いのシェルが Zsh の場合には ~/.zshrc に, Bash の場合には ~/.bashrc にて登録してください。

vpn() (
  # エラー対応
  set -eu

  # パラメータの設定
  export VPN_HOST='接続先'
  export VPN_GROUP=${VPN_GROUP:=デフォルトのグループ番号} 
  export VPN_USERNAME='ユーザ名'
  export VPN_PASSWORD_KEYNAME='パスワードキーチェーン登録名'
  export VPN_SECOND_USERNAME='第2ユーザ名'
  export VPN_TOTP_SECRET_KEYNAME='シークレットキーチェーン登録名'

  # "vpn" "vpn connect" 以外の呼び出しは元のコマンドに流す
  if [[ "$#" -ne 0 && ( "$#" -ne 1 || "$1" != 'connect' ) ]]; then
    exec command vpn "$@"
  fi

  # GUI 版が起動していたら一度強制終了する
  killall 'Cisco AnyConnect Secure Mobility Client' >/dev/null 2>&1 || :

  # 既に VPN に接続していたら強制切断する
  expect -c '
    set log_user 0
    set timeout 5
    spawn vpn disconnect
    expect ">> state: Disconnected"
    interact
  '

  # キーチェーンからの取得
  VPN_PASSWORD="$(security find-generic-password -s "$VPN_PASSWORD_KEYNAME" -w)"
  VPN_TOKEN="$(oathtool --totp --base32 "$(security find-generic-password -s "$VPN_TOTP_SECRET_KEYNAME" -w)")"
  export VPN_PASSWORD
  export VPN_TOKEN

  # 接続
  expect -c '
    set log_user 0
    set timeout 5

    spawn vpn connect $env(VPN_HOST)

    expect "Group:"
    send "$env(VPN_GROUP)\r"
    expect "Username:"
    send "$env(VPN_USERNAME)\r"
    expect "Password:"
    send "$env(VPN_PASSWORD)\r"
    expect "Second Username:"
    send "$env(VPN_SECOND_USERNAME)\r"
    expect "Second Password:"
    send "$env(VPN_TOKEN)\r"

    interact
  '

  # GUI 版を起動
  open '/Applications/Cisco/Cisco AnyConnect Secure Mobility Client.app'
)
  • 環境変数を利用して,エスケープ処理などを考慮せずに簡単に下位のプロセスに値を渡せるようにしています。
  • シェルを単なるサブシェル {} ではなく別プロセス () として起動させることで, exportset -eu の影響が外に出ないようにしています。
  • GUI 版起動状態では CLI 版での操作は行えませんが, CLI 版で接続済みの状態で GUI 版を起動すると状態の引き継ぎが行えるようです。ここではその性質を利用して最後に GUI アプリケーションの起動を行っています。

やっぱり接続状態が視覚的に見えるほうがいいですよね↓

image.png

使い方

  • vpn または vpn connect で接続
    • パスワード入力を求められます。 残念ながら Touch ID は使えないようなので, 利便性を考えると常に許可を押しておくのが現状妥当な選択肢かなと思います。
    • グループ番号を変更したいときは VPN_GROUP=xxx vpn
  • vpn disconnect で切断
  • vpn state で接続状態確認
  • 元のコマンドをそのまま実行したいときは command vpn
28
15
1

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
28
15