root 権限なしで Tunnelblick から共有をマウントする
開いたままの AppleScript が 一つの Tunnelblick プロファイルを監視し、ログイン中のユーザーとして SMB 共有をマウントします。root フックもパスワードなしの sudo も不要です。
VPN クライアントはネットワークを再構成するために特権的な助けを必要とし、その事実は周囲のあらゆるものに過剰に適用されます。ファイル共有をマウントするスクリプトに root が必要ないのは、共有がログイン中のユーザーのセッションと、そのユーザーの資格情報に属しているからです。マウントを特権的な経路から外せば、sudo ルールや root フック、同梱アプリの編集は不要になります。残るのは、開いたまま一つのプロファイルを監視し、一度だけ試行する小さなアプレットです。
これがユーザーセッションに属する理由
マウントされたボリュームは、それをマウントしたセッションの属性です。別のユーザーのセッションからは見えず、root 所有のマウントは Finder が作成したものとは異なる振る舞いをします。マウントをユーザーセッションに留めれば、その意味論が保たれます。
特権ヘルパーは、本当に必要な部分、すなわちトンネルインターフェースの作成やルートの変更、リゾルバの更新のために存在します。SMB 共有のマウントはそのどれでもありません。そうであるかのように扱うと、パスワードなしの sudo と、誰もいないときに動く root スクリプトのどちらかを選ばされます。どちらも問題に見合わない大きなリスクであり、ユーザーセッション方式はその選択自体を避けます。
Tunnelblick 自身の接続フックは、デスクトップセッションの外で動き、クライアントのスクリプトとの順序も定義されていないため、ここでは代わりになりません。ユーザーアプレットから状態を問い合わせるほうが、前提の少ない小さな仕組みです。
まず手作業で共有が動くことを確かめる
自動化は、存在しないルートや権限、アカウントを用意できません。スクリプトを書く前に、手動で VPN に接続し Finder で共有を開いてください。それが失敗するなら、スクリプトはもっと分かりにくい形で失敗します。
スクリプトが使う三つの値、すなわち Tunnelblick が表示する通りのプロファイル名と共有の URL、マウント後に Finder が示すボリューム名を記録します。ボリューム名が重要なのは、既存のマウントの確認がそれを使うからであり、名前だけの確認は利便性のための防護であって、サーバーの同一性の証明ではありません。マシン上で一意な名前を使ってください。そのうえで、サーバーがそもそも応答するかを確かめます。
smbutil view //files.corp.example
osascript -e 'tell application "Tunnelblick" to get state of first configuration whose name is "Office"'
mount | grep -i smbfs
一つ目のコマンドはサーバーが応答することを、二つ目はプロファイル名が解決することを証明し、三つ目は成功したマウントがどんなものかを示します。自動化の前に 三つとも記録してください。
Tunnelblick からプロファイルの状態を読む
Tunnelblick は AppleScript 経由で設定とその状態を公開し、これがこの設計に必要な唯一のフックです。重要なのは、その 一つのプロファイルについて報告される文字列であり、アプリの存在ではありません。
問い合わせには触れておくべき代償があります。間隔より短い再接続は見過ごされ、アプレットは切り替わりをまったく見ません。マウントの見張りには許容できますが、あらゆるイベントに反応すべきものには許容できません。イベントの忠実さが必要なら、これは間隔を短くすべき仕組みではなく、誤った仕組みです。そのトレードオフを踏まえて間隔を選んでください。
property attempted : false
on idle
set profileName to "Office"
set shareURL to "smb://files.corp.example/OfficeTeam"
set volName to "OfficeTeam"
tell application "Tunnelblick" to set vpnState to state of first configuration whose name is profileName
if vpnState is not "CONNECTED" then
set attempted to false
return 20
end if
tell application "Finder" to set mounted to (exists disk volName)
if (not mounted) and (not attempted) then
set attempted to true
try
mount volume shareURL
end try
end if
return 20
end idle
開いたままにするオプションを付けてアプリケーションとして書き出し、sudo ではなくログイン中のセッションで起動します。
一度だけマウントし、その後は試行をやめる
「試行済み」フラグが、これを資格情報の推測ループにしないための仕組みです。アプレットは試行したあと、接続状態が変わるまで待ってから再試行します。
この振る舞いは意図的です。マウントを拒否する共有は、再試行では直せない理由で拒否しているのが普通で、繰り返し試すと 一度のプロンプトが連続になります。フラグはプロファイルが接続状態を離れたときに戻るので、本当の再接続ではちょうど 一度だけ新しい試行が発生します。アプレットを終了して再起動しても戻り、それが手動の上書きです。どちらの経路も失敗を隠さず、どちらも Finder に失敗を残します。
スクリプトはパスワードを持たず、そうあり続けるべきです。マウント操作に尋ねさせ、キーチェーンに組織が対応する範囲のことをさせてください。資格情報を URL に埋め込むと、便利スクリプトがローテーションすべき秘密になります。
ヘルパーにできないこと
三つの限界をアプレットのそばに書いておく価値があります。ネットワーク障害後に使用中のボリュームをイジェクトできないこと、状態が接続を示しても DNS とサーバーが準備できている保証はないこと、そして再接続のたびに反応するわけではないことです。
一つ目の限界が実務では最も重要です。サーバーが消えた共有は、マウントされているように見えてアクセス時に固まるボリュームを残し、丁寧な対処は予定した切断の前にファイル操作を終えてイジェクトすることです。二つ目は最初の試行が失敗する大半を説明しますが、トンネルは上がっているのにリゾルバが追いついていないので、待ってから一度だけ手作業でマウントして確認します。三つ目は先に述べた問い合わせのトレードオフであり、直すべき不具合ではありません。
無人機や管理されたフリートには、この設計は形が合いません。デスクトップのアプレットを特権サービスに拡張するのではなく、管理者が持つマウントポリシーを使ってください。