Cisco AnyConnect に付属する vpn クライアントは会話のように振る舞います。プロンプトを表示し、一行を待ち、次を表示します。その会話をファイルやパイプから流し込めば、同じバイナリが無人で動作します。手法はリダイレクト一つと順序付きの回答リストであり、スクリプトが信頼できるかどうかを決めるのはリダイレクトではなくリストのほうです。

実際には二つのことがこれを壊します。ゲートウェイが追加の質問をするとプロンプトの順序が変わり、保存されたパスワードは一度の打ち間違いをアカウントのロックアウトに変えます。手順はバージョン管理されたデータとして扱い、ゲートウェイの変更のたびに差分を確認してください。再試行ではなく大きな音を立てて失敗させてください。ループこそが、打ち間違えたパスワードをロックされたアカウントに変える道だからです。

一つの手順を、クライアントが尋ねる順序で

クライアントを一度対話的に実行し、表示される順序どおりにすべてのプロンプトを書き留めます。その記録が回答ファイルになります。ファイルをバイナリにリダイレクトするとき、最初の行は接続コマンド自体でなければなりません。だから手順はユーザー名ではなくホスト名で始まるのです。

connect vpn.example.com
0
<username>
<password>
y
exit

素の 0 は、ゲートウェイが一覧を提示したときに最初の接続プロファイルを選びます。ゲートウェイが表示するインデックスを使ってください。y はログインバナーを受け入れるので、バナーがなければその行を削除し、ゲートウェイが追加の質問をする場所に行を挿入します。末尾の exit は、最後の回答が消費されたときにクライアントをきれいに終了させるものです。

/opt/cisco/anyconnect/bin/vpn -s < anyconnect.txt

ここでは非対話モードは任意ではありません。-s がないとクライアントが端末を占有し、リダイレクトされた入力を無視することがあります。これがあれば回答はストリームから読まれ、プロセスは戻ります。バイナリに昇格した権限が必要な場合は、呼び出しを sudo -n で包み、パスワードのプロンプトで止まるのではなく即座に失敗させます。

回答ファイルが漏れる場所

パスワードを含むファイルは資格情報です。ですから資格情報の権限を与えます。モード 600、ジョブを実行するアカウントの所有とし、共有や同期のディレクトリには決して置きません。後から直すのではなく、モードを最初から適用して作成してください。書き込みと chmod の間、ファイルは一瞬だけ誰でも読めるからです。

install -m 600 /dev/null ~/.config/anyconnect.txt
chmod 700 ~/.config

もう半分は argv です。引数として渡されたパスワードは、プロセスを一覧するすべてのローカルユーザーに見え、シェルの履歴にも残ります。代わりにファイルディスクリプタや環境変数から読み取り、ペイロードを引用してシェルが内部で何も展開できないようにします。

printf '%s\n' 'connect vpn.example.com' '0' "$VPN_USER" "$VPN_PASS" 'y' 'exit' \
  | sudo -n /opt/cisco/anyconnect/bin/vpn -s

引用に注意してください。二重引用符の中では、! を含むパスワードがクライアントに見える前にやり取り中のシェルで展開されることがあり、失敗はプロトコルの問題に見える一般的な event-not-found エラーとして届きます。

トンネルを証明し、それから閉じる

接続済みという状態は、クライアントがセッションを持っていると信じていることしか証明しません。社内ネットワークの内側からしか応答しないホストを選び、何かをする前に一度だけ試してください。そこでの失敗はルーティングかポリシーの不具合であり、ログインをやり直しても変わりません。

/opt/cisco/anyconnect/bin/vpn state
curl -sS -o /dev/null -w '%{http_code}\n' --max-time 10 https://<internal-host>/
/opt/cisco/anyconnect/bin/vpn disconnect

切断の前後で状態の出力を確認します。古いセッションを抱えたままのクライアントは、スクリプトがまだ成功を報告していても次の接続を拒否することがあり、終了コードだけを見ているとこの失敗の形は見えません。二回目の読み取りだけを信じるのではなく、二つの読み取りを比較してください。シェルプロファイルのエイリアスは日常利用には十分ですが、スケジュールされたものは他の無人ジョブと同じ資格情報と身元の見直しに値します。

固定リストが安全でなくなるとき

回答ファイルは今日のプロンプト順序を符号化しています。バナーの変更、強制パスワード変更、第二要素の要求は、それをアカウントをロックしうるループに変えます。だからスクリプトは再起動ではなく、最初の予期しない回答で終了しなければなりません。

クライアントが表示した内容を取得し、非ゼロのステータスを返し、判断は呼び出し側に委ねます。ゲートウェイが第二要素を要求する場合、正直な答えはそのプロファイルでは無人ログインが利用できないということであり、手順にあと一行足りないということではありません。

ローテーションも同じ見直しに属します。資格情報を保存するなら、誰が読めるか、いつ変わるか、ホストが再構築されたときに何がそれを消すかを決めてください。誰もパスワードに触れなかったから一年動くスクリプトは設計ではなく、ローテーション方針を待つ事故です。

AnyConnect ではないプロファイル

Cisco の古い資格情報の二つの様式がこれと混同されます。vpnc プロファイルは IPSec ID を要求します。これは実際にはグループ名で、次にグループシークレット、次に自分の資格情報を要求し、三つともゲートウェイを運用する人から来ます。従来の Cisco IPsec は AnyConnect とは別のプロトコルなので、vpnc は AnyConnect 専用のサービスには到達できません。openconnect --protocol=anyconnect がその作業のためのオープンなクライアントです。

.pcf ファイルは古いクライアントプロファイルを記述します。その中のホスト、グループ名、グループパスワードは Cisco IPsec を話すクライアントで手入力し直せますが、ファイルは万能なインポートではなく、非標準のポートやトンネリングモードは通常移行で失われます。

ゲートウェイが実際に提供するものから始め、そのプロトコルを話すクライアントを選んでください。別のサイトからプロファイルをコピーすることが、問題ですらなかった資格情報をデバッグすることになる道です。

関連記事